Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/tools/lldb-dap/output/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C_SOURCES := main.c

include Makefile.rules
31 changes: 31 additions & 0 deletions lldb/test/API/tools/lldb-dap/output/TestDAP_output.py
Original file line number Diff line number Diff line change
@@ -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,
)
12 changes: 12 additions & 0 deletions lldb/test/API/tools/lldb-dap/output/main.c
Original file line number Diff line number Diff line change
@@ -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;
}
22 changes: 16 additions & 6 deletions lldb/tools/lldb-dap/DAP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions lldb/tools/lldb-dap/DAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lldb/tools/lldb-dap/OutputRedirector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <unistd.h>
#endif

#include "DAP.h"
#include "OutputRedirector.h"
#include "llvm/ADT/StringRef.h"

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lldb/tools/lldb-dap/lldb-dap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Loading