[Lldb-commits] [lldb] 1654d7d - [lldb-dap] Add an option to provide a format for threads (#72196)

via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 14 10:23:59 PST 2023


Author: Walter Erquinigo
Date: 2023-11-14T13:23:55-05:00
New Revision: 1654d7dc38af3bd10ab55929161966601672c115

URL: https://github.com/llvm/llvm-project/commit/1654d7dc38af3bd10ab55929161966601672c115
DIFF: https://github.com/llvm/llvm-project/commit/1654d7dc38af3bd10ab55929161966601672c115.diff

LOG: [lldb-dap] Add an option to provide a format for threads (#72196)

When this option gets enabled, descriptions of threads will be generated
using the format provided in the launch configuration instead of
generating it manually in the dap code. This allows lldb-dap to show an
output similar to the one in the CLI.
This is very similar to https://github.com/llvm/llvm-project/pull/71843

Added: 
    lldb/test/API/tools/lldb-dap/threads/Makefile
    lldb/test/API/tools/lldb-dap/threads/TestDAP_threads.py
    lldb/test/API/tools/lldb-dap/threads/main.c

Modified: 
    lldb/include/lldb/API/SBFormat.h
    lldb/include/lldb/API/SBThread.h
    lldb/include/lldb/Target/Thread.h
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
    lldb/source/API/SBThread.cpp
    lldb/source/Target/Thread.cpp
    lldb/tools/lldb-dap/DAP.cpp
    lldb/tools/lldb-dap/DAP.h
    lldb/tools/lldb-dap/JSONUtils.cpp
    lldb/tools/lldb-dap/lldb-dap.cpp
    lldb/tools/lldb-dap/package.json

Removed: 
    lldb/test/API/tools/lldb-dap/correct-thread/Makefile
    lldb/test/API/tools/lldb-dap/correct-thread/TestDAP_correct_thread.py
    lldb/test/API/tools/lldb-dap/correct-thread/main.c


################################################################################
diff  --git a/lldb/include/lldb/API/SBFormat.h b/lldb/include/lldb/API/SBFormat.h
index 1bbaad18cafafab..757340ab2b5c2a4 100644
--- a/lldb/include/lldb/API/SBFormat.h
+++ b/lldb/include/lldb/API/SBFormat.h
@@ -52,6 +52,7 @@ class LLDB_API SBFormat {
 
 protected:
   friend class SBFrame;
+  friend class SBThread;
 
   /// \return
   ///   The underlying shared pointer storage for this object.

diff  --git a/lldb/include/lldb/API/SBThread.h b/lldb/include/lldb/API/SBThread.h
index 49c3d954fa93329..dcf6aa9d5424e85 100644
--- a/lldb/include/lldb/API/SBThread.h
+++ b/lldb/include/lldb/API/SBThread.h
@@ -200,6 +200,21 @@ class LLDB_API SBThread {
 
   bool GetDescription(lldb::SBStream &description, bool stop_format) const;
 
+  /// Similar to \a GetDescription() but the format of the description can be
+  /// configured via the \p format parameter. See
+  /// https://lldb.llvm.org/use/formatting.html for more information on format
+  /// strings.
+  ///
+  /// \param[in] format
+  ///   The format to use for generating the description.
+  ///
+  /// \param[out] output
+  ///   The stream where the description will be written to.
+  ///
+  /// \return
+  ///   An error object with an error message in case of failures.
+  SBError GetDescriptionWithFormat(const SBFormat &format, SBStream &output);
+
   bool GetStatus(lldb::SBStream &status) const;
 
   SBThread GetExtendedBacktraceThread(const char *type);

diff  --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index dabdbb8d455021a..e423dd4a6d2baa7 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -490,6 +490,23 @@ class Thread : public std::enable_shared_from_this<Thread>,
   void DumpTraceInstructions(Stream &s, size_t count,
                              size_t start_position = 0) const;
 
+  /// Print a description of this thread using the provided thread format.
+  ///
+  /// \param[out] strm
+  ///   The Stream to print the description to.
+  ///
+  /// \param[in] frame_idx
+  ///   If not \b LLDB_INVALID_FRAME_ID, then use this frame index as context to
+  ///   generate the description.
+  ///
+  /// \param[in] format
+  ///   The input format.
+  ///
+  /// \return
+  ///   \b true if and only if dumping with the given \p format worked.
+  bool DumpUsingFormat(Stream &strm, uint32_t frame_idx,
+                       const FormatEntity::Entry *format);
+
   // If stop_format is true, this will be the form used when we print stop
   // info. If false, it will be the form we use for thread list and co.
   void DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,

diff  --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
index a41861c59d2875a..518e3b9cf5bab33 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py
@@ -733,6 +733,7 @@ def request_launch(
         enableSyntheticChildDebugging=False,
         commandEscapePrefix="`",
         customFrameFormat=None,
+        customThreadFormat=None,
     ):
         args_dict = {"program": program}
         if args:
@@ -776,6 +777,8 @@ def request_launch(
             args_dict["postRunCommands"] = postRunCommands
         if customFrameFormat:
             args_dict["customFrameFormat"] = customFrameFormat
+        if customThreadFormat:
+            args_dict["customThreadFormat"] = customThreadFormat
 
         args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
         args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging

diff  --git a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
index 2c9f8703d56b405..9d79872b029a334 100644
--- a/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
+++ b/lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
@@ -353,6 +353,7 @@ def launch(
         enableSyntheticChildDebugging=False,
         commandEscapePrefix="`",
         customFrameFormat=None,
+        customThreadFormat=None,
     ):
         """Sending launch request to dap"""
 
@@ -393,6 +394,7 @@ def cleanup():
             enableSyntheticChildDebugging=enableSyntheticChildDebugging,
             commandEscapePrefix=commandEscapePrefix,
             customFrameFormat=customFrameFormat,
+            customThreadFormat=customThreadFormat,
         )
 
         if expectFailure:
@@ -431,6 +433,7 @@ def build_and_launch(
         enableSyntheticChildDebugging=False,
         commandEscapePrefix="`",
         customFrameFormat=None,
+        customThreadFormat=None,
     ):
         """Build the default Makefile target, create the DAP debug adaptor,
         and launch the process.
@@ -463,4 +466,5 @@ def build_and_launch(
             enableSyntheticChildDebugging=enableSyntheticChildDebugging,
             commandEscapePrefix=commandEscapePrefix,
             customFrameFormat=customFrameFormat,
+            customThreadFormat=customThreadFormat,
         )

diff  --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 15c909b0b46008f..18c086bb04c682f 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -12,6 +12,7 @@
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBFormat.h"
 #include "lldb/API/SBFrame.h"
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBStream.h"
@@ -1223,17 +1224,41 @@ bool SBThread::GetDescription(SBStream &description, bool stop_format) const {
   ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
 
   if (exe_ctx.HasThreadScope()) {
-    exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(strm,
-                                                    LLDB_INVALID_THREAD_ID,
-                                                    stop_format);
-    // strm.Printf("SBThread: tid = 0x%4.4" PRIx64,
-    // exe_ctx.GetThreadPtr()->GetID());
+    exe_ctx.GetThreadPtr()->DumpUsingSettingsFormat(
+        strm, LLDB_INVALID_THREAD_ID, stop_format);
   } else
     strm.PutCString("No value");
 
   return true;
 }
 
+SBError SBThread::GetDescriptionWithFormat(const SBFormat &format,
+                                           SBStream &output) {
+  Stream &strm = output.ref();
+
+  SBError error;
+  if (!format) {
+    error.SetErrorString("The provided SBFormat object is invalid");
+    return error;
+  }
+
+  std::unique_lock<std::recursive_mutex> lock;
+  ExecutionContext exe_ctx(m_opaque_sp.get(), lock);
+
+  if (exe_ctx.HasThreadScope()) {
+    if (exe_ctx.GetThreadPtr()->DumpUsingFormat(
+            strm, LLDB_INVALID_THREAD_ID, format.GetFormatEntrySP().get())) {
+      return error;
+    }
+  }
+
+  error.SetErrorStringWithFormat(
+      "It was not possible to generate a thread description with the given "
+      "format string '%s'",
+      format.GetFormatEntrySP()->string.c_str());
+  return error;
+}
+
 SBThread SBThread::GetExtendedBacktraceThread(const char *type) {
   LLDB_INSTRUMENT_VA(this, type);
 

diff  --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 6731b76c87b88b6..865cee97e6d8783 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -1589,12 +1589,12 @@ Status Thread::JumpToLine(const FileSpec &file, uint32_t line,
   return Status();
 }
 
-void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
-                                     bool stop_format) {
+bool Thread::DumpUsingFormat(Stream &strm, uint32_t frame_idx,
+                             const FormatEntity::Entry *format) {
   ExecutionContext exe_ctx(shared_from_this());
   Process *process = exe_ctx.GetProcessPtr();
-  if (process == nullptr)
-    return;
+  if (!process || !format)
+    return false;
 
   StackFrameSP frame_sp;
   SymbolContext frame_sc;
@@ -1606,6 +1606,14 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
     }
   }
 
+  return FormatEntity::Format(*format, strm, frame_sp ? &frame_sc : nullptr,
+                              &exe_ctx, nullptr, nullptr, false, false);
+}
+
+void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
+                                     bool stop_format) {
+  ExecutionContext exe_ctx(shared_from_this());
+
   const FormatEntity::Entry *thread_format;
   if (stop_format)
     thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadStopFormat();
@@ -1614,8 +1622,7 @@ void Thread::DumpUsingSettingsFormat(Stream &strm, uint32_t frame_idx,
 
   assert(thread_format);
 
-  FormatEntity::Format(*thread_format, strm, frame_sp ? &frame_sc : nullptr,
-                       &exe_ctx, nullptr, nullptr, false, false);
+  DumpUsingFormat(strm, frame_idx, thread_format);
 }
 
 void Thread::SettingsInitialize() {}

diff  --git a/lldb/test/API/tools/lldb-dap/correct-thread/Makefile b/lldb/test/API/tools/lldb-dap/threads/Makefile
similarity index 100%
rename from lldb/test/API/tools/lldb-dap/correct-thread/Makefile
rename to lldb/test/API/tools/lldb-dap/threads/Makefile

diff  --git a/lldb/test/API/tools/lldb-dap/correct-thread/TestDAP_correct_thread.py b/lldb/test/API/tools/lldb-dap/threads/TestDAP_threads.py
similarity index 63%
rename from lldb/test/API/tools/lldb-dap/correct-thread/TestDAP_correct_thread.py
rename to lldb/test/API/tools/lldb-dap/threads/TestDAP_threads.py
index 43d3b02e408c79b..35d2d32eefdc5c0 100644
--- a/lldb/test/API/tools/lldb-dap/correct-thread/TestDAP_correct_thread.py
+++ b/lldb/test/API/tools/lldb-dap/threads/TestDAP_threads.py
@@ -2,14 +2,13 @@
 Test lldb-dap setBreakpoints request
 """
 
-import dap_server
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
 import lldbdap_testcase
 
 
-class TestDAP_correct_thread(lldbdap_testcase.DAPTestCaseBase):
+class TestDAP_threads(lldbdap_testcase.DAPTestCaseBase):
     @skipIfWindows
     @skipIfRemote
     def test_correct_thread(self):
@@ -44,3 +43,27 @@ def test_correct_thread(self):
         )
         self.assertFalse(stopped_event[0]["body"]["preserveFocusHint"])
         self.assertTrue(stopped_event[0]["body"]["threadCausedFocus"])
+
+    @skipIfWindows
+    @skipIfRemote
+    def test_thread_format(self):
+        """
+        Tests the support for custom thread formats.
+        """
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(
+            program, customThreadFormat="This is thread index #${thread.index}"
+        )
+        source = "main.c"
+        breakpoint_line = line_number(source, "// break here")
+        lines = [breakpoint_line]
+        # Set breakpoint in the thread function
+        breakpoint_ids = self.set_source_breakpoints(source, lines)
+        self.assertEqual(
+            len(breakpoint_ids), len(lines), "expect correct number of breakpoints"
+        )
+        self.continue_to_breakpoints(breakpoint_ids)
+        # We are stopped at the second thread
+        threads = self.dap_server.get_threads()
+        self.assertEquals(threads[0]["name"], "This is thread index #1")
+        self.assertEquals(threads[1]["name"], "This is thread index #2")

diff  --git a/lldb/test/API/tools/lldb-dap/correct-thread/main.c b/lldb/test/API/tools/lldb-dap/threads/main.c
similarity index 100%
rename from lldb/test/API/tools/lldb-dap/correct-thread/main.c
rename to lldb/test/API/tools/lldb-dap/threads/main.c

diff  --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index ba111a77d6e5496..492662097397653 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -828,7 +828,7 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
   if (format.empty())
     return;
   lldb::SBError error;
-  g_dap.frame_format = lldb::SBFormat(format.data(), error);
+  g_dap.frame_format = lldb::SBFormat(format.str().c_str(), error);
   if (error.Fail()) {
     g_dap.SendOutput(
         OutputType::Console,
@@ -839,4 +839,19 @@ void DAP::SetFrameFormat(llvm::StringRef format) {
   }
 }
 
+void DAP::SetThreadFormat(llvm::StringRef format) {
+  if (format.empty())
+    return;
+  lldb::SBError error;
+  g_dap.thread_format = lldb::SBFormat(format.str().c_str(), error);
+  if (error.Fail()) {
+    g_dap.SendOutput(
+        OutputType::Console,
+        llvm::formatv(
+            "The provided thread format '{0}' couldn't be parsed: {1}\n",
+            format, error.GetCString())
+            .str());
+  }
+}
+
 } // namespace lldb_dap

diff  --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 11c2cc1fa8646a9..c7d56a06bfa1fdf 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -191,6 +191,7 @@ struct DAP {
   bool auto_repl_mode_collision_warning;
   std::string command_escape_prefix = "`";
   lldb::SBFormat frame_format;
+  lldb::SBFormat thread_format;
 
   DAP();
   ~DAP();
@@ -309,6 +310,8 @@ struct DAP {
 
   void SetFrameFormat(llvm::StringRef format);
 
+  void SetThreadFormat(llvm::StringRef format);
+
 private:
   // Send the JSON in "json_str" to the "out" stream. Correctly send the
   // "Content-Length:" field followed by the length, followed by the raw

diff  --git a/lldb/tools/lldb-dap/JSONUtils.cpp b/lldb/tools/lldb-dap/JSONUtils.cpp
index 2023291729762f1..50ade02801529c3 100644
--- a/lldb/tools/lldb-dap/JSONUtils.cpp
+++ b/lldb/tools/lldb-dap/JSONUtils.cpp
@@ -852,26 +852,33 @@ 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());
-  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();
+  lldb::SBStream stream;
+  if (g_dap.thread_format &&
+      thread.GetDescriptionWithFormat(g_dap.thread_format, stream).Success()) {
+    thread_str = stream.GetData();
   } else {
-    thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
+    const char *thread_name = thread.GetName();
+    const char *queue_name = thread.GetQueueName();
+
+    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 {
+      thread_str = llvm::formatv("Thread {0}", thread.GetIndexID()).str();
+    }
   }
 
   EmplaceSafeString(object, "name", thread_str);

diff  --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index 01738b3f5150ba3..d6b593eba93ecaf 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -654,6 +654,7 @@ void request_attach(const llvm::json::Object &request) {
   g_dap.command_escape_prefix =
       GetString(arguments, "commandEscapePrefix", "`");
   g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
+  g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
 
   // This is a hack for loading DWARF in .o files on Mac where the .o files
   // in the debug map of the main executable have relative paths which require
@@ -1807,6 +1808,7 @@ void request_launch(const llvm::json::Object &request) {
   g_dap.command_escape_prefix =
       GetString(arguments, "commandEscapePrefix", "`");
   g_dap.SetFrameFormat(GetString(arguments, "customFrameFormat"));
+  g_dap.SetThreadFormat(GetString(arguments, "customThreadFormat"));
 
   // This is a hack for loading DWARF in .o files on Mac where the .o files
   // in the debug map of the main executable have relative paths which require

diff  --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json
index 6d6c46b55518c07..ebb1103d695e175 100644
--- a/lldb/tools/lldb-dap/package.json
+++ b/lldb/tools/lldb-dap/package.json
@@ -260,6 +260,11 @@
 								"type": "string",
 								"description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
 								"default": ""
+							},
+							"customThreadFormat": {
+								"type": "string",
+								"description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
+								"default": ""
 							}
 						}
 					},
@@ -359,6 +364,11 @@
 								"type": "string",
 								"description": "If non-empty, stack frames will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for frames. If the format string contains errors, an error message will be displayed on the Debug Console and the default frame names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
 								"default": ""
+							},
+							"customThreadFormat": {
+								"type": "string",
+								"description": "If non-empty, threads will have descriptions generated based on the provided format. See https://lldb.llvm.org/use/formatting.html for an explanation on format strings for threads. If the format string contains errors, an error message will be displayed on the Debug Console and the default thread names will be used. This might come with a performance cost because debug information might need to be processed to generate the description.",
+								"default": ""
 							}
 						}
 					}


        


More information about the lldb-commits mailing list