Skip to content

Commit

Permalink
refactor(base): add stacktrace to replace backtrace (#16643)
Browse files Browse the repository at this point in the history
* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): make  lint for macos

* refactor(base): make  lint for macos

* refactor(base): make lint for linux

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): add display text cache

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* refactor(base): lock free backtrace capture

* fix(base): fix test failure

* fix(base): fix test failure
  • Loading branch information
zhang2014 authored Nov 11, 2024
1 parent c3688cb commit 1f712dc
Show file tree
Hide file tree
Showing 21 changed files with 3,225 additions and 182 deletions.
42 changes: 39 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ geo-types = "0.7.13"
geohash = "0.13.0"
geos = { version = "9.0.0", features = ["static", "geo", "geo-types"] }
geozero = { version = "0.14.0", features = ["default", "with-wkb", "with-geos", "with-geojson"] }
gimli = "0.31.0"
globiter = "0.1"
goldenfile = "1.4"
h3o = "0.4.0"
Expand Down Expand Up @@ -354,6 +355,7 @@ num-bigint = "0.4.6"
num-derive = "0.3.3"
num-traits = "0.2.19"
num_cpus = "1.13.1"
object = "0.36.5"
object_store_opendal = "0.48.1"
once_cell = "1.15.0"
openai_api_rust = "0.1"
Expand Down Expand Up @@ -514,6 +516,7 @@ nom-rule = "0.4"
pratt = "0.4.0"
pretty = "0.11.3"
rspack-codespan-reporting = "0.11"
rustc-demangle = "0.1"
strsim = "0.10"
strum_macros = "0.24"
vergen = { version = "8.3.1", default-features = false, features = ["build", "cargo", "git", "gix", "rustc"] }
Expand Down Expand Up @@ -597,6 +600,7 @@ gimli = { opt-level = 3 }
miniz_oxide = { opt-level = 3 }
object = { opt-level = 3 }
rustc-demangle = { opt-level = 3 }
databend-common-exception = { opt-level = 3 }

[profile.test]
opt-level = 0
Expand Down
1 change: 1 addition & 0 deletions src/common/arrow/src/arrow/ffi/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ unsafe fn read_bytes(ptr: *const u8, len: usize) -> &'static str {
}

