This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Add Python bindings to print stack traces on crashes.
ClosedPublic

Authored by JDevlieghere on Nov 19 2020, 5:49 PM.

Details

Summary

As noticed in D87637, when LLDB crashes, we only print stack traces if LLDB is directly executed, not when used via Python bindings. Enabling this by default may be undesirable (libraries shouldn't be messing with signal handlers), so make this an explicit opt-in.

When adding an abort() to CommandInterpreter::HandleCommand(), this prints:

$ ninja check-lldb-api-commands-apropos-basic
FAIL: lldb-api :: commands/apropos/basic/TestApropos.py (1 of 1)
******************** TEST 'lldb-api :: commands/apropos/basic/TestApropos.py' FAILED ********************
...
Command Output (stderr):
--
python3.8: /home/rupprecht/src/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:1655: bool lldb_private::CommandInterpreter::HandleCommand(const char *, lldb_private::LazyBool, lldb_private::CommandReturnObject &, lldb_private::ExecutionContext *, bool, bool): Assertion `false && "crash!"' failed.
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace.
Stack dump:
0.      HandleCommand(command = "settings clear -all")
 #0 0x00007f1e9a0ff4ea llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /home/rupprecht/src/llvm-project/llvm/lib/Support/Unix/Signals.inc:563:11
 #1 0x00007f1e9a0ff6bb PrintStackTraceSignalHandler(void*) /home/rupprecht/src/llvm-project/llvm/lib/Support/Unix/Signals.inc:630:1
 #2 0x00007f1e9a0fdcdb llvm::sys::RunSignalHandlers() /home/rupprecht/src/llvm-project/llvm/lib/Support/Signals.cpp:70:5
 #3 0x00007f1e9a0ffded SignalHandler(int) /home/rupprecht/src/llvm-project/llvm/lib/Support/Unix/Signals.inc:405:1
...
 #9 0x00007f1e997e6228 /home/rupprecht/src/llvm-project/lldb/source/Interpreter/CommandInterpreter.cpp:1655:3

Diff Detail

Event Timeline

rupprecht created this revision.Nov 19 2020, 5:49 PM
Herald added a project: Restricted Project. · View Herald TranscriptNov 19 2020, 5:49 PM
rupprecht requested review of this revision.Nov 19 2020, 5:49 PM

I ran manual tests for this, but I did so by introducing an intentional crash in a place that obviously can't be checked in. Does LLDB have any kind of intentional-crash-for-test mechanism that could be used for a test?

Also, I picked SBDebugger for this as it seems like a very global option, but I'm happy to put it somewhere else. Not sure where though?

I ran manual tests for this, but I did so by introducing an intentional crash in a place that obviously can't be checked in. Does LLDB have any kind of intentional-crash-for-test mechanism that could be used for a test?

We have a reproducer xcrash command that could be used for this purpose.

Though that does bring up the question of how will this interact with reproducers, which also mess with signal handlers. @JDevlieghere, any thoughts on that?

Also, I picked SBDebugger for this as it seems like a very global option, but I'm happy to put it somewhere else. Not sure where though?

SBDebugger is probably fine. That's where we put our other global static functions...

lldb/packages/Python/lldbsuite/test/dotest.py
885–886

I'm also not sure, but I guess this should be coming after the reproducer initialization code (below).

I ran manual tests for this, but I did so by introducing an intentional crash in a place that obviously can't be checked in. Does LLDB have any kind of intentional-crash-for-test mechanism that could be used for a test?

We have a reproducer xcrash command that could be used for this purpose.

Currently the command is a noop when reproducers are off, we could consider changing that to be only the case when replaying.

Though that does bring up the question of how will this interact with reproducers, which also mess with signal handlers. @JDevlieghere, any thoughts on that?

The signal handler mechanism in LLVM allows you to install multiple signal handlers that can be run one after the other. With reproducers enabled it will print the pretty stack trace first and then call the reproducer signal handler. However, that all happens in the driver, so none of this should matter for an SB API test. Enabling the reproducers in dotest is a pain though, because it needs to happen before the debugger is initialized, so you can't just enable it for a single test without messing with dotest or lit directly. If this would be useful more generally, maybe the xcrash subcommand should be part of a hidden/undocumented top level test command?

Also, I picked SBDebugger for this as it seems like a very global option, but I'm happy to put it somewhere else. Not sure where though?

SBDebugger is probably fine. That's where we put our other global static functions...

JDevlieghere commandeered this revision.Apr 7 2022, 9:58 AM
JDevlieghere edited reviewers, added: rupprecht; removed: JDevlieghere.
Herald added a project: Restricted Project. · View Herald TranscriptApr 7 2022, 9:58 AM
JDevlieghere edited reviewers, added: mib, kastiglione; removed: teemperor, vsk.Apr 7 2022, 10:42 AM
mib accepted this revision.Apr 7 2022, 11:10 AM

Pretty straightforward, LGTM!

This revision is now accepted and ready to land.Apr 7 2022, 11:10 AM
teemperor accepted this revision.Apr 7 2022, 11:12 AM
teemperor added a subscriber: teemperor.

This is beautiful

labath added inline comments.Apr 7 2022, 12:44 PM
lldb/source/API/SBDebugger.cpp
215–216

if you really wanted to, you could call something like llvm::sys::fs::getMainExecutable(nullptr, nullptr) -- it quite often finds the executable name even without the argv0 and MainAddr hints.