Skip to content

[lldb] Implement JSON RPC (newline delimited) Transport #143946

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 3 commits into from
Jun 12, 2025
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
23 changes: 19 additions & 4 deletions lldb/include/lldb/Host/JSONTransport.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
}
};

class TransportClosedError : public llvm::ErrorInfo<TransportClosedError> {
class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
public:
static char ID;

TransportClosedError() = default;
TransportInvalidError() = default;

void log(llvm::raw_ostream &OS) const override {
OS << "transport is closed";
OS << "transport IO object invalid";
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
return std::make_error_code(std::errc::not_connected);
}
};

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

/// A transport class for JSON RPC.
class JSONRPCTransport : public JSONTransport {
public:
JSONRPCTransport(lldb::IOObjectSP input, lldb::IOObjectSP output)
: JSONTransport(input, output) {}
virtual ~JSONRPCTransport() = default;

protected:
virtual llvm::Error WriteImpl(const std::string &message) override;
virtual llvm::Expected<std::string>
ReadImpl(const std::chrono::microseconds &timeout) override;

static constexpr llvm::StringLiteral kMessageSeparator = "\n";
};

} // namespace lldb_private

#endif
37 changes: 33 additions & 4 deletions lldb/source/Host/common/JSONTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ static Expected<std::string>
ReadFull(IOObject &descriptor, size_t length,
std::optional<std::chrono::microseconds> timeout = std::nullopt) {
if (!descriptor.IsValid())
return llvm::make_error<TransportClosedError>();
return llvm::make_error<TransportInvalidError>();

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

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

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

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

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

Expected<std::string>
JSONRPCTransport::ReadImpl(const std::chrono::microseconds &timeout) {
if (!m_input || !m_input->IsValid())
return make_error<TransportInvalidError>();

IOObject *input = m_input.get();
Expected<std::string> raw_json =
ReadUntil(*input, kMessageSeparator, timeout);
if (!raw_json)
return raw_json.takeError();

Log(llvm::formatv("--> {0}", *raw_json).str());

return *raw_json;
}

Error JSONRPCTransport::WriteImpl(const std::string &message) {
if (!m_output || !m_output->IsValid())
return llvm::make_error<TransportInvalidError>();

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

std::string Output;
llvm::raw_string_ostream OS(Output);
OS << message << kMessageSeparator;
size_t num_bytes = Output.size();
return m_output->Write(Output.data(), num_bytes).takeError();
}

char TransportEOFError::ID;
char TransportTimeoutError::ID;
char TransportClosedError::ID;
char TransportInvalidError::ID;
1 change: 0 additions & 1 deletion lldb/unittests/DAP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ add_lldb_unittest(DAPTests
LLDBUtilsTest.cpp
ProtocolTypesTest.cpp
TestBase.cpp
TransportTest.cpp
VariablesTest.cpp

LINK_COMPONENTS
Expand Down
7 changes: 1 addition & 6 deletions lldb/unittests/DAP/TestBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,8 @@ using lldb_private::File;
using lldb_private::NativeFile;
using lldb_private::Pipe;

void PipeBase::SetUp() {
ASSERT_THAT_ERROR(input.CreateNew(false).ToError(), Succeeded());
ASSERT_THAT_ERROR(output.CreateNew(false).ToError(), Succeeded());
}

void TransportBase::SetUp() {
PipeBase::SetUp();
PipeTest::SetUp();
to_dap = std::make_unique<Transport>(
"to_dap", nullptr,
std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
Expand Down
13 changes: 2 additions & 11 deletions lldb/unittests/DAP/TestBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,17 @@

#include "DAP.h"
#include "Protocol/ProtocolBase.h"
#include "TestingSupport/Host/PipeTestUtilities.h"
#include "Transport.h"
#include "lldb/Host/Pipe.h"
#include "llvm/ADT/StringRef.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace lldb_dap_tests {

/// A base class for tests that need a pair of pipes for communication.
class PipeBase : public testing::Test {
protected:
lldb_private::Pipe input;
lldb_private::Pipe output;

void SetUp() override;
};

/// A base class for tests that need transport configured for communicating DAP
/// messages.
class TransportBase : public PipeBase {
class TransportBase : public PipeTest {
protected:
std::unique_ptr<lldb_dap::Transport> to_dap;
std::unique_ptr<lldb_dap::Transport> from_dap;
Expand Down
98 changes: 0 additions & 98 deletions lldb/unittests/DAP/TransportTest.cpp

This file was deleted.

1 change: 1 addition & 0 deletions lldb/unittests/Host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set (FILES
HostInfoTest.cpp
HostTest.cpp
MainLoopTest.cpp
JSONTransportTest.cpp
NativeProcessProtocolTest.cpp
PipeTest.cpp
ProcessLaunchInfoTest.cpp
Expand Down
Loading
Loading