unsafe fn metadata_from_bytes(data: *const ::std::os::raw::c_char) -> (Metadata, Extension) {
#[allow(clippy::unnecessary_cast)]
let mut data = data as *const u8; // u8 = i8
if data.is_null() {
return (Metadata::default(), None);
Expand Down
1 change: 1 addition & 0 deletions src/common/base/src/mem_allocator/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ pub mod linux {
}
// fallback to (5.13.0)
let fallback_version = 5u32 << 16 | 13u32 << 8;
#[allow(clippy::unnecessary_cast)]
let slice = unsafe { &*(&uname.release[..length] as *const _ as *const [u8]) };
let result = match std::str::from_utf8(slice) {
Ok(ver) => match semver::Version::parse(ver) {
Expand Down
7 changes: 6 additions & 1 deletion src/common/exception/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@ databend-common-ast = { workspace = true }
anyhow = { workspace = true }
arrow-flight = { workspace = true }
arrow-schema = { workspace = true }
backtrace = { workspace = true }
backtrace = { workspace = true, features = ["std", "serialize-serde"] }
bincode = { workspace = true }
geos = { workspace = true }
geozero = { workspace = true }
gimli = { workspace = true }
http = { workspace = true }
libc = { workspace = true }
object = { workspace = true }
once_cell = { workspace = true }
opendal = { workspace = true }
parquet = { workspace = true }
paste = { workspace = true }
prost = { workspace = true }
reqwest = { workspace = true }
rustc-demangle = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sqlx = { workspace = true }
Expand Down
205 changes: 205 additions & 0 deletions src/common/exception/src/elf/dwarf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright 2021 Datafuse Labs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;

use gimli::DebugAbbrev;
use gimli::DebugAddr;
use gimli::DebugAranges;
use gimli::DebugInfo;
use gimli::DebugInfoOffset;
use gimli::DebugLine;
use gimli::DebugLineStr;
use gimli::DebugRanges;
use gimli::DebugRngLists;
use gimli::DebugStr;
use gimli::DebugStrOffsets;
use gimli::EndianSlice;
use gimli::NativeEndian;
use gimli::RangeLists;
use gimli::Reader;
use gimli::UnitHeader;
use gimli::UnitType;
use object::CompressionFormat;
use object::Object;
use object::ObjectSection;

use crate::elf::dwarf_unit::Unit;
use crate::elf::dwarf_unit::UnitAttrs;
use crate::elf::ElfFile;

#[derive(Debug)]
pub struct CallLocation {
pub symbol: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub is_inlined: bool,
}

pub struct Dwarf {
#[allow(unused)]
elf: Arc<ElfFile>,
debug_str: DebugStr<EndianSlice<'static, NativeEndian>>,
debug_info: DebugInfo<EndianSlice<'static, NativeEndian>>,
debug_line: DebugLine<EndianSlice<'static, NativeEndian>>,
debug_line_str: DebugLineStr<EndianSlice<'static, NativeEndian>>,
debug_str_offsets: DebugStrOffsets<EndianSlice<'static, NativeEndian>>,
debug_aranges: DebugAranges<EndianSlice<'static, NativeEndian>>,
debug_abbrev: DebugAbbrev<EndianSlice<'static, NativeEndian>>,
debug_addr: DebugAddr<EndianSlice<'static, NativeEndian>>,
debug_range_list: RangeLists<EndianSlice<'static, NativeEndian>>,
}

static EMPTY_BYTES: &[u8] = &[];

impl Dwarf {
pub fn create(elf: Arc<ElfFile>) -> Option<Dwarf> {
fn get_debug_section(elf: &ElfFile, name: &str) -> EndianSlice<'static, NativeEndian> {
let Some(section) = elf.section_by_name(name) else {
return EndianSlice::new(EMPTY_BYTES, NativeEndian);
};

// Unsupported compress debug info
let Ok(compressed) = section.compressed_file_range() else {
return EndianSlice::new(EMPTY_BYTES, NativeEndian);
};

#[allow(clippy::missing_transmute_annotations)]
unsafe {
match compressed.format != CompressionFormat::None {
true => EndianSlice::new(EMPTY_BYTES, NativeEndian),
false => match section.data() {
Err(_) => EndianSlice::new(EMPTY_BYTES, NativeEndian),
Ok(data) => EndianSlice::new(std::mem::transmute(data), NativeEndian),
},
}
}
}

for name in [".debug_info", ".debug_abbrev", ".debug_line"] {
if get_debug_section(&elf, name).is_empty() {
return None;
}
}

Some(Dwarf {
debug_str: DebugStr::from(get_debug_section(&elf, ".debug_str")),
debug_info: DebugInfo::from(get_debug_section(&elf, ".debug_info")),
debug_line: DebugLine::from(get_debug_section(&elf, ".debug_line")),
debug_line_str: DebugLineStr::from(get_debug_section(&elf, ".debug_line_str")),
debug_str_offsets: DebugStrOffsets::from(get_debug_section(&elf, ".debug_str_offsets")),
debug_aranges: DebugAranges::from(get_debug_section(&elf, ".debug_aranges")),
debug_abbrev: DebugAbbrev::from(get_debug_section(&elf, ".debug_abbrev")),
debug_range_list: RangeLists::new(
DebugRanges::from(get_debug_section(&elf, ".debug_ranges")),
DebugRngLists::from(get_debug_section(&elf, ".debug_rnglists")),
),
debug_addr: DebugAddr::from(get_debug_section(&elf, ".debug_addr")),
elf,
})
}

fn find_debug_info_offset(&self, probe: u64) -> Option<DebugInfoOffset<usize>> {
let mut heads = self.debug_aranges.headers();
while let Some(head) = heads.next().ok()? {
let mut entries = head.entries();
while let Some(entry) = entries.next().ok()? {
if probe >= entry.address() && probe <= entry.address() + entry.length() {
return Some(head.debug_info_offset());
}
}
}

None
}

fn get_unit(
&self,
head: UnitHeader<EndianSlice<'static, NativeEndian>>,
) -> gimli::Result<Option<Unit<EndianSlice<'static, NativeEndian>>>> {
let abbrev_offset = head.debug_abbrev_offset();
let Ok(abbreviations) = self.debug_abbrev.abbreviations(abbrev_offset) else {
return Ok(None);
};

let mut cursor = head.entries(&abbreviations);
let (_idx, root) = cursor.next_dfs()?.unwrap();

let mut attrs = root.attrs();
let mut unit_attrs = UnitAttrs::create();

while let Some(attr) = attrs.next()? {
unit_attrs.set_attr(&self.debug_str, attr);
}

Ok(Some(Unit {
head,
abbreviations,
attrs: unit_attrs,
debug_str: self.debug_str,
debug_info: self.debug_info,
debug_abbrev: self.debug_abbrev,
debug_line: self.debug_line,
debug_line_str: self.debug_line_str,
debug_str_offsets: self.debug_str_offsets,
debug_addr: self.debug_addr,
range_list: self.debug_range_list,
}))
}

fn fast_find_frames(&self, probe: u64) -> gimli::Result<Option<Vec<CallLocation>>> {
if let Some(debug_info_offset) = self.find_debug_info_offset(probe) {
let head = self.debug_info.header_from_offset(debug_info_offset)?;

let type_ = head.type_();
if matches!(type_, UnitType::Compilation | UnitType::Skeleton(_)) {
if let Some(unit) = self.get_unit(head)? {
return Ok(Some(unit.find_frames(probe)?));
}
}
}

Ok(None)
}

fn slow_find_frames(&self, probe: u64) -> gimli::Result<Vec<CallLocation>> {
let mut units = self.debug_info.units();
while let Some(head) = units.next()? {
if matches!(head.type_(), UnitType::Compilation | UnitType::Skeleton(_)) {
if let Some(unit) = self.get_unit(head)? {
if unit.match_pc(probe) {
return unit.find_frames(probe);
}
}
}
}

Ok(vec![])
}

pub fn find_frames(&self, probe: u64) -> gimli::Result<Vec<CallLocation>> {
match self.fast_find_frames(probe)? {
Some(location) => Ok(location),
None => self.slow_find_frames(probe),
}
}
}

// #[cfg(target_os = "linux")]
#[derive(Copy, Clone, Debug)]
pub enum HighPc {
Addr(u64),
Offset(u64),
}
Loading

0 comments on commit 1f712dc

Please sign in to comment.