Skip to content

Commit f6c2e00

Browse files
committed
Always call backtrace_syminfo with libbacktrace
Turns out if we favor backtrace_pcinfo then if there's no debuginfo in the binary (e.g. `strip -g`) then symbol information comes out! It also looks to have more accurate symbol names in terms of loading it from the symbol table rather than debuginfo where debuginfo tends to typically be a bit more terse in naming.
1 parent b1c76e4 commit f6c2e00

File tree

1 file changed

+74
-34
lines changed

1 file changed

+74
-34
lines changed

src/symbolize/libbacktrace.rs

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -114,26 +114,60 @@ extern "C" fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int)
114114
// do nothing for now
115115
}
116116

117+
/// Type of the `data` pointer passed into `syminfo_cb`
118+
struct SyminfoState<'a> {
119+
cb: &'a mut (FnMut(&super::Symbol) + 'a),
120+
pc: usize,
121+
}
122+
117123
extern "C" fn syminfo_cb(
118124
data: *mut c_void,
119125
pc: uintptr_t,
120126
symname: *const c_char,
121127
_symval: uintptr_t,
122128
_symsize: uintptr_t,
123129
) {
130+
// Once this callback is invoked from `backtrace_syminfo` when we start
131+
// resolving we go further to call `backtrace_pcinfo`. The
132+
// `backtrace_pcinfo` function will consult debug information and attemp tto
133+
// do things like recover file/line information as well as inlined frames.
134+
// Note though that `backtrace_pcinfo` can fail or not do much if there's
135+
// not debug info, so if that happens we're sure to call the callback with
136+
// at least one symbol from the `syminfo_cb`.
124137
unsafe {
125-
call(
126-
data,
127-
&super::Symbol {
138+
let syminfo_state = &mut *(data as *mut SyminfoState);
139+
let mut pcinfo_state = PcinfoState {
140+
symname,
141+
called: false,
142+
cb: syminfo_state.cb,
143+
};
144+
bt::backtrace_pcinfo(
145+
init_state(),
146+
syminfo_state.pc as uintptr_t,
147+
pcinfo_cb,
148+
error_cb,
149+
&mut pcinfo_state as *mut _ as *mut _,
150+
);
151+
if !pcinfo_state.called {
152+
let mut bomb = ::Bomb { enabled: true };
153+
(pcinfo_state.cb)(&super::Symbol {
128154
inner: Symbol::Syminfo {
129155
pc: pc,
130156
symname: symname,
131157
},
132-
},
133-
);
158+
});
159+
bomb.enabled = false;
160+
}
134161
}
135162
}
136163

164+
/// Type of the `data` pointer passed into `pcinfo_cb`
165+
struct PcinfoState<'a> {
166+
cb: &'a mut (FnMut(&super::Symbol) + 'a),
167+
symname: *const c_char,
168+
called: bool,
169+
}
170+
137171
extern "C" fn pcinfo_cb(
138172
data: *mut c_void,
139173
pc: uintptr_t,
@@ -145,28 +179,33 @@ extern "C" fn pcinfo_cb(
145179
if filename.is_null() || function.is_null() {
146180
return -1;
147181
}
148-
call(
149-
data,
150-
&super::Symbol {
151-
inner: Symbol::Pcinfo {
152-
pc: pc,
153-
filename: filename,
154-
lineno: lineno,
155-
function: function,
182+
let state = &mut *(data as *mut PcinfoState);
183+
let mut bomb = ::Bomb { enabled: true };
184+
state.called = true;
185+
(state.cb)(&super::Symbol {
186+
inner: Symbol::Pcinfo {
187+
pc: pc,
188+
filename: filename,
189+
lineno: lineno,
190+
191+
// Favor the function name going into the `syminfo_cb`. That's
192+
// typically from the symbol table and is the full symbol name
193+
// whereas the `function` above is coming from debuginfo which
194+
// isn't always as descriptive when considering the whole
195+
// program.
196+
function: if !state.symname.is_null() {
197+
state.symname
198+
} else {
199+
function
156200
},
157201
},
158-
);
202+
});
203+
bomb.enabled = false;
204+
159205
return 0;
160206
}
161207
}
162208

163-
unsafe fn call(data: *mut c_void, sym: &super::Symbol) {
164-
let cb = data as *mut &mut FnMut(&super::Symbol);
165-
let mut bomb = ::Bomb { enabled: true };
166-
(*cb)(sym);
167-
bomb.enabled = false;
168-
}
169-
170209
// The libbacktrace API supports creating a state, but it does not
171210
// support destroying a state. I personally take this to mean that a
172211
// state is meant to be created and then live forever.
@@ -334,7 +373,7 @@ unsafe fn init_state() -> *mut bt::backtrace_state {
334373
}
335374
}
336375

337-
pub unsafe fn resolve(what: ResolveWhat, mut cb: &mut FnMut(&super::Symbol)) {
376+
pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
338377
let symaddr = what.address_or_ip();
339378

340379
// backtrace errors are currently swept under the rug
@@ -343,20 +382,21 @@ pub unsafe fn resolve(what: ResolveWhat, mut cb: &mut FnMut(&super::Symbol)) {
343382
return;
344383
}
345384

346-
let ret = bt::backtrace_pcinfo(
385+
// Call the `backtrace_syminfo` API first. This is (from reading the code)
386+
// guaranteed to call `syminfo_cb` exactly once (or fail with an error
387+
// presumably). We then handle more within the `syminfo_cb`.
388+
//
389+
// Note that we do this since `syminfo` will consult the symbol table,
390+
// finding symbol names even if there's no debug information in the binary.
391+
let mut syminfo_state = SyminfoState {
392+
pc: symaddr as usize,
393+
cb: cb,
394+
};
395+
bt::backtrace_syminfo(
347396
state,
348397
symaddr as uintptr_t,
349-
pcinfo_cb,
398+
syminfo_cb,
350399
error_cb,
351-
&mut cb as *mut _ as *mut _,
400+
&mut syminfo_state as *mut _ as *mut _,
352401
);
353-
if ret != 0 {
354-
bt::backtrace_syminfo(
355-
state,
356-
symaddr as uintptr_t,
357-
syminfo_cb,
358-
error_cb,
359-
&mut cb as *mut _ as *mut _,
360-
);
361-
}
362402
}

0 commit comments

Comments
 (0)