================ @@ -3106,80 +3228,105 @@ void request_stackTrace(const llvm::json::Object &request) { llvm::json::Array stackFrames; llvm::json::Object body; + // Threads stacks may contain runtime specific extended backtraces, when + // constructing a stack trace first report the full thread stack trace then + // perform a breadth first traversal of any extended backtrace frames. + // + // For example: + // + // Thread (id=th0) stack=[s0, s1, s2, s3] + // \ Extended backtrace "libdispatch" Thread (id=th1) stack=[s0, s1] + // \ Extended backtrace "libdispatch" Thread (id=th2) stack=[s0, s1] + // \ Extended backtrace "Application Specific Backtrace" Thread (id=th3) + // stack=[s0, s1, s2] + // + // Which will flatten into: + // + // 0. th0->s0 + // 1. th0->s1 + // 2. th0->s2 + // 3. th0->s3 + // 4. label - Enqueued from th1 + // 5. th1->s0 + // 6. th1->s1 + // 7. label - Enqueued from th2 + // 8. th2->s0 + // 9. th2->s1 + // 10. label - Application Specific Backtrace + // 11. th3->s0 + // 12. th3->s1 + // 13. th3->s2 + if (thread.IsValid()) { 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; - if (g_dap.enable_display_extended_backtrace) - 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; - } + bool done = false; + int64_t offset = 0; + lldb::SBProcess process = thread.GetProcess(); + llvm::SmallVector<lldb::SBThread> threadCluster{{thread}}; + + for (uint32_t i = startFrame; i < endFrame && !threadCluster.empty(); ++i) { + lldb::SBThread current = threadCluster.front(); + lldb::SBFrame frame = current.GetFrameAtIndex(i - offset); + + // If we don't have a valid frame, check if we have any extended frames to + // report. + // *NOTE*: Threads can be chained across mutliple backtraces, so we + // need to keep track of each backtrace we've traversed fully in the + // offset. + while (!frame.IsValid() && current.IsValid() && !threadCluster.empty()) { + offset += current.GetNumFrames() + + 1 /* one extra frame for a label between threads*/; + threadCluster.pop_back(); + + // Check for any extended backtraces. + for (uint32_t i = 0; i < process.GetNumExtendedBacktraceTypes(); i++) { + lldb::SBThread backtrace = current.GetExtendedBacktraceThread( + process.GetExtendedBacktraceTypeAtIndex(i)); + if (backtrace.IsValid()) { + threadCluster.emplace_back(backtrace); + } + } - // This will always return an invalid thread when there is no exception in - // the current thread. - lldb::SBThread exception_backtrace_thread; - if (g_dap.enable_display_extended_backtrace) - exception_backtrace_thread = thread.GetCurrentExceptionBacktrace(); + if (threadCluster.empty()) + break; - if (exception_backtrace_thread.IsValid()) { - // One extra frame as a label to mark the exception thread. - totalFrames += exception_backtrace_thread.GetNumFrames() + 1; - } + current = threadCluster.front(); + frame = current.GetFrameAtIndex(0); + } - for (uint32_t i = startFrame; i < endFrame; ++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; - } + // If we're out of extended backtraces, no more frames to load. + if (!frame.IsValid() && threadCluster.empty()) { + done = true; + break; + } - frame = exception_backtrace_thread.GetFrameAtIndex( - i - thread.GetNumFrames() - - (queue_backtrace_thread.IsValid() - ? queue_backtrace_thread.GetNumFrames() + 1 - : 0)); + // Between the thread and extended backtrace add a label. + if (offset != 0 && (i - offset) == 0) { + const uint32_t thread_idx = + current.GetExtendedBacktraceOriginatingIndexID(); + const char *queue_name = current.GetQueueName(); + std::string name; + if (queue_name != nullptr) { + name = llvm::formatv("Enqueued from {0} (Thread {1})", queue_name, + thread_idx); + } else { + name = llvm::formatv("Thread {0}", thread_idx); + } + stackFrames.emplace_back( + llvm::json::Object{{"id", thread.GetThreadID() + 1}, + {"name", name}, + {"presentationHint", "label"}}); + } else { + stackFrames.emplace_back(CreateStackFrame(frame)); } - if (!frame.IsValid()) - break; - stackFrames.emplace_back(CreateStackFrame(frame)); } - body.try_emplace("totalFrames", totalFrames); + // If we loaded all the frames, set the total frame to the current total, + // otherwise use the totalFrames to indiciate more data is available. + body.try_emplace("totalFrames", ---------------- ashgti wrote:
Done. I had tested this but the 20 page size does work better with deep stack traces. Also updated the tests to verify this as well. https://github.com/llvm/llvm-project/pull/105905 _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits