Hi all,
I recently started reviewing a C++ project (call it “Hello”) that is completely new to me. In this codebase, the authors make extensive use of std::function, and this has made it more awkward to read/navigate code by following backtraces in a debugger session. Here is a small example that shows the problem:
#include <functional>
void asdf() { __builtin_debugtrap(); }
int main(int, const char**)
{
auto foo= std::function{ asdf };
foo();
}
Suppose that main()
and asdf()
are both part of project Hello, and I’m interested in reviewing any backtrace in which asdf()
is called directly or indirectly by main()
.
Here is what I see when I request a backtrace while execution is paused at the debug-trap above:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1000023a4)
* frame #0: 0x00000001000023a8 hello`asdf() at u.cpp:3:38
frame #1: 0x0000000100003dd4 hello`decltype(std::declval<void (*&)()>()()) std::__1::__invoke[abi:v160006]<void (*&)()>(__f=0x000000016fdfdb60) at invoke.h:394:23
frame #2: 0x0000000100003d88 hello`void std::__1::__invoke_void_return_wrapper<void, true>::__call<void (*&)()>(__args=0x000000016fdfdb60) at invoke.h:487:9
frame #3: 0x0000000100003d64 hello`std::__1::__function::__alloc_func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()[abi:v160006](this=0x000000016fdfdb60) at function.h:185:16
frame #4: 0x0000000100002a8c hello`std::__1::__function::__func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()(this=0x000000016fdfdb58) at function.h:356:12
frame #5: 0x0000000100003f20 hello`std::__1::__function::__value_func<void ()>::operator()[abi:v160006](this=0x000000016fdfdb58) const at function.h:510:16
frame #6: 0x0000000100002474 hello`std::__1::function<void ()>::operator()(this= Function = asdf() ) const at function.h:1156:12
frame #7: 0x00000001000023ec hello`main((null)=1, (null)=0x000000016fdfddd8) at u.cpp:8:5
frame #8: 0x00000001886f50e0 dyld`start + 2360
(lldb)
As you can see, frames 1 through 6 are pure noise (at least, for the purpose of understanding the broad strokes of the call graph of project Hello). And of course, it gets worse when the backtrace contains multiple uses of std::function
. In a familiar codebase, you could learn to look past this annoyance. But in a new-to-you codebase, this visual noise can be demoralizing, and could potentially contribute to driving away prospective contributors to project Hello.
So what I’d like is some way to tell LLDB that i’m not interested in seeing the guts of std::function when displaying the backtrace (or the guts of any function that matches at least one element of a set of user-specified patterns), so that instead we see something like:
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1000023a4)
* frame #0: 0x00000001000023a8 hello`asdf() at u.cpp:3:38
...frame #7: 0x00000001000023ec hello`main((null)=1, (null)=0x000000016fdfddd8) at u.cpp:8:5
frame #8: 0x00000001886f50e0 dyld`start + 2360
(lldb)
(I.e., use the ellipsis at the beginning of the line for frame 7 to indicate that frames have been hidden at that point.)
Likewise, when using the LLDB commands up
and down
, i want them to skip over frames that are hidden in the backtrace. (So, if the current frame is frame 0 and I execute up
, LLDB should take me to frame 7.) An option to _regexp-up
/_regexp-down
could allow moving into hidden frames (and of course a new setting could toggle frame-hiding completely).
Ideally, this behavior would also be reflected in IDEs like Xcode (so that arrowing up/down through the backtrace would skip over hidden frames unless you use e.g. a disclosure widget to display those frames).
Does LLDB already have a feature like this? If so, how can I enable it?