Skip to content

Commit 4930c76

Browse files
committed
[lldb-dap] Refactoring IO handling for the SBDebugger.
This refactors the IO handling of lldb-dap and its SBDebugger. This adjusts the SBDebugger instance to have a pty associated with in/out/err. Using a pty allows us to handle commands with raw input modes, such as `script` or `breakpoint command add`. Additionally, to better handle output produced by the debugger and evaluate command I added a print helper. This new print helper will inspect the SBCommandReturnObject and store any associated variables in the variable store. This lets users more easily inspect variables directly in the Debug Console in VSCode.
1 parent 2c0e4e7 commit 4930c76

File tree

9 files changed

+181
-49
lines changed

9 files changed

+181
-49
lines changed

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "Protocol/ProtocolRequests.h"
2121
#include "Protocol/ProtocolTypes.h"
2222
#include "ProtocolUtils.h"
23-
#include "Transport.h"
2423
#include "lldb/API/SBBreakpoint.h"
2524
#include "lldb/API/SBCommandInterpreter.h"
2625
#include "lldb/API/SBEvent.h"
@@ -29,12 +28,13 @@
2928
#include "lldb/API/SBMutex.h"
3029
#include "lldb/API/SBProcess.h"
3130
#include "lldb/API/SBStream.h"
32-
#include "lldb/Host/JSONTransport.h"
31+
#include "lldb/Host/File.h"
3332
#include "lldb/Host/MainLoop.h"
3433
#include "lldb/Host/MainLoopBase.h"
3534
#include "lldb/Utility/Status.h"
3635
#include "lldb/lldb-defines.h"
3736
#include "lldb/lldb-enumerations.h"
37+
#include "lldb/lldb-forward.h"
3838
#include "lldb/lldb-types.h"
3939
#include "llvm/ADT/ArrayRef.h"
4040
#include "llvm/ADT/STLExtras.h"
@@ -55,7 +55,6 @@
5555
#include <cstdint>
5656
#include <cstdio>
5757
#include <functional>
58-
#include <future>
5958
#include <memory>
6059
#include <mutex>
6160
#include <optional>
@@ -77,14 +76,6 @@ using namespace lldb_dap;
7776
using namespace lldb_dap::protocol;
7877
using namespace lldb_private;
7978

