Skip to content

Commit 7790d69

Browse files
ashgtiJDevlieghere
andauthored
[lldb-dap] Refactoring IOStream into Transport handler. (llvm#130026)
Instead of having two discrete InputStream and OutputStream helpers, this merges the two into a unifed 'Transport' handler. This handler is responsible for reading the DAP message headers, parsing the resulting JSON and converting the messages into `lldb_dap::protocol::Message`s for both input and output. --------- Co-authored-by: Jonas Devlieghere <[email protected]>
1 parent 4d79e98 commit 7790d69

File tree

11 files changed

+288
-248
lines changed

11 files changed

+288
-248
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ def send_recv(self, command):
337337
self.send_packet(
338338
{
339339
"type": "response",
340-
"seq": -1,
340+
"seq": 0,
341341
"request_seq": response_or_request["seq"],
342342
"success": True,
343343
"command": "runInTerminal",
@@ -349,7 +349,7 @@ def send_recv(self, command):
349349
self.send_packet(
350350
{
351351
"type": "response",
352-
"seq": -1,
352+
"seq": 0,
353353
"request_seq": response_or_request["seq"],
354354
"success": True,
355355
"command": "startDebugging",

lldb/test/API/tools/lldb-dap/io/TestDAP_io.py

+26-15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Test lldb-dap IO handling.
33
"""
44

5+
import sys
6+
57
from lldbsuite.test.decorators import *
68
import lldbdap_testcase
79
import dap_server
@@ -19,18 +21,18 @@ def cleanup():
1921
if process.poll() is None:
2022
process.terminate()
2123
process.wait()
22-
stdout_data = process.stdout.read()
23-
stderr_data = process.stderr.read()
24-
print("========= STDOUT =========")
25-
print(stdout_data)
26-
print("========= END =========")
27-
print("========= STDERR =========")
28-
print(stderr_data)
29-
print("========= END =========")
30-
print("========= DEBUG ADAPTER PROTOCOL LOGS =========")
24+
stdout_data = process.stdout.read().decode()
25+
stderr_data = process.stderr.read().decode()
26+
print("========= STDOUT =========", file=sys.stderr)
27+
print(stdout_data, file=sys.stderr)
28+
print("========= END =========", file=sys.stderr)
29+
print("========= STDERR =========", file=sys.stderr)
30+
print(stderr_data, file=sys.stderr)
31+
print("========= END =========", file=sys.stderr)
32+
print("========= DEBUG ADAPTER PROTOCOL LOGS =========", file=sys.stderr)
3133
with open(log_file_path, "r") as file:
32-
print(file.read())
33-
print("========= END =========")
34+
print(file.read(), file=sys.stderr)
35+
print("========= END =========", file=sys.stderr)
3436

3537
# Execute the cleanup function during test case tear down.
3638
self.addTearDownHook(cleanup)
@@ -45,14 +47,23 @@ def test_eof_immediately(self):
4547
process.stdin.close()
4648
self.assertEqual(process.wait(timeout=5.0), 0)
4749

50+
def test_invalid_header(self):
51+
"""
52+
lldb-dap handles invalid message headers.
53+
"""
54+
process = self.launch()
55+
process.stdin.write(b"not the corret message header")
56+
process.stdin.close()
57+
self.assertEqual(process.wait(timeout=5.0), 1)
58+
4859
def test_partial_header(self):
4960
"""
5061
lldb-dap handles parital message headers.
5162
"""
5263
process = self.launch()
5364
process.stdin.write(b"Content-Length: ")
5465
process.stdin.close()
55-
self.assertEqual(process.wait(timeout=5.0), 0)
66+
self.assertEqual(process.wait(timeout=5.0), 1)
5667

5768
def test_incorrect_content_length(self):
5869
"""
@@ -61,13 +72,13 @@ def test_incorrect_content_length(self):
6172
process = self.launch()
6273
process.stdin.write(b"Content-Length: abc")
6374
process.stdin.close()
64-
self.assertEqual(process.wait(timeout=5.0), 0)
75+
self.assertEqual(process.wait(timeout=5.0), 1)
6576

6677
def test_partial_content_length(self):
6778
"""
6879
lldb-dap handles partial messages.
6980
"""
7081
process = self.launch()
71-
process.stdin.write(b"Content-Length: 10{")
82+
process.stdin.write(b"Content-Length: 10\r\n\r\n{")
7283
process.stdin.close()
73-
self.assertEqual(process.wait(timeout=5.0), 0)
84+
self.assertEqual(process.wait(timeout=5.0), 1)

lldb/tools/lldb-dap/CMakeLists.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ add_lldb_tool(lldb-dap
2828
FifoFiles.cpp
2929
FunctionBreakpoint.cpp
3030
InstructionBreakpoint.cpp
31-
IOStream.cpp
3231
JSONUtils.cpp
3332
LLDBUtils.cpp
3433
OutputRedirector.cpp
3534
ProgressEvent.cpp
35+
Protocol.cpp
3636
RunInTerminal.cpp
3737
SourceBreakpoint.cpp
38-
Protocol.cpp
38+
Transport.cpp
3939
Watchpoint.cpp
4040

4141
Handler/ResponseHandler.cpp

lldb/tools/lldb-dap/DAP.cpp

+31-85
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "JSONUtils.h"
1313
#include "LLDBUtils.h"
1414
#include "OutputRedirector.h"
15+
#include "Transport.h"
1516
#include "lldb/API/SBBreakpoint.h"
1617
#include "lldb/API/SBCommandInterpreter.h"
1718
#include "lldb/API/SBCommandReturnObject.h"
@@ -63,11 +64,10 @@ const char DEV_NULL[] = "/dev/null";
6364

6465
namespace lldb_dap {
6566

66-
DAP::DAP(llvm::StringRef client_name, llvm::StringRef path, std::ofstream *log,
67-
lldb::IOObjectSP input, lldb::IOObjectSP output, ReplMode repl_mode,
68-
std::vector<std::string> pre_init_commands)
69-
: client_name(client_name), debug_adapter_path(path), log(log),
70-
input(std::move(input)), output(std::move(output)),
67+
DAP::DAP(llvm::StringRef path, std::ofstream *log,
68+
const ReplMode default_repl_mode,
69+
std::vector<std::string> pre_init_commands, Transport &transport)
70+
: debug_adapter_path(path), log(log), transport(transport),
7171
broadcaster("lldb-dap"), exception_breakpoints(),
7272
pre_init_commands(std::move(pre_init_commands)),
7373
focus_tid(LLDB_INVALID_THREAD_ID), stop_at_entry(false), is_attach(false),
@@ -78,7 +78,7 @@ DAP::DAP(llvm::StringRef client_name, llvm::StringRef path, std::ofstream *log,
7878
configuration_done_sent(false), waiting_for_run_in_terminal(false),
7979
progress_event_reporter(
8080
[&](const ProgressEvent &event) { SendJSON(event.ToJSON()); }),
81-
reverse_request_seq(0), repl_mode(repl_mode) {}
81+
reverse_request_seq(0), repl_mode(default_repl_mode) {}
8282

8383
DAP::~DAP() = default;
8484

@@ -221,52 +221,21 @@ void DAP::StopEventHandlers() {
221221
}
222222
}
223223

224-
// Send the JSON in "json_str" to the "out" stream. Correctly send the
225-
// "Content-Length:" field followed by the length, followed by the raw
226-
// JSON bytes.
227-
void DAP::SendJSON(const std::string &json_str) {
228-
output.write_full("Content-Length: ");
229-
output.write_full(llvm::utostr(json_str.size()));
230-
output.write_full("\r\n\r\n");
231-
output.write_full(json_str);
232-
}
233-
234224
// Serialize the JSON value into a string and send the JSON packet to
235225
// the "out" stream.
236226
void DAP::SendJSON(const llvm::json::Value &json) {
237-
std::string json_str;
238-
llvm::raw_string_ostream strm(json_str);
239-
strm << json;
240-
static std::mutex mutex;
241-
std::lock_guard<std::mutex> locker(mutex);
242-
SendJSON(json_str);
243-
244-
DAP_LOG(log, "({0}) <-- {1}", client_name, json_str);
245-
}
246-
247-
// Read a JSON packet from the "in" stream.
248-
std::string DAP::ReadJSON() {
249-
std::string length_str;
250-
std::string json_str;
251-
int length;
252-
253-
if (!input.read_expected(log, "Content-Length: "))
254-
return json_str;
255-
256-
if (!input.read_line(log, length_str))
257-
return json_str;
258-
259-
if (!llvm::to_integer(length_str, length))
260-
return json_str;
261-
262-
if (!input.read_expected(log, "\r\n"))
263-
return json_str;
264-
265-
if (!input.read_full(log, length, json_str))
266-
return json_str;
267-
268-
DAP_LOG(log, "({0}) --> {1}", client_name, json_str);
269-
return json_str;
227+
// FIXME: Instead of parsing the output message from JSON, pass the `Message`
228+
// as parameter to `SendJSON`.
229+
protocol::Message message;
230+
llvm::json::Path::Root root;
231+
if (!fromJSON(json, message, root)) {
232+
DAP_LOG_ERROR(log, root.getError(), "({1}) encoding failed: {0}",
233+
transport.GetClientName());
234+
return;
235+
}
236+
if (llvm::Error err = transport.Write(message))
237+
DAP_LOG_ERROR(log, std::move(err), "({1}) write failed: {0}",
238+
transport.GetClientName());
270239
}
271240

272241
// "OutputEvent": {
@@ -693,29 +662,10 @@ void DAP::SetTarget(const lldb::SBTarget target) {
693662
}
694663
}
695664

696-
PacketStatus DAP::GetNextObject(llvm::json::Object &object) {
697-
std::string json = ReadJSON();
698-
if (json.empty())
699-
return PacketStatus::EndOfFile;
700-
701-
llvm::StringRef json_sref(json);
702-
llvm::Expected<llvm::json::Value> json_value = llvm::json::parse(json_sref);
703-
if (!json_value) {
704-
DAP_LOG_ERROR(log, json_value.takeError(),
705-
"({1}) failed to parse JSON: {0}", client_name);
706-
return PacketStatus::JSONMalformed;
707-
}
708-
709-
llvm::json::Object *object_ptr = json_value->getAsObject();
710-
if (!object_ptr) {
711-
DAP_LOG(log, "({0}) error: json packet isn't a object", client_name);
712-
return PacketStatus::JSONNotObject;
713-
}
714-
object = *object_ptr;
715-
return PacketStatus::Success;
716-
}
717-
718-
bool DAP::HandleObject(const llvm::json::Object &object) {
665+
bool DAP::HandleObject(const protocol::Message &M) {
666+
// FIXME: Directly handle `Message` instead of serializing to JSON.
667+
llvm::json::Value v = toJSON(M);
668+
llvm::json::Object object = *v.getAsObject();
719669
const auto packet_type = GetString(object, "type");
720670
if (packet_type == "request") {
721671
const auto command = GetString(object, "command");
@@ -726,7 +676,8 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
726676
return true; // Success
727677
}
728678

729-
DAP_LOG(log, "({0}) error: unhandled command '{1}'", client_name, command);
679+
DAP_LOG(log, "({0}) error: unhandled command '{1}'",
680+
transport.GetClientName(), command);
730681
return false; // Fail
731682
}
732683

@@ -749,9 +700,8 @@ bool DAP::HandleObject(const llvm::json::Object &object) {
749700
// Result should be given, use null if not.
750701
if (GetBoolean(object, "success").value_or(false)) {
751702
llvm::json::Value Result = nullptr;
752-
if (auto *B = object.get("body")) {
703+
if (auto *B = object.get("body"))
753704
Result = std::move(*B);
754-
}
755705
(*response_handler)(Result);
756706
} else {
757707
llvm::StringRef message = GetString(object, "message");
@@ -818,19 +768,15 @@ llvm::Error DAP::Loop() {
818768
StopEventHandlers();
819769
});
820770
while (!disconnecting) {
821-
llvm::json::Object object;
822-
lldb_dap::PacketStatus status = GetNextObject(object);
771+
llvm::Expected<std::optional<protocol::Message>> next = transport.Read();
772+
if (!next)
773+
return next.takeError();
823774

824-
if (status == lldb_dap::PacketStatus::EndOfFile) {
775+
// nullopt on EOF
776+
if (!*next)
825777
break;
826-
}
827-
828-
if (status != lldb_dap::PacketStatus::Success) {
829-
return llvm::createStringError(llvm::inconvertibleErrorCode(),
830-
"failed to send packet");
831-
}
832778

833-
if (!HandleObject(object)) {
779+
if (!HandleObject(**next)) {
834780
return llvm::createStringError(llvm::inconvertibleErrorCode(),
835781
"unhandled packet");
836782
}

lldb/tools/lldb-dap/DAP.h

+25-18
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
#include "FunctionBreakpoint.h"
1515
#include "Handler/RequestHandler.h"
1616
#include "Handler/ResponseHandler.h"
17-
#include "IOStream.h"
1817
#include "InstructionBreakpoint.h"
1918
#include "OutputRedirector.h"
2019
#include "ProgressEvent.h"
20+
#include "Protocol.h"
2121
#include "SourceBreakpoint.h"
22+
#include "Transport.h"
2223
#include "lldb/API/SBBroadcaster.h"
2324
#include "lldb/API/SBCommandInterpreter.h"
2425
#include "lldb/API/SBDebugger.h"
@@ -39,7 +40,6 @@
3940
#include "llvm/Support/Error.h"
4041
#include "llvm/Support/JSON.h"
4142
#include "llvm/Support/Threading.h"
42-
#include <map>
4343
#include <memory>
4444
#include <mutex>
4545
#include <optional>
@@ -145,11 +145,9 @@ struct SendEventRequestHandler : public lldb::SBCommandPluginInterface {
145145
};
146146

147147
struct DAP {
148-
llvm::StringRef client_name;
149148
llvm::StringRef debug_adapter_path;
150149
std::ofstream *log;
151-
InputStream input;
152-
OutputStream output;
150+
Transport &transport;
153151
lldb::SBFile in;
154152
OutputRedirector out;
155153
OutputRedirector err;
@@ -210,12 +208,30 @@ struct DAP {
210208
// will contain that expression.
211209
std::string last_nonempty_var_expression;
212210

213-
DAP(llvm::StringRef client_name, llvm::StringRef path, std::ofstream *log,
214-
lldb::IOObjectSP input, lldb::IOObjectSP output, ReplMode repl_mode,
215-
std::vector<std::string> pre_init_commands);
211+
/// Creates a new DAP sessions.
212+
///
213+
/// \param[in] path
214+
/// Path to the lldb-dap binary.
215+
/// \param[in] log
216+
/// Log file stream, if configured.
217+
/// \param[in] default_repl_mode
218+
/// Default repl mode behavior, as configured by the binary.
219+
/// \param[in] pre_init_commands
220+
/// LLDB commands to execute as soon as the debugger instance is allocaed.
221+
/// \param[in] transport
222+
/// Transport for this debug session.
223+
DAP(llvm::StringRef path, std::ofstream *log,
224+
const ReplMode default_repl_mode,
225+
std::vector<std::string> pre_init_commands, Transport &transport);
226+
216227
~DAP();
228+
229+
/// DAP is not copyable.
230+
/// @{
217231
DAP(const DAP &rhs) = delete;
218232
void operator=(const DAP &rhs) = delete;
233+
/// @}
234+
219235
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
220236
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
221237

@@ -233,8 +249,6 @@ struct DAP {
233249
// the "out" stream.
234250
void SendJSON(const llvm::json::Value &json);
235251

236-
std::string ReadJSON();
237-
238252
void SendOutput(OutputType o, const llvm::StringRef output);
239253

240254
void SendProgressEvent(uint64_t progress_id, const char *message,
@@ -307,8 +321,7 @@ struct DAP {
307321
/// listeing for its breakpoint events.
308322
void SetTarget(const lldb::SBTarget target);
309323

310-
PacketStatus GetNextObject(llvm::json::Object &object);
311-
bool HandleObject(const llvm::json::Object &object);
324+
bool HandleObject(const protocol::Message &M);
312325

313326
/// Disconnect the DAP session.
314327
lldb::SBError Disconnect();
@@ -382,12 +395,6 @@ struct DAP {
382395
InstructionBreakpoint *GetInstructionBreakpoint(const lldb::break_id_t bp_id);
383396

384397
InstructionBreakpoint *GetInstructionBPFromStopReason(lldb::SBThread &thread);
385-
386-
private:
387-
// Send the JSON in "json_str" to the "out" stream. Correctly send the
388-
// "Content-Length:" field followed by the length, followed by the raw
389-
// JSON bytes.
390-
void SendJSON(const std::string &json_str);
391398
};
392399

393400
} // namespace lldb_dap

0 commit comments

Comments
 (0)