Skip to content

Commit

Permalink
Properly demangle inlined calls
Browse files Browse the repository at this point in the history
We missed to honor the demangle setting for the recently added inlined
function calls. Make sure to demangle their names as necessary and
desired.

Signed-off-by: Daniel Müller <deso@posteo.net>
  • Loading branch information
d-e-s-o committed Sep 28, 2023
1 parent cbb8d42 commit 43dd335
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 69 deletions.
13 changes: 8 additions & 5 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn print_frame(
addr_info: Option<(Addr, Addr, usize)>,
code_info: &Option<symbolize::CodeInfo>,
) {
let code_info = if let Some(code_info) = code_info {
let code_info = code_info.as_ref().map(|code_info| {
let path = code_info.to_path();
let path = path.display();

Expand All @@ -94,21 +94,24 @@ fn print_frame(
(Some(line), None) => format!(" {path}:{line}"),
(None, _) => format!(" {path}"),
}
} else {
String::new()
};
});

if let Some((input_addr, addr, offset)) = addr_info {
// If we have various address information bits we have a new symbol.
println!(
"{input_addr:#0width$x}: {name} @ {addr:#x}+{offset:#x}{code_info}",
code_info = code_info.as_deref().unwrap_or(""),
width = ADDR_WIDTH
)
} else {
// Otherwise we are dealing with an inlined call.
println!(
"{:width$} {name} @ {code_info} [inlined]",
"{:width$} {name}{code_info} [inlined]",
" ",
code_info = code_info
.map(|info| format!(" @{info}"))
.as_deref()
.unwrap_or(""),
width = ADDR_WIDTH
)
}
Expand Down
13 changes: 8 additions & 5 deletions examples/addr2ln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ADDR_WIDTH: usize = 16;


fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &Option<CodeInfo>) {
let code_info = if let Some(code_info) = code_info {
let code_info = code_info.as_ref().map(|code_info| {
let path = code_info.to_path();
let path = path.display();

Expand All @@ -25,21 +25,24 @@ fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &O
(Some(line), None) => format!(" {path}:{line}"),
(None, _) => format!(" {path}"),
}
} else {
String::new()
};
});

if let Some((input_addr, addr, offset)) = addr_info {
// If we have various address information bits we have a new symbol.
println!(
"{input_addr:#0width$x}: {name} @ {addr:#x}+{offset:#x}{code_info}",
code_info = code_info.as_deref().unwrap_or(""),
width = ADDR_WIDTH
)
} else {
// Otherwise we are dealing with an inlined call.
println!(
"{:width$} {name} @ {code_info} [inlined]",
"{:width$} {name}{code_info} [inlined]",
" ",
code_info = code_info
.map(|info| format!(" @{info}"))
.as_deref()
.unwrap_or(""),
width = ADDR_WIDTH
)
}
Expand Down
13 changes: 8 additions & 5 deletions examples/addr2ln_pid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ADDR_WIDTH: usize = 16;


fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &Option<CodeInfo>) {
let code_info = if let Some(code_info) = code_info {
let code_info = code_info.as_ref().map(|code_info| {
let path = code_info.to_path();
let path = path.display();

Expand All @@ -25,21 +25,24 @@ fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &O
(Some(line), None) => format!(" {path}:{line}"),
(None, _) => format!(" {path}"),
}
} else {
String::new()
};
});

if let Some((input_addr, addr, offset)) = addr_info {
// If we have various address information bits we have a new symbol.
println!(
"{input_addr:#0width$x}: {name} @ {addr:#x}+{offset:#x}{code_info}",
code_info = code_info.as_deref().unwrap_or(""),
width = ADDR_WIDTH
)
} else {
// Otherwise we are dealing with an inlined call.
println!(
"{:width$} {name} @ {code_info} [inlined]",
"{:width$} {name}{code_info} [inlined]",
" ",
code_info = code_info
.map(|info| format!(" @{info}"))
.as_deref()
.unwrap_or(""),
width = ADDR_WIDTH
)
}
Expand Down
26 changes: 18 additions & 8 deletions examples/backtrace.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::let_unit_value)]

use std::cmp::min;
use std::mem::size_of;
use std::mem::transmute;
Expand All @@ -16,7 +18,7 @@ const ADDR_WIDTH: usize = 16;


fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &Option<CodeInfo>) {
let code_info = if let Some(code_info) = code_info {
let code_info = code_info.as_ref().map(|code_info| {
let path = code_info.to_path();
let path = path.display();

Expand All @@ -25,21 +27,24 @@ fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &O
(Some(line), None) => format!(" {path}:{line}"),
(None, _) => format!(" {path}"),
}
} else {
String::new()
};
});

if let Some((input_addr, addr, offset)) = addr_info {
// If we have various address information bits we have a new symbol.
println!(
"{input_addr:#0width$x}: {name} @ {addr:#x}+{offset:#x}{code_info}",
code_info = code_info.as_deref().unwrap_or(""),
width = ADDR_WIDTH
)
} else {
// Otherwise we are dealing with an inlined call.
println!(
"{:width$} {name} @ {code_info} [inlined]",
"{:width$} {name}{code_info} [inlined]",
" ",
code_info = code_info
.map(|info| format!(" @{info}"))
.as_deref()
.unwrap_or(""),
width = ADDR_WIDTH
)
}
Expand All @@ -52,8 +57,13 @@ fn symbolize_current_bt() {

let mut bt_buf = [ptr::null_mut::<libc::c_void>(); MAX_CNT];
let bt_cnt = unsafe { libc::backtrace(bt_buf.as_mut_ptr(), MAX_CNT as _) } as usize;
let bt = &bt_buf[0..min(bt_cnt, MAX_CNT)];
let bt = unsafe { transmute::<&[*mut libc::c_void], &[Addr]>(bt) };
let bt = &mut bt_buf[0..min(bt_cnt, MAX_CNT)];
let bt = unsafe { transmute::<&mut [*mut libc::c_void], &mut [Addr]>(bt) };

// For all but the top most address in the call stack, adjust for
// the fact that we captured the address we will return to, but not
// the one we called from.
let () = bt.iter_mut().skip(1).for_each(|addr| *addr -= 1);

// Symbolize the addresses for the current process, as that's where
// they were captured.
Expand Down Expand Up @@ -91,7 +101,7 @@ fn f() {
g()
}

#[inline(never)]
#[inline(always)]
fn g() {
h()
}
Expand Down
19 changes: 13 additions & 6 deletions src/symbolize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@
//!
//! const ADDR_WIDTH: usize = 16;
//!
//! fn print_frame(name: &str, addr_info: Option<(Addr, Addr, usize)>, code_info: &Option<CodeInfo>) {
//! let code_info = if let Some(code_info) = code_info {
//! fn print_frame(
//! name: &str,
//! addr_info: Option<(Addr, Addr, usize)>,
//! code_info: &Option<CodeInfo>,
//! ) {
//! let code_info = code_info.as_ref().map(|code_info| {
//! let path = code_info.to_path();
//! let path = path.display();
//!
Expand All @@ -31,21 +35,24 @@
//! (Some(line), None) => format!(" {path}:{line}"),
//! (None, _) => format!(" {path}"),
//! }
//! } else {
//! String::new()
//! };
//! });
//!
//! if let Some((input_addr, addr, offset)) = addr_info {
//! // If we have various address information bits we have a new symbol.
//! println!(
//! "{input_addr:#0width$x}: {name} @ {addr:#x}+{offset:#x}{code_info}",
//! code_info = code_info.as_deref().unwrap_or(""),
//! width = ADDR_WIDTH
//! )
//! } else {
//! // Otherwise we are dealing with an inlined call.
//! println!(
//! "{:width$} {name} @ {code_info} [inlined]",
//! "{:width$} {name}{code_info} [inlined]",
//! " ",
//! code_info = code_info
//! .map(|info| format!(" @{info}"))
//! .as_deref()
//! .unwrap_or(""),
//! width = ADDR_WIDTH
//! )
//! }
Expand Down
16 changes: 8 additions & 8 deletions src/symbolize/symbolizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,19 @@ impl Symbolizer {
(None, None)
};

let IntSym {
name: sym_name,
addr: sym_addr,
size: sym_size,
lang,
} = sym;

let inlined = if let Some(code_info) = &addr_code_info {
code_info
.inlined
.iter()
.map(|(name, info)| {
let name = name.to_string();
let name = self.maybe_demangle(name, lang);
let info = info.as_ref().map(CodeInfo::from);
InlinedFn {
name,
Expand All @@ -232,13 +239,6 @@ impl Symbolizer {
Vec::new()
};

let IntSym {
name: sym_name,
addr: sym_addr,
size: sym_size,
lang,
} = sym;

let sym = Sym {
name: self.maybe_demangle(name.unwrap_or(sym_name), lang),
addr: sym_addr,
Expand Down
100 changes: 68 additions & 32 deletions tests/blazesym.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::env::current_exe;
use std::ffi::CString;
use std::ffi::OsStr;
use std::fs::read as read_file;
use std::hint::black_box;
use std::io::Error;
use std::os::unix::ffi::OsStringExt as _;
use std::path::Path;
Expand Down Expand Up @@ -221,52 +222,87 @@ fn symbolize_dwarf_complex() {
assert_eq!(result.code_info.as_ref().unwrap().line, Some(534));
}


#[inline(always)]
fn inlined_call() -> usize {
black_box(42)
}


#[inline(never)]
fn test_function() {
let x = inlined_call();
let _x = black_box(x);
}


/// Symbolize a normalized address inside an ELF file, with and without
/// auto-demangling enabled.
#[test]
fn symbolize_elf_demangle() {
let test_elf = current_exe().unwrap();
let addr = Normalizer::normalize_user_addrs_sorted as Addr;
fn test(addr: Addr) -> Result<(), ()> {
let test_elf = current_exe().unwrap();
let src = symbolize::Source::Elf(symbolize::Elf::new(test_elf));
let symbolizer = Symbolizer::builder().enable_demangling(false).build();
let result = symbolizer
.symbolize_single(&src, addr)
.unwrap()
.into_sym()
.unwrap();

assert!(
result.name.contains("blazesym13test_function"),
"{result:x?}"
);

if cfg!(debug_assertions) {
if result.inlined.is_empty() {
return Err(())
}
assert!(result.inlined[0].name.contains("blazesym12inlined_call"));

// Do it again, this time with demangling enabled.
let symbolizer = Symbolizer::new();
let result = symbolizer
.symbolize_single(&src, addr)
.unwrap()
.into_sym()
.unwrap();

assert_eq!(result.name, "blazesym::test_function");
assert_eq!(result.inlined.len(), 1, "{:#?}", result.inlined);
assert_eq!(result.inlined[0].name, "blazesym::inlined_call");
}
Ok(())
}


let addrs = [test_function as Addr + 8];
let normalizer = Normalizer::builder().enable_build_ids(false).build();
let norm_addrs = normalizer
.normalize_user_addrs_sorted(&[addr], Pid::Slf)
.normalize_user_addrs_sorted(&addrs, Pid::Slf)
.unwrap();
let (addr, _meta_idx) = norm_addrs.addrs[0];

let test_elf = current_exe().unwrap();
let src = symbolize::Source::Elf(symbolize::Elf::new(test_elf));
let symbolizer = Symbolizer::builder().enable_demangling(false).build();
let results = symbolizer
.symbolize(&src, &[addr])
let result = symbolizer
.symbolize_single(&src, addr)
.unwrap()
.into_iter()
.collect::<Vec<_>>();
assert_eq!(results.len(), 1);

let result = results[0].as_sym().unwrap();
assert!(
result
.name
.contains("Normalizer27normalize_user_addrs_sorted"),
"{result:x?}"
);
.into_sym()
.unwrap();

// Do it again, this time with demangling enabled.
let symbolizer = Symbolizer::new();
let results = symbolizer
.symbolize(&src, &[addr])
.unwrap()
.into_iter()
.collect::<Vec<_>>();
assert_eq!(results.len(), 1);
let addr = result.addr;
let size = result.size.unwrap();
for inst_addr in addr..addr + size {
println!("{inst_addr:#x} {size}");
if test(inst_addr).is_ok() {
return
}
}

let result = results[0].as_sym().unwrap();
assert!(
result.name == "blazesym::normalize::normalizer::Normalizer::normalize_user_addrs_sorted"
|| result.name
== "<blazesym::normalize::normalizer::Normalizer>::normalize_user_addrs_sorted",
"{}",
result.name
);
panic!("failed to find inlined function call");
}

/// Check that we can symbolize addresses inside our own process.
Expand Down

0 comments on commit 43dd335

Please sign in to comment.