Skip to content

Commit d39f024

Browse files
ashgtiJDevlieghere
authored andcommitted
[lldb-dap] When sending a DAP Output Event break each message into separate lines. (llvm#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 llvm#105444 (cherry picked from commit 30ca06c)
1 parent b6cf44e commit d39f024

File tree

8 files changed

+74
-8
lines changed

8 files changed

+74
-8
lines changed

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ def collect_console(self, timeout_secs, pattern=None):
202202
"console", timeout_secs=timeout_secs, pattern=pattern
203203
)
204204

205+
def collect_stdout(self, timeout_secs, pattern=None):
206+
return self.dap_server.collect_output(
207+
"stdout", timeout_secs=timeout_secs, pattern=pattern
208+
)
209+
205210
def get_local_as_int(self, name, threadId=None):
206211
value = self.dap_server.get_local_variable_value(name, threadId=threadId)
207212
# 'value' may have the variable value and summary.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Test lldb-dap output events
3+
"""
4+
5+
from lldbsuite.test.decorators import *
6+
from lldbsuite.test.lldbtest import *
7+
import lldbdap_testcase
8+
9+
10+
class TestDAP_output(lldbdap_testcase.DAPTestCaseBase):
11+
def test_output(self):
12+
program = self.getBuildArtifact("a.out")
13+
self.build_and_launch(program)
14+
source = "main.c"
15+
lines = [line_number(source, "// breakpoint 1")]
16+
breakpoint_ids = self.set_source_breakpoints(source, lines)
17+
self.continue_to_breakpoints(breakpoint_ids)
18+
19+
# Ensure partial messages are still sent.
20+
output = self.collect_stdout(timeout_secs=1.0, pattern="abcdef")
21+
self.assertTrue(output and len(output) > 0, "expect no program output")
22+
23+
self.continue_to_exit()
24+
25+
output += self.get_stdout(timeout=lldbdap_testcase.DAPTestCaseBase.timeoutval)
26+
self.assertTrue(output and len(output) > 0, "expect no program output")
27+
self.assertIn(
28+
"abcdefghi\r\nhello world\r\n",
29+
output,
30+
'full output not found in: ' + output,
31+
)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <unistd.h>
4+
5+
int main() {
6+
// Ensure multiple partial lines are detected and sent.
7+
printf("abc");
8+
printf("def");
9+
printf("ghi\n");
10+
printf("hello world\n"); // breakpoint 1
11+
return 0;
12+
}

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,6 @@ void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
294294
if (output.empty())
295295
return;
296296

297-
llvm::json::Object event(CreateEventObject("output"));
298-
llvm::json::Object body;
299297
const char *category = nullptr;
300298
switch (o) {
301299
case OutputType::Console:
@@ -311,10 +309,22 @@ void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
311309
category = "telemetry";
312310
break;
313311
}
314-
body.try_emplace("category", category);
315-
EmplaceSafeString(body, "output", output.str());
316-
event.try_emplace("body", std::move(body));
317-
SendJSON(llvm::json::Value(std::move(event)));
312+
313+
// Send each line of output as an individual event, including the newline if
314+
// present.
315+
::size_t idx = 0;
316+
do {
317+
::size_t end = output.find('\n', idx);
318+
if (end == llvm::StringRef::npos)
319+
end = output.size() - 1;
320+
llvm::json::Object event(CreateEventObject("output"));
321+
llvm::json::Object body;
322+
body.try_emplace("category", category);
323+
EmplaceSafeString(body, "output", output.slice(idx, end + 1).str());
324+
event.try_emplace("body", std::move(body));
325+
SendJSON(llvm::json::Value(std::move(event)));
326+
idx = end + 1;
327+
} while (idx < output.size());
318328
}
319329

320330
// interface ProgressStartEvent extends Event {

lldb/tools/lldb-dap/DAP.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,12 @@ namespace lldb_dap {
6868

6969
typedef llvm::DenseMap<uint32_t, SourceBreakpoint> SourceBreakpointMap;
7070
typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap;
71+
7172
enum class OutputType { Console, Stdout, Stderr, Telemetry };
7273

74+
/// Buffer size for handling output events.
75+
constexpr uint64_t OutputBufferSize = (1u << 12);
76+
7377
enum DAPBroadcasterBits {
7478
eBroadcastBitStopEventThread = 1u << 0,
7579
eBroadcastBitStopProgressThread = 1u << 1

lldb/tools/lldb-dap/OutputRedirector.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <unistd.h>
1414
#endif
1515

16+
#include "DAP.h"
1617
#include "OutputRedirector.h"
1718
#include "llvm/ADT/StringRef.h"
1819

@@ -42,7 +43,7 @@ Error RedirectFd(int fd, std::function<void(llvm::StringRef)> callback) {
4243

4344
int read_fd = new_fd[0];
4445
std::thread t([read_fd, callback]() {
45-
char buffer[4096];
46+
char buffer[OutputBufferSize];
4647
while (true) {
4748
ssize_t bytes_count = read(read_fd, &buffer, sizeof(buffer));
4849
if (bytes_count == 0)

lldb/tools/lldb-dap/lldb-dap.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ void SendProcessEvent(LaunchMethod launch_method) {
399399
// Grab any STDOUT and STDERR from the process and send it up to VS Code
400400
// via an "output" event to the "stdout" and "stderr" categories.
401401
void SendStdOutStdErr(lldb::SBProcess &process) {
402-
char buffer[1024];
402+
char buffer[OutputBufferSize];
403403
size_t count;
404404
while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
405405
g_dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));

0 commit comments

Comments
 (0)