Skip to content

Commit 704b55c

Browse files
Tomasz Miąskotmiasko
andauthored
Support line-tables-only when using libbacktrace (#303)
* Add test case for line-tables-only * Support line-tables-only when using libbacktrace Previously when `backtrace_pcinfo` succeeded, but failed to obtain a filename or a function name, the line number would be ignored. Instead, when successful combine all available information. For example, when using clang `-g1` or `-gline-tables-only` before: ``` 1: baz 2: bar 3: foo ``` and after: ``` 1: baz at src/callback.c:5 2: bar at src/callback.c:9 3: foo at src/callback.c:13 ``` Co-authored-by: Tomasz Miąsko <[email protected]>
1 parent 6ffa2c3 commit 704b55c

File tree

7 files changed

+106
-4
lines changed

7 files changed

+106
-4
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jobs:
8282
- run: cargo test --features libbacktrace --manifest-path crates/without_debuginfo/Cargo.toml
8383
- run: cargo test --features "libbacktrace coresymbolication" --manifest-path crates/without_debuginfo/Cargo.toml
8484
- run: cargo test --features "libbacktrace gimli-symbolize" --manifest-path crates/without_debuginfo/Cargo.toml
85+
- run: cargo test --manifest-path crates/line-tables-only/Cargo.toml
8586

8687
windows_arm64:
8788
name: Windows AArch64

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ edition = "2018"
1616

1717
[workspace]
1818
members = ['crates/cpp_smoke_test']
19-
exclude = ['crates/without_debuginfo', 'crates/macos_frames_test']
19+
exclude = ['crates/without_debuginfo', 'crates/macos_frames_test', 'crates/line-tables-only']
2020

2121
[dependencies]
2222
cfg-if = "0.1.10"

crates/line-tables-only/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[package]
2+
name = "line-tables-only"
3+
version = "0.1.0"
4+
edition = "2018"
5+
6+
[build-dependencies]
7+
cc = "1.0"
8+
9+
[dependencies]
10+
libc = { version = "0.2", default-features = false }
11+
12+
[dependencies.backtrace]
13+
path = "../.."
14+
features = [
15+
'libunwind',
16+
'std',
17+
]
18+
19+
[features]
20+
libbacktrace = ['backtrace/libbacktrace']

crates/line-tables-only/build.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn main() {
2+
println!("cargo:rerun-if-changed=src/callback.c");
3+
4+
cc::Build::new()
5+
.opt_level(0)
6+
.debug(false)
7+
.flag("-g1")
8+
.file("src/callback.c")
9+
.compile("libcallback.a");
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
typedef void (*callback) (void *data);
3+
4+
void baz(callback cb, void *data) {
5+
cb(data);
6+
}
7+
8+
void bar(callback cb, void *data) {
9+
baz(cb, data);
10+
}
11+
12+
void foo(callback cb, void *data) {
13+
bar(cb, data);
14+
}

crates/line-tables-only/src/lib.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use std::path::Path;
4+
use backtrace::Backtrace;
5+
use libc::c_void;
6+
7+
pub type Callback = extern "C" fn(data: *mut c_void);
8+
9+
extern "C" {
10+
fn foo(cb: Callback, data: *mut c_void);
11+
}
12+
13+
extern "C" fn store_backtrace(data: *mut c_void) {
14+
let bt = backtrace::Backtrace::new();
15+
unsafe { *(data as *mut Option<Backtrace>) = Some(bt) };
16+
}
17+
18+
fn assert_contains(backtrace: &Backtrace,
19+
expected_name: &str,
20+
expected_file: &str,
21+
expected_line: u32) {
22+
23+
let expected_file = Path::new(expected_file);
24+
25+
for frame in backtrace.frames() {
26+
for symbol in frame.symbols() {
27+
if let Some(name) = symbol.name() {
28+
if name.as_bytes() == expected_name.as_bytes() {
29+
assert_eq!(symbol.filename(), Some(expected_file));
30+
assert_eq!(symbol.lineno(), Some(expected_line));
31+
return;
32+
}
33+
}
34+
}
35+
}
36+
37+
panic!("symbol {:?} not found in backtrace: {:?}", expected_name, backtrace);
38+
}
39+
40+
/// Verifies that when debug info includes only lines tables the generated
41+
/// backtrace is still generated successfully. The test exercises behaviour
42+
/// that failed previously when compiling with clang -g1.
43+
///
44+
/// The test case uses C rather than rust, since at that time when it was
45+
/// written the debug info generated at level 1 in rustc was essentially
46+
/// the same as at level 2.
47+
#[test]
48+
#[cfg_attr(windows, ignore)]
49+
fn backtrace_works_with_line_tables_only() {
50+
let mut backtrace: Option<Backtrace> = None;
51+
unsafe { foo(store_backtrace, &mut backtrace as *mut _ as *mut c_void) };
52+
let backtrace = backtrace.expect("backtrace");
53+
assert_contains(&backtrace, "foo", "src/callback.c", 13);
54+
assert_contains(&backtrace, "bar", "src/callback.c", 9);
55+
assert_contains(&backtrace, "baz", "src/callback.c", 5);
56+
}
57+
}

src/symbolize/libbacktrace.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ impl Symbol<'_> {
111111
Symbol::Syminfo { .. } => None,
112112
Symbol::Pcinfo { filename, .. } => {
113113
let ptr = filename as *const u8;
114+
if ptr.is_null() {
115+
return None;
116+
}
114117
unsafe {
115118
let len = libc::strlen(filename);
116119
Some(slice::from_raw_parts(ptr, len))
@@ -220,9 +223,6 @@ extern "C" fn pcinfo_cb(
220223
lineno: c_int,
221224
function: *const c_char,
222225
) -> c_int {
223-
if filename.is_null() || function.is_null() {
224-
return -1;
225-
}
226226
let mut bomb = crate::Bomb { enabled: true };
227227

228228
unsafe {

0 commit comments

Comments
 (0)