Skip to content

Commit fb8c0e2

Browse files
committed
[lldb] Implement JSON RPC (newline delimited) Transport (llvm#143946)
This PR implements JSON RPC-style (i.e. newline delimited) JSON transport. I moved the existing transport tests from DAP to Host and moved the PipeTest base class into TestingSupport so it can be shared by both. (cherry picked from commit 8a2895a)
1 parent 2646dce commit fb8c0e2

File tree

6 files changed

+386
-8
lines changed

6 files changed

+386
-8
lines changed

lldb/include/lldb/Host/JSONTransport.h

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,17 @@ class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
5151
}
5252
};
5353

54-
class TransportClosedError : public llvm::ErrorInfo<TransportClosedError> {
54+
class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
5555
public:
5656
static char ID;
5757

58-
TransportClosedError() = default;
58+
TransportInvalidError() = default;
5959

6060
void log(llvm::raw_ostream &OS) const override {
61-
OS << "transport is closed";
61+
OS << "transport IO object invalid";
6262
}
6363
std::error_code convertToErrorCode() const override {
64-
return llvm::inconvertibleErrorCode();
64+
return std::make_error_code(std::errc::not_connected);
6565
}
6666
};
6767

@@ -121,6 +121,21 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
121121
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n";
122122
};
123123

124+
/// A transport class for JSON RPC.
125+
class JSONRPCTransport : public JSONTransport {
126+
public:
127+
JSONRPCTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
128+
: JSONTransport(input, output) {}
129+
virtual ~JSONRPCTransport() = default;
130+
131+
protected:
132+
virtual llvm::Error WriteImpl(const std::string &message) override;
133+
virtual llvm::Expected<std::string>
134+
ReadImpl(const std::chrono::microseconds &timeout) override;
135+
136+
static constexpr llvm::StringLiteral kMessageSeparator = "\n";
137+
};
138+
124139
} // namespace lldb_private
125140

126141
#endif