80-
namespace {
81-
#ifdef _WIN32
82-
const char DEV_NULL[] = "nul";
83-
#else
84-
const char DEV_NULL[] = "/dev/null";
85-
#endif
86-
} // namespace
87-
8879
namespace lldb_dap {
8980

9081
static std::string GetStringFromStructuredData(lldb::SBStructuredData &data,
@@ -226,8 +217,6 @@ ExceptionBreakpoint *DAP::GetExceptionBreakpoint(const lldb::break_id_t bp_id) {
226217
}
227218

228219
llvm::Error DAP::ConfigureIO(std::FILE *overrideOut, std::FILE *overrideErr) {
229-
in = lldb::SBFile(std::fopen(DEV_NULL, "r"), /*transfer_ownership=*/true);
230-
231220
if (auto Error = out.RedirectTo(overrideOut, [this](llvm::StringRef output) {
232221
SendOutput(OutputType::Console, output);
233222
}))
@@ -375,7 +364,8 @@ Id DAP::Send(const Message &message) {
375364
// "required": [ "event", "body" ]
376365
// }]
377366
// }
378-
void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
367+
void DAP::SendOutput(OutputType o, const llvm::StringRef output,
368+
int64_t varref) {
379369
if (output.empty())
380370
return;
381371

@@ -409,6 +399,10 @@ void DAP::SendOutput(OutputType o, const llvm::StringRef output) {
409399
llvm::json::Object body;
410400
body.try_emplace("category", category);
411401
EmplaceSafeString(body, "output", output.slice(idx, end + 1).str());
402+
if (varref != 0) {
403+
body["variablesReference"] = varref;
404+
varref = 0; // In case there are multiple lines.
405+
}
412406
event.try_emplace("body", std::move(body));
413407
SendJSON(llvm::json::Value(std::move(event)));
414408
idx = end + 1;
@@ -1075,9 +1069,7 @@ llvm::Error DAP::Loop() {
10751069
auto thread = std::thread(std::bind(&DAP::TransportHandler, this));
10761070

10771071
auto cleanup = llvm::make_scope_exit([this]() {
1078-
// FIXME: Merge these into the MainLoop handler.
1079-
out.Stop();
1080-
err.Stop();
1072+
pty.ClosePrimaryFileDescriptor();
10811073
StopEventHandlers();
10821074

10831075
// Destroy the debugger when the session ends. This will trigger the
@@ -1304,6 +1296,33 @@ void DAP::StartEventThread() {
13041296
event_thread = std::thread(&DAP::EventThread, this);
13051297
}
13061298

1299+
void DAP::StartIOThread() {
1300+
lldb::FileSP file = std::make_shared<lldb_private::NativeFile>(
1301+
pty.GetPrimaryFileDescriptor(),
1302+
lldb_private::NativeFile::eOpenOptionReadWrite |
1303+
lldb_private::NativeFile::eOpenOptionNonBlocking,
1304+
false);
1305+
1306+
Status error;
1307+
handle = m_loop.RegisterReadObject(
1308+
file,
1309+
[this, file](MainLoopBase &loop) {
1310+
char buf[4096];
1311+
size_t len = sizeof(buf);
1312+
if (auto err = file->Read(buf, len).takeError())
1313+
DAP_LOG_ERROR(log, std::move(err), "Reading from pty failed {0}");
1314+
if (len == 0) { // EOF
1315+
loop.RequestTermination();
1316+
return;
1317+
}
1318+
llvm::StringRef output = {buf, len};
1319+
output = output.trim();
1320+
if (!output.empty())
1321+
SendOutput(OutputType::Console, output);
1322+
},
1323+
error);
1324+
}
1325+
13071326
void DAP::StartProgressEventThread() {
13081327
progress_event_thread = std::thread(&DAP::ProgressEventThread, this);
13091328
}

lldb/tools/lldb-dap/DAP.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,19 @@
2222
#include "Transport.h"
2323
#include "Variables.h"
2424
#include "lldb/API/SBBroadcaster.h"
25-
#include "lldb/API/SBCommandInterpreter.h"
25+
#include "lldb/API/SBCommandReturnObject.h"
2626
#include "lldb/API/SBDebugger.h"
2727
#include "lldb/API/SBError.h"
28-
#include "lldb/API/SBFile.h"
2928
#include "lldb/API/SBFormat.h"
3029
#include "lldb/API/SBFrame.h"
3130
#include "lldb/API/SBMutex.h"
3231
#include "lldb/API/SBTarget.h"
3332
#include "lldb/API/SBThread.h"
3433
#include "lldb/Host/MainLoop.h"
35-
#include "lldb/Utility/Status.h"
34+
#include "lldb/Host/PseudoTerminal.h"
3635
#include "lldb/lldb-types.h"
3736
#include "llvm/ADT/DenseMap.h"
3837
#include "llvm/ADT/DenseSet.h"
39-
#include "llvm/ADT/FunctionExtras.h"
4038
#include "llvm/ADT/SmallSet.h"
4139
#include "llvm/ADT/StringMap.h"
4240
#include "llvm/ADT/StringRef.h"
@@ -47,6 +45,7 @@
4745
#include <condition_variable>
4846
#include <cstdint>
4947
#include <deque>
48+
#include <future>
5049
#include <memory>
5150
#include <mutex>
5251
#include <optional>
@@ -80,13 +79,18 @@ enum class ReplMode { Variable = 0, Command, Auto };
8079

8180
using DAPTransport = lldb_private::transport::JSONTransport<ProtocolDescriptor>;
8281

82+
struct EvaluateContext {
83+
lldb::SBCommandReturnObject result;
84+
std::promise<void> done;
85+
};
86+
8387
struct DAP final : public DAPTransport::MessageHandler {
8488
/// Path to the lldb-dap binary itself.
8589
static llvm::StringRef debug_adapter_path;
8690

8791
Log *log;
8892
DAPTransport &transport;
89-
lldb::SBFile in;
93+
lldb_private::PseudoTerminal pty;
9094
OutputRedirector out;
9195
OutputRedirector err;
9296

@@ -99,6 +103,8 @@ struct DAP final : public DAPTransport::MessageHandler {
99103
/// The target instance for this DAP session.
100104
lldb::SBTarget target;
101105

106+
std::optional<EvaluateContext> context;
107+
102108
Variables variables;
103109
lldb::SBBroadcaster broadcaster;
104110
FunctionBreakpointMap function_breakpoints;
@@ -223,7 +229,8 @@ struct DAP final : public DAPTransport::MessageHandler {
223229
/// Send the given message to the client.
224230
protocol::Id Send(const protocol::Message &message);
225231

226-
void SendOutput(OutputType o, const llvm::StringRef output);
232+
void SendOutput(OutputType o, const llvm::StringRef output,
233+
int64_t varref = 0);
227234

228235
void SendProgressEvent(uint64_t progress_id, const char *message,
229236
uint64_t completed, uint64_t total);
@@ -409,6 +416,7 @@ struct DAP final : public DAPTransport::MessageHandler {
409416
lldb::SBMutex GetAPIMutex() const { return target.GetAPIMutex(); }
410417

411418
void StartEventThread();
419+
void StartIOThread();
412420
void StartProgressEventThread();
413421

414422
/// Sets the given protocol `breakpoints` in the given `source`, while
@@ -477,6 +485,8 @@ struct DAP final : public DAPTransport::MessageHandler {
477485
// Loop for managing reading from the client.
478486
lldb_private::MainLoop &m_loop;
479487

488+
lldb_private::MainLoop::ReadHandleUP handle;
489+
480490
std::mutex m_cancelled_requests_mutex;
481491
llvm::SmallSet<int64_t, 4> m_cancelled_requests;
482492

lldb/tools/lldb-dap/EventHelper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ void SendTerminatedEvent(DAP &dap) { dap.SendTerminatedEvent(); }
243243
// Grab any STDOUT and STDERR from the process and send it up to VS Code
244244
// via an "output" event to the "stdout" and "stderr" categories.
245245
void SendStdOutStdErr(DAP &dap, lldb::SBProcess &process) {
246-
char buffer[OutputBufferSize];
246+
char buffer[OutputBufferSize] = {0};
247247
size_t count;
248248
while ((count = process.GetSTDOUT(buffer, sizeof(buffer))) > 0)
249249
dap.SendOutput(OutputType::Stdout, llvm::StringRef(buffer, count));

lldb/tools/lldb-dap/Handler/CompletionsHandler.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "DAP.h"
10-
#include "JSONUtils.h"
1110
#include "Protocol/ProtocolRequests.h"
1211
#include "Protocol/ProtocolTypes.h"
1312
#include "RequestHandler.h"
13+
#include "lldb/API/SBCommandInterpreter.h"
14+
#include "lldb/API/SBDebugger.h"
1415
#include "lldb/API/SBStringList.h"
1516

1617
using namespace llvm;

lldb/tools/lldb-dap/Handler/EvaluateRequestHandler.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99
#include "DAP.h"
1010
#include "EventHelper.h"
1111
#include "JSONUtils.h"
12-
#include "LLDBUtils.h"
1312
#include "RequestHandler.h"
13+
#include "lldb/API/SBCommandInterpreter.h"
14+
#include "lldb/API/SBCommandReturnObject.h"
15+
#include "lldb/lldb-enumerations.h"
16+
#include "llvm/ADT/StringRef.h"
17+
#include <future>
18+
#include <optional>
19+
#include <unistd.h>
1420

1521
namespace lldb_dap {
1622

@@ -164,13 +170,36 @@ void EvaluateRequestHandler::operator()(
164170
dap.focus_tid = frame.GetThread().GetThreadID();
165171
}
166172

167-
bool required_command_failed = false;
168-
std::string result = RunLLDBCommands(
169-
dap.debugger, llvm::StringRef(), {expression}, required_command_failed,
170-
/*parse_command_directives=*/false, /*echo_commands=*/false);
173+
std::string result = "";
174+
int64_t varref = 0;
175+
int fd = dap.pty.GetPrimaryFileDescriptor();
176+
for (auto expr : llvm::split(expression, "\n")) {
177+
if (!dap.debugger.GetCommandInterpreter().IsActive()) {
178+
::write(fd, expr.data(), expr.size());
179+
std::string rcnl = "\r\n";
180+
::write(fd, rcnl.data(), rcnl.size());
181+
continue;
182+
}
183+
184+
dap.context = EvaluateContext{};
185+
std::future<void> done = dap.context->done.get_future();
186+
::write(fd, expr.data(), expr.size());
187+
std::string rcnl = "\r\n";
188+
::write(fd, rcnl.data(), rcnl.size());
189+
dap.GetAPIMutex().unlock();
190+
done.wait();
191+
dap.GetAPIMutex().lock();
192+
lldb::SBCommandReturnObject ret = dap.context->result;
193+
dap.context = std::nullopt;
194+
if (ret)
195+
result += ret.GetOutput();
196+
auto variables = ret.GetValues(lldb::eNoDynamicValues);
197+
if (variables)
198+
varref = dap.variables.InsertVariables(variables, true);
199+
}
171200

172201
EmplaceSafeString(body, "result", result);
173-
body.try_emplace("variablesReference", (int64_t)0);
202+
body.try_emplace("variablesReference", (int64_t)varref);
174203
} else {
175204
if (context == "repl") {
176205
// If the expression is empty and the last expression was for a

0 commit comments

Comments
 (0)