[Lldb-commits] [lldb] [lldb-dap] Improve `stackTrace` and `exceptionInfo` DAP request handlers (PR #105905)

John Harrison via lldb-commits lldb-commits at lists.llvm.org
Wed Aug 28 14:29:41 PDT 2024


================
@@ -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


More information about the lldb-commits mailing list