Filtering frames out of the backtrace display

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?

@jingham answered this some years ago elsewhere - debugging - Hide stack frames from certain source files in LLDB - Stack Overflow. To my knowledge that is still correct.

The closest we have built in is a way to not stop in those std:: functions when stepping, again Jim talked about it previously - c++ - Why the breakpoints set in STL are "skipped/ignored" while using LLDB? - Stack Overflow

Writing a custom up/down would be possible (up until?) using Python, as would a custom bt. Not sure you can match the output exactly but for basic stuff it would be fine.

lldb does mark inlined frames so there is precedent for attaching properties to them, which could be whether they’re interesting or not. If you wanted to pursue making this a built in feature.

I don’t see an existing feature request for it so you could open one if you like Issues · llvm/llvm-project · GitHub.

1 Like