[lldb-vscode] Adding support for displaying backtraces.

On Apple platforms when debugging with libBacktraceRecording.dylib backtraces are stored as part of the thread stack. This change includes support for displaying the back traces when they are present in the stack trace.

To use this on macOS a binary needs to be run with the following environment variables configured:

DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/usr/lib/libBacktraceRecording.dylib
{F28473587}

Reviewed By: wallace

Differential Revision: https://reviews.llvm.org/D156465
This commit is contained in:
John Harrison
2023-07-28 14:22:19 -04:00
committed by David Goldman
parent e777e44546
commit 1bf6f55911
2 changed files with 83 additions and 11 deletions

View File

@@ -815,17 +815,30 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame) {
llvm::json::Value CreateThread(lldb::SBThread &thread) {
llvm::json::Object object;
object.try_emplace("id", (int64_t)thread.GetThreadID());
char thread_str[64];
snprintf(thread_str, sizeof(thread_str), "Thread #%u", thread.GetIndexID());
const char *name = thread.GetName();
if (name) {
std::string thread_with_name(thread_str);
thread_with_name += ' ';
thread_with_name += name;
EmplaceSafeString(object, "name", thread_with_name);
const char *thread_name = thread.GetName();
const char *queue_name = thread.GetQueueName();
std::string thread_str;
if (thread_name) {
thread_str = std::string(thread_name);
} else if (queue_name) {
auto kind = thread.GetQueue().GetKind();
std::string queue_kind_label = "";
if (kind == lldb::eQueueKindSerial) {
queue_kind_label = " (serial)";
} else if (kind == lldb::eQueueKindConcurrent) {
queue_kind_label = " (concurrent)";
}
thread_str = llvm::formatv("Thread {0} Queue: {1}{2}", thread.GetIndexID(),
queue_name, queue_kind_label)
.str();
} else {
EmplaceSafeString(object, "name", std::string(thread_str));
thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
}
EmplaceSafeString(object, "name", thread_str);
return llvm::json::Value(std::move(object));
}

View File

@@ -2687,13 +2687,72 @@ void request_stackTrace(const llvm::json::Object &request) {
const auto startFrame = GetUnsigned(arguments, "startFrame", 0);
const auto levels = GetUnsigned(arguments, "levels", 0);
const auto endFrame = (levels == 0) ? INT64_MAX : (startFrame + levels);
auto totalFrames = thread.GetNumFrames();
// This will always return an invalid thread when
// libBacktraceRecording.dylib is not loaded or if there is no extended
// backtrace.
lldb::SBThread queue_backtrace_thread =
thread.GetExtendedBacktraceThread("libdispatch");
if (queue_backtrace_thread.IsValid()) {
// One extra frame as a label to mark the enqueued thread.
totalFrames += queue_backtrace_thread.GetNumFrames() + 1;
}
// This will always return an invalid thread when there is no exception in
// the current thread.
lldb::SBThread exception_backtrace_thread =
thread.GetCurrentExceptionBacktrace();
if (exception_backtrace_thread.IsValid()) {
// One extra frame as a label to mark the exception thread.
totalFrames += exception_backtrace_thread.GetNumFrames() + 1;
}
for (uint32_t i = startFrame; i < endFrame; ++i) {
auto frame = thread.GetFrameAtIndex(i);
lldb::SBFrame frame;
std::string prefix;
if (i < thread.GetNumFrames()) {
frame = thread.GetFrameAtIndex(i);
} else if (queue_backtrace_thread.IsValid() &&
i < (thread.GetNumFrames() +
queue_backtrace_thread.GetNumFrames() + 1)) {
if (i == thread.GetNumFrames()) {
const uint32_t thread_idx =
queue_backtrace_thread.GetExtendedBacktraceOriginatingIndexID();
const char *queue_name = queue_backtrace_thread.GetQueueName();
auto name = llvm::formatv("Enqueued from {0} (Thread {1})",
queue_name, thread_idx);
stackFrames.emplace_back(
llvm::json::Object{{"id", thread.GetThreadID() + 1},
{"name", name},
{"presentationHint", "label"}});
continue;
}
frame = queue_backtrace_thread.GetFrameAtIndex(
i - thread.GetNumFrames() - 1);
} else if (exception_backtrace_thread.IsValid()) {
if (i == thread.GetNumFrames() +
(queue_backtrace_thread.IsValid()
? queue_backtrace_thread.GetNumFrames() + 1
: 0)) {
stackFrames.emplace_back(
llvm::json::Object{{"id", thread.GetThreadID() + 2},
{"name", "Original Exception Backtrace"},
{"presentationHint", "label"}});
continue;
}
frame = exception_backtrace_thread.GetFrameAtIndex(
i - thread.GetNumFrames() -
(queue_backtrace_thread.IsValid()
? queue_backtrace_thread.GetNumFrames() + 1
: 0));
}
if (!frame.IsValid())
break;
stackFrames.emplace_back(CreateStackFrame(frame));
}
const auto totalFrames = thread.GetNumFrames();
body.try_emplace("totalFrames", totalFrames);
}
body.try_emplace("stackFrames", std::move(stackFrames));