lldb/source/Host/common/JSONTransport.cpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ static Expected<std::string>
3131
ReadFull(IOObject &descriptor, size_t length,
3232
std::optional<std::chrono::microseconds> timeout = std::nullopt) {
3333
if (!descriptor.IsValid())
34-
return llvm::make_error<TransportClosedError>();
34+
return llvm::make_error<TransportInvalidError>();
3535

3636
bool timeout_supported = true;
3737
// FIXME: SelectHelper does not work with NativeFile on Win32.
@@ -92,7 +92,7 @@ void JSONTransport::Log(llvm::StringRef message) {
9292
Expected<std::string>
9393
HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) {
9494
if (!m_input || !m_input->IsValid())
95-
return createStringError("transport output is closed");
95+
return llvm::make_error<TransportInvalidError>();
9696

9797
IOObject *input = m_input.get();
9898
Expected<std::string> message_header =
@@ -131,7 +131,7 @@ HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) {
131131

132132
Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) {
133133
if (!m_output || !m_output->IsValid())
134-
return llvm::make_error<TransportClosedError>();
134+
return llvm::make_error<TransportInvalidError>();
135135

136136
Log(llvm::formatv("<-- {0}", message).str());
137137

@@ -142,6 +142,35 @@ Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) {
142142
return m_output->Write(Output.data(), num_bytes).takeError();
143143
}
144144

145+
Expected<std::string>
146+
JSONRPCTransport::ReadImpl(const std::chrono::microseconds &timeout) {
147+
if (!m_input || !m_input->IsValid())
148+
return make_error<TransportInvalidError>();
149+
150+
IOObject *input = m_input.get();
151+
Expected<std::string> raw_json =
152+
ReadUntil(*input, kMessageSeparator, timeout);
153+
if (!raw_json)
154+
return raw_json.takeError();
155+
156+
Log(llvm::formatv("--> {0}", *raw_json).str());
157+
158+
return *raw_json;
159+
}
160+
161+
Error JSONRPCTransport::WriteImpl(const std::string &message) {
162+
if (!m_output || !m_output->IsValid())
163+
return llvm::make_error<TransportInvalidError>();
164+
165+
Log(llvm::formatv("<-- {0}", message).str());
166+
167+
std::string Output;
168+
llvm::raw_string_ostream OS(Output);
169+
OS << message << kMessageSeparator;
170+
size_t num_bytes = Output.size();
171+
return m_output->Write(Output.data(), num_bytes).takeError();
172+
}
173+
145174
char TransportEOFError::ID;
146175
char TransportTimeoutError::ID;
147-
char TransportClosedError::ID;
176+
char TransportInvalidError::ID;

lldb/unittests/DAP/TestBase.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===-- TestBase.cpp ------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "TestBase.h"
10+
#include "Protocol/ProtocolBase.h"
11+
#include "TestingSupport/TestUtilities.h"
12+
#include "lldb/API/SBDefines.h"
13+
#include "lldb/API/SBStructuredData.h"
14+
#include "lldb/Host/File.h"
15+
#include "lldb/Host/Pipe.h"
16+
#include "lldb/lldb-forward.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/Testing/Support/Error.h"
19+
#include "gtest/gtest.h"
20+
#include <memory>
21+
22+
using namespace llvm;
23+
using namespace lldb;
24+
using namespace lldb_dap;
25+
using namespace lldb_dap::protocol;
26+
using namespace lldb_dap_tests;
27+
using lldb_private::File;
28+
using lldb_private::NativeFile;
29+
using lldb_private::Pipe;
30+
31+
void TransportBase::SetUp() {
32+
PipeTest::SetUp();
33+
to_dap = std::make_unique<Transport>(
34+
"to_dap", nullptr,
35+
std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
36+
File::eOpenOptionReadOnly,
37+
NativeFile::Unowned),
38+
std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
39+
File::eOpenOptionWriteOnly,
40+
NativeFile::Unowned));
41+
from_dap = std::make_unique<Transport>(
42+
"from_dap", nullptr,
43+
std::make_shared<NativeFile>(output.GetReadFileDescriptor(),
44+
File::eOpenOptionReadOnly,
45+
NativeFile::Unowned),
46+
std::make_shared<NativeFile>(input.GetWriteFileDescriptor(),
47+
File::eOpenOptionWriteOnly,
48+
NativeFile::Unowned));
49+
}
50+
51+
void DAPTestBase::SetUp() {
52+
TransportBase::SetUp();
53+
dap = std::make_unique<DAP>(
54+
/*log=*/nullptr,
55+
/*default_repl_mode=*/ReplMode::Auto,
56+
/*pre_init_commands=*/std::vector<std::string>(),
57+
/*transport=*/*to_dap);
58+
}
59+
60+
void DAPTestBase::TearDown() {
61+
if (core)
62+
ASSERT_THAT_ERROR(core->discard(), Succeeded());
63+
if (binary)
64+
ASSERT_THAT_ERROR(binary->discard(), Succeeded());
65+
}
66+
67+
void DAPTestBase::SetUpTestSuite() {
68+
lldb::SBError error = SBDebugger::InitializeWithErrorHandling();
69+
EXPECT_TRUE(error.Success());
70+
}
71+
void DAPTestBase::TeatUpTestSuite() { SBDebugger::Terminate(); }
72+
73+
bool DAPTestBase::GetDebuggerSupportsTarget(llvm::StringRef platform) {
74+
EXPECT_TRUE(dap->debugger);
75+
76+
lldb::SBStructuredData data = dap->debugger.GetBuildConfiguration()
77+
.GetValueForKey("targets")
78+
.GetValueForKey("value");
79+
for (size_t i = 0; i < data.GetSize(); i++) {
80+
char buf[100] = {0};
81+
size_t size = data.GetItemAtIndex(i).GetStringValue(buf, sizeof(buf));
82+
if (llvm::StringRef(buf, size) == platform)
83+
return true;
84+
}
85+
86+
return false;
87+
}
88+
89+
void DAPTestBase::CreateDebugger() {
90+
dap->debugger = lldb::SBDebugger::Create();
91+
ASSERT_TRUE(dap->debugger);
92+
}
93+
94+
void DAPTestBase::LoadCore() {
95+
ASSERT_TRUE(dap->debugger);
96+
llvm::Expected<lldb_private::TestFile> binary_yaml =
97+
lldb_private::TestFile::fromYamlFile(k_linux_binary);
98+
ASSERT_THAT_EXPECTED(binary_yaml, Succeeded());
99+
llvm::Expected<llvm::sys::fs::TempFile> binary_file =
100+
binary_yaml->writeToTemporaryFile();
101+
ASSERT_THAT_EXPECTED(binary_file, Succeeded());
102+
binary = std::move(*binary_file);
103+
dap->target = dap->debugger.CreateTarget(binary->TmpName.data());
104+
ASSERT_TRUE(dap->target);
105+
llvm::Expected<lldb_private::TestFile> core_yaml =
106+
lldb_private::TestFile::fromYamlFile(k_linux_core);
107+
ASSERT_THAT_EXPECTED(core_yaml, Succeeded());
108+
llvm::Expected<llvm::sys::fs::TempFile> core_file =
109+
core_yaml->writeToTemporaryFile();
110+
ASSERT_THAT_EXPECTED(core_file, Succeeded());
111+
this->core = std::move(*core_file);
112+
SBProcess process = dap->target.LoadCore(this->core->TmpName.data());
113+
ASSERT_TRUE(process);
114+
}
115+
116+
std::vector<Message> DAPTestBase::DrainOutput() {
117+
std::vector<Message> msgs;
118+
output.CloseWriteFileDescriptor();
119+
while (true) {
120+
Expected<Message> next =
121+
from_dap->Read<protocol::Message>(std::chrono::milliseconds(1));
122+
if (!next) {
123+
consumeError(next.takeError());
124+
break;
125+
}
126+
msgs.push_back(*next);
127+
}
128+
return msgs;
129+
}

lldb/unittests/Host/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set (FILES
1313
HostInfoTest.cpp
1414
HostTest.cpp
1515
MainLoopTest.cpp
16+
JSONTransportTest.cpp
1617
NativeProcessProtocolTest.cpp
1718
PipeTest.cpp
1819
ProcessLaunchInfoTest.cpp

0 commit comments

Comments
 (0)