Skip to content

Commit b723887

Browse files
authored
[lldb] Update JSONTransport to use MainLoop for reading. (#148300)
This updates JSONTransport to use a MainLoop for reading messages. This also allows us to read in larger chunks than we did previously. With the event driven reading operations we can read in chunks and store the contents in an internal buffer. Separately we can parse the buffer and split the contents up into messages. Our previous version approach would read a byte at a time, which is less efficient.
1 parent 47ef3d0 commit b723887

File tree

11 files changed

+535
-347
lines changed

11 files changed

+535
-347
lines changed

lldb/include/lldb/Host/JSONTransport.h

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
#ifndef LLDB_HOST_JSONTRANSPORT_H
1414
#define LLDB_HOST_JSONTRANSPORT_H
1515

16+
#include "lldb/Host/MainLoopBase.h"
1617
#include "lldb/lldb-forward.h"
18+
#include "llvm/ADT/FunctionExtras.h"
1719
#include "llvm/ADT/StringRef.h"
1820
#include "llvm/Support/Error.h"
1921
#include "llvm/Support/FormatVariadic.h"
2022
#include "llvm/Support/JSON.h"
21-
#include <chrono>
23+
#include <string>
2224
#include <system_error>
25+
#include <vector>
2326

2427
namespace lldb_private {
2528

@@ -28,27 +31,33 @@ class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> {
2831
static char ID;
2932

3033
TransportEOFError() = default;
31-
32-
void log(llvm::raw_ostream &OS) const override {
33-
OS << "transport end of file reached";
34-
}
34+
void log(llvm::raw_ostream &OS) const override { OS << "transport EOF"; }
3535
std::error_code convertToErrorCode() const override {
36-
return llvm::inconvertibleErrorCode();
36+
return std::make_error_code(std::errc::io_error);
3737
}
3838
};
3939

40-
class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> {
40+
class TransportUnhandledContentsError
41+
: public llvm::ErrorInfo<TransportUnhandledContentsError> {
4142
public:
4243
static char ID;
4344

44-
TransportTimeoutError() = default;
45+
explicit TransportUnhandledContentsError(std::string unhandled_contents)
46+
: m_unhandled_contents(unhandled_contents) {}
4547

4648
void log(llvm::raw_ostream &OS) const override {
47-
OS << "transport operation timed out";
49+
OS << "transport EOF with unhandled contents " << m_unhandled_contents;
4850
}
4951
std::error_code convertToErrorCode() const override {
50-
return std::make_error_code(std::errc::timed_out);
52+
return std::make_error_code(std::errc::bad_message);
5153
}
54+
55+
const std::string &getUnhandledContents() const {
56+
return m_unhandled_contents;
57+
}
58+
59+
private:
60+
std::string m_unhandled_contents;
5261
};
5362

5463
class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
@@ -68,6 +77,11 @@ class TransportInvalidError : public llvm::ErrorInfo<TransportInvalidError> {
6877
/// A transport class that uses JSON for communication.
6978
class JSONTransport {
7079
public:
80+
using ReadHandleUP = MainLoopBase::ReadHandleUP;
81+
template <typename T>
82+
using Callback =
83+
llvm::unique_function<void(MainLoopBase &, const llvm::Expected<T>)>;
84+
7185
JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output);
7286
virtual ~JSONTransport() = default;
7387

@@ -83,24 +97,69 @@ class JSONTransport {
8397
return WriteImpl(message);
8498
}
8599

86-
/// Reads the next message from the input stream.
100+
/// Registers the transport with the MainLoop.
87101
template <typename T>
88-
llvm::Expected<T> Read(const std::chrono::microseconds &timeout) {
89-
llvm::Expected<std::string> message = ReadImpl(timeout);
90-
if (!message)
91-
return message.takeError();
92-
return llvm::json::parse<T>(/*JSON=*/*message);
102+
llvm::Expected<ReadHandleUP> RegisterReadObject(MainLoopBase &loop,
103+
Callback<T> callback) {
104+
Status error;
105+
ReadHandleUP handle = loop.RegisterReadObject(
106+
m_input,
107+
[&](MainLoopBase &loop) {
108+
char buffer[kReadBufferSize];
109+
size_t len = sizeof(buffer);
110+
if (llvm::Error error = m_input->Read(buffer, len).takeError()) {
111+
callback(loop, std::move(error));
112+
return;
113+
}
114+
115+
if (len)
116+
m_buffer.append(std::string(buffer, len));
117+
118+
// If the buffer has contents, try parsing any pending messages.
119+
if (!m_buffer.empty()) {
120+
llvm::Expected<std::vector<std::string>> messages = Parse();
121+
if (llvm::Error error = messages.takeError()) {
122+
callback(loop, std::move(error));
123+
return;
124+
}
125+
126+
for (const auto &message : *messages)
127+
if constexpr (std::is_same<T, std::string>::value)
128+
callback(loop, message);
129+
else
130+
callback(loop, llvm::json::parse<T>(message));
131+
}
132+
133+
// On EOF, notify the callback after the remaining messages were
134+
// handled.
135+
if (len == 0) {
136+
if (m_buffer.empty())
137+
callback(loop, llvm::make_error<TransportEOFError>());
138+
else
139+
callback(loop, llvm::make_error<TransportUnhandledContentsError>(
140+
m_buffer));
141+
}
142+
},
143+
error);
144+
if (error.Fail())
145+
return error.takeError();
146+
return handle;
93147
}
94148

95149
protected:
150+
template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) {
151+
Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str());
152+
}
96153
virtual void Log(llvm::StringRef message);
97154

98155
virtual llvm::Error WriteImpl(const std::string &message) = 0;
99-
virtual llvm::Expected<std::string>
100-
ReadImpl(const std::chrono::microseconds &timeout) = 0;
156+
virtual llvm::Expected<std::vector<std::string>> Parse() = 0;
101157

102158
lldb::IOObjectSP m_input;
103159
lldb::IOObjectSP m_output;
160+
std::string m_buffer;
161+
162+
static constexpr size_t kReadBufferSize = 1024;
104163
};
105164

106165
/// A transport class for JSON with a HTTP header.
@@ -111,14 +170,13 @@ class HTTPDelimitedJSONTransport : public JSONTransport {
111170
virtual ~HTTPDelimitedJSONTransport() = default;
112171

113172
protected:
114-
virtual llvm::Error WriteImpl(const std::string &message) override;
115-
virtual llvm::Expected<std::string>
116-
ReadImpl(const std::chrono::microseconds &timeout) override;
117-
118-
// FIXME: Support any header.
119-
static constexpr llvm::StringLiteral kHeaderContentLength =
120-
"Content-Length: ";
121-
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n";
173+
llvm::Error WriteImpl(const std::string &message) override;
174+
llvm::Expected<std::vector<std::string>> Parse() override;
175+
176+
static constexpr llvm::StringLiteral kHeaderContentLength = "Content-Length";
177+
static constexpr llvm::StringLiteral kHeaderFieldSeparator = ":";
178+
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n";
179+
static constexpr llvm::StringLiteral kEndOfHeader = "\r\n\r\n";
122180
};
123181

124182
/// A transport class for JSON RPC.
@@ -129,9 +187,8 @@ class JSONRPCTransport : public JSONTransport {
129187
virtual ~JSONRPCTransport() = default;
130188

131189
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;
190+
llvm::Error WriteImpl(const std::string &message) override;
191+
llvm::Expected<std::vector<std::string>> Parse() override;
135192

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

0 commit comments

Comments
 (0)