[Lldb-commits] [lldb] 30ca06c - [lldb-dap] When sending a DAP Output Event break each message into separate lines. (#105456)

via lldb-commits lldb-commits at lists.llvm.org
Wed Aug 21 13:48:34 PDT 2024


Author: John Harrison
Date: 2024-08-21T13:48:29-07:00
New Revision: 30ca06c4d0d06f67f10a9e19d4333acc2074811b

URL: https://github.com/llvm/llvm-project/commit/30ca06c4d0d06f67f10a9e19d4333acc2074811b
DIFF: https://github.com/llvm/llvm-project/commit/30ca06c4d0d06f67f10a9e19d4333acc2074811b.diff

LOG: [lldb-dap] When sending a DAP Output Event break each message into separate lines. (#105456)

Previously, when output like `"hello\nworld\n"` was produced by lldb (or
the process) the message would be sent as a single Output event. By
being a single event this causes VS Code to treat this as a single
message in the console when handling displaying and filtering in the
Debug Console.

Instead, with these changes we send each line as its own event. This
results in VS Code representing each line of output from lldb-dap as an
individual output message.

Resolves #105444

Added: 
    lldb/test/API/tools/lldb-dap/output/Makefile
    lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
    lldb/test/API/tools/lldb-dap/output/main.c

Modified: 
    lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py
    lldb/tools/lldb-dap/DAP.cpp
    lldb/tools/lldb-dap/DAP.h
    lldb/tools/lldb-dap/OutputRedirector.cpp
    lldb/tools/lldb-dap/lldb-dap.cpp

Removed: 
    


################################################################################
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 27545816f20707..86eba355da83db 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
@@ -202,6 +202,11 @@ def collect_console(self, timeout_secs, pattern=None):
             "console", timeout_secs=timeout_secs, pattern=pattern
         )
 
+    def collect_stdout(self, timeout_secs, pattern=None):
+        return self.dap_server.collect_output(
+            "stdout", timeout_secs=timeout_secs, pattern=pattern
+        )
+
     def get_local_as_int(self, name, threadId=None):
         value = self.dap_server.get_local_variable_value(name, threadId=threadId)
         # 'value' may have the variable value and summary.

diff  --git a/lldb/test/API/tools/lldb-dap/output/Makefile b/lldb/test/API/tools/lldb-dap/output/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/output/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules

diff  --git a/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
new file mode 100644
index 00000000000000..0d40ce993dc31c
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
@@ -0,0 +1,31 @@
+"""
+Test lldb-dap output events
+"""
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+import lldbdap_testcase
+
+
+class TestDAP_output(lldbdap_testcase.DAPTestCaseBase):
+    def test_output(self):
+        program = self.getBuildArtifact("a.out")
+        self.build_and_launch(program)
+        source = "main.c"
+        lines = [line_number(source, "// breakpoint 1")]
+        breakpoint_ids = self.set_source_breakpoints(source, lines)
+        self.continue_to_breakpoints(breakpoint_ids)
+        
+        # Ensure partial messages are still sent.
+        output = self.collect_stdout(timeout_secs=1.0, pattern="abcdef")
+        self.assertTrue(output and len(output) > 0, "expect no program output")
+
+        self.continue_to_exit()
+        
+        output += self.get_stdout(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
+        self.assertTrue(output and len(output) > 0, "expect no program output")
+        self.assertIn(
+            "abcdefghi\r\nhello world\r\n",
+            output,
+            'full output not found in: ' + output,
+        )

diff  --git a/lldb/test/API/tools/lldb-dap/output/main.c b/lldb/test/API/tools/lldb-dap/output/main.c
new file mode 100644
index 00000000000000..0cfcf604aa68f7
--- /dev/null
+++ b/lldb/test/API/tools/lldb-dap/output/main.c
@@ -0,0 +1,12 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main() {
+  // Ensure multiple partial lines are detected and sent.
+  printf("abc");
+  printf("def");
+  printf("ghi\n");
+  printf("hello world\n"); // breakpoint 1
+  return 0;
+}

diff  --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp
index c3c70e9d739846..1fd560f21904ab 100644
--- a/lldb/tools/lldb-dap/DAP.cpp
+++ b/lldb/tools/lldb-dap/DAP.cpp
@@ -294,8 +294,6 @@ void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
   if (output.empty())
     return;
 
-  llvm::json::Object event(CreateEventObject("output"));
-  llvm::json::Object body;
   const char *category = nullptr;
   switch (o) {
   case OutputType::Console:
@@ -311,10 +309,22 @@ void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
     category = "telemetry";
     break;
   }
-  body.try_emplace("category", category);
-  EmplaceSafeString(body, "output", output.str());
-  event.try_emplace("body", std::move(body));
-  SendJSON(llvm::json::Value(std::move(event)));
+
+  // Send each line of output as an individual event, including the newline if
+  // present.
+  ::size_t idx = 0;
+  do {
+    ::size_t end = output.find('\n', idx);
+    if (end == llvm::StringRef::npos)
+      end = output.size() - 1;
+    llvm::json::Object event(CreateEventObject("output"));
+    llvm::json::Object body;
+    body.try_emplace("category", category);
+    EmplaceSafeString(body, "output", output.slice(idx, end + 1).str());
+    event.try_emplace("body", std::move(body));
+    SendJSON(llvm::json::Value(std::move(event)));
+    idx = end + 1;
+  } while (idx < output.size());
 }
 
 // interface ProgressStartEvent extends Event {

diff  --git a/lldb/tools/lldb-dap/DAP.h b/lldb/tools/lldb-dap/DAP.h
index 7828272aa15a7d..27ea6c7ff8423f 100644
--- a/lldb/tools/lldb-dap/DAP.h
+++ b/lldb/tools/lldb-dap/DAP.h
@@ -68,8 +68,12 @@ namespace lldb_dap {
 
 typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
 typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
+
 enum class OutputType { Console, Stdout, Stderr, Telemetry };
 
+/// Buffer size for handling output events.
+constexpr uint64_t OutputBufferSize = (1u << 12);
+
 enum DAPBroadcasterBits {
   eBroadcastBitStopEventThread = 1u << 0,
   eBroadcastBitStopProgressThread = 1u << 1

diff  --git a/lldb/tools/lldb-dap/OutputRedirector.cpp b/lldb/tools/lldb-dap/OutputRedirector.cpp
index 4e6907ce6c7806..2c2f49569869b4 100644
--- a/lldb/tools/lldb-dap/OutputRedirector.cpp
+++ b/lldb/tools/lldb-dap/OutputRedirector.cpp
@@ -13,6 +13,7 @@
 #include <unistd.h>
 #endif
 
+#include "DAP.h"
 #include "OutputRedirector.h"
 #include "llvm/ADT/StringRef.h"
 
@@ -42,7 +43,7 @@ Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
 
   int read_fd = new_fd[0];
   std::thread t([read_fd, callback]() {
-    char buffer[4096];
+    char buffer[OutputBufferSize];
     while (true) {
       ssize_t bytes_count = read(read_fd, &buffer, sizeof(buffer));
       if (bytes_count == 0)

diff  --git a/lldb/tools/lldb-dap/lldb-dap.cpp b/lldb/tools/lldb-dap/lldb-dap.cpp
index b534a48660a5f8..7b83767d1afeab 100644
--- a/lldb/tools/lldb-dap/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/lldb-dap.cpp
@@ -399,7 +399,7 @@ void SendProcessEvent(LaunchMethod launch_method) {
 // Grab any STDOUT and STDERR from the process and send it up to VS Code
 // via an "output" event to the "stdout" and "stderr" categories.
 void SendStdOutStdErr(lldb::SBProcess &process) {
-  char buffer[1024];
+  char buffer[OutputBufferSize];
   size_t count;
   while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
     g_dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));


        


More information about the lldb-commits mailing list