-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb] Move Transport class into lldb_private (NFC) #143806
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
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
//===-- JSONTransport.h ---------------------------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// Transport layer for encoding and decoding JSON protocol messages. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef LLDB_HOST_JSONTRANSPORT_H | ||
#define LLDB_HOST_JSONTRANSPORT_H | ||
|
||
#include "lldb/lldb-forward.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Support/Error.h" | ||
#include "llvm/Support/FormatVariadic.h" | ||
#include "llvm/Support/JSON.h" | ||
#include <chrono> | ||
#include <system_error> | ||
|
||
namespace lldb_private { | ||
|
||
class TransportEOFError : public llvm::ErrorInfo<TransportEOFError> { | ||
public: | ||
static char ID; | ||
|
||
TransportEOFError() = default; | ||
|
||
void log(llvm::raw_ostream &OS) const override { | ||
OS << "transport end of file reached"; | ||
} | ||
std::error_code convertToErrorCode() const override { | ||
return llvm::inconvertibleErrorCode(); | ||
} | ||
}; | ||
|
||
class TransportTimeoutError : public llvm::ErrorInfo<TransportTimeoutError> { | ||
public: | ||
static char ID; | ||
|
||
TransportTimeoutError() = default; | ||
|
||
void log(llvm::raw_ostream &OS) const override { | ||
OS << "transport operation timed out"; | ||
} | ||
std::error_code convertToErrorCode() const override { | ||
return std::make_error_code(std::errc::timed_out); | ||
} | ||
}; | ||
|
||
class TransportClosedError : public llvm::ErrorInfo<TransportClosedError> { | ||
public: | ||
static char ID; | ||
|
||
TransportClosedError() = default; | ||
|
||
void log(llvm::raw_ostream &OS) const override { | ||
OS << "transport is closed"; | ||
} | ||
std::error_code convertToErrorCode() const override { | ||
return llvm::inconvertibleErrorCode(); | ||
} | ||
}; | ||
|
||
/// A transport class that uses JSON for communication. | ||
class JSONTransport { | ||
public: | ||
JSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output); | ||
virtual ~JSONTransport() = default; | ||
|
||
/// Transport is not copyable. | ||
/// @{ | ||
JSONTransport(const JSONTransport &rhs) = delete; | ||
void operator=(const JSONTransport &rhs) = delete; | ||
/// @} | ||
|
||
/// Writes a message to the output stream. | ||
template <typename T> llvm::Error Write(const T &t) { | ||
const std::string message = llvm::formatv("{0}", toJSON(t)).str(); | ||
return WriteImpl(message); | ||
} | ||
|
||
/// Reads the next message from the input stream. | ||
template <typename T> | ||
llvm::Expected<T> Read(const std::chrono::microseconds &timeout) { | ||
llvm::Expected<std::string> message = ReadImpl(timeout); | ||
if (!message) | ||
return message.takeError(); | ||
return llvm::json::parse<T>(/*JSON=*/*message); | ||
} | ||
|
||
protected: | ||
virtual void Log(llvm::StringRef message); | ||
|
||
virtual llvm::Error WriteImpl(const std::string &message) = 0; | ||
virtual llvm::Expected<std::string> | ||
ReadImpl(const std::chrono::microseconds &timeout) = 0; | ||
|
||
lldb::IOObjectSP m_input; | ||
lldb::IOObjectSP m_output; | ||
}; | ||
|
||
/// A transport class for JSON with a HTTP header. | ||
class HTTPDelimitedJSONTransport : public JSONTransport { | ||
public: | ||
HTTPDelimitedJSONTransport(lldb::IOObjectSP input, lldb::IOObjectSP output) | ||
: JSONTransport(input, output) {} | ||
virtual ~HTTPDelimitedJSONTransport() = default; | ||
|
||
protected: | ||
virtual llvm::Error WriteImpl(const std::string &message) override; | ||
virtual llvm::Expected<std::string> | ||
ReadImpl(const std::chrono::microseconds &timeout) override; | ||
|
||
// FIXME: Support any header. | ||
static constexpr llvm::StringLiteral kHeaderContentLength = | ||
"Content-Length: "; | ||
static constexpr llvm::StringLiteral kHeaderSeparator = "\r\n\r\n"; | ||
}; | ||
|
||
} // namespace lldb_private | ||
|
||
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
//===-- JSONTransport.cpp -------------------------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "lldb/Host/JSONTransport.h" | ||
#include "lldb/Utility/IOObject.h" | ||
#include "lldb/Utility/LLDBLog.h" | ||
#include "lldb/Utility/Log.h" | ||
#include "lldb/Utility/SelectHelper.h" | ||
#include "lldb/Utility/Status.h" | ||
#include "lldb/lldb-forward.h" | ||
#include "llvm/ADT/StringExtras.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Support/Error.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
#include <optional> | ||
#include <string> | ||
#include <utility> | ||
|
||
using namespace llvm; | ||
using namespace lldb; | ||
using namespace lldb_private; | ||
|
||
/// ReadFull attempts to read the specified number of bytes. If EOF is | ||
/// encountered, an empty string is returned. | ||
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>(); | ||
|
||
bool timeout_supported = true; | ||
// FIXME: SelectHelper does not work with NativeFile on Win32. | ||
#if _WIN32 | ||
timeout_supported = descriptor.GetFdType() == IOObject::eFDTypeSocket; | ||
#endif | ||
|
||
if (timeout && timeout_supported) { | ||
SelectHelper sh; | ||
sh.SetTimeout(*timeout); | ||
sh.FDSetRead(descriptor.GetWaitableHandle()); | ||
Status status = sh.Select(); | ||
if (status.Fail()) { | ||
// Convert timeouts into a specific error. | ||
if (status.GetType() == lldb::eErrorTypePOSIX && | ||
status.GetError() == ETIMEDOUT) | ||
return make_error<TransportTimeoutError>(); | ||
return status.takeError(); | ||
} | ||
} | ||
|
||
std::string data; | ||
data.resize(length); | ||
Status status = descriptor.Read(data.data(), length); | ||
if (status.Fail()) | ||
return status.takeError(); | ||
|
||
// Read returns '' on EOF. | ||
if (length == 0) | ||
return make_error<TransportEOFError>(); | ||
|
||
// Return the actual number of bytes read. | ||
return data.substr(0, length); | ||
} | ||
|
||
static Expected<std::string> | ||
ReadUntil(IOObject &descriptor, StringRef delimiter, | ||
std::optional<std::chrono::microseconds> timeout = std::nullopt) { | ||
std::string buffer; | ||
buffer.reserve(delimiter.size() + 1); | ||
while (!llvm::StringRef(buffer).ends_with(delimiter)) { | ||
Expected<std::string> next = | ||
ReadFull(descriptor, buffer.empty() ? delimiter.size() : 1, timeout); | ||
if (auto Err = next.takeError()) | ||
return std::move(Err); | ||
buffer += *next; | ||
} | ||
return buffer.substr(0, buffer.size() - delimiter.size()); | ||
} | ||
|
||
JSONTransport::JSONTransport(IOObjectSP input, IOObjectSP output) | ||
: m_input(std::move(input)), m_output(std::move(output)) {} | ||
|
||
void JSONTransport::Log(llvm::StringRef message) { | ||
LLDB_LOG(GetLog(LLDBLog::Host), "{0}", message); | ||
} | ||
|
||
Expected<std::string> | ||
HTTPDelimitedJSONTransport::ReadImpl(const std::chrono::microseconds &timeout) { | ||
if (!m_input || !m_input->IsValid()) | ||
return createStringError("transport output is closed"); | ||
|
||
IOObject *input = m_input.get(); | ||
Expected<std::string> message_header = | ||
ReadFull(*input, kHeaderContentLength.size(), timeout); | ||
if (!message_header) | ||
return message_header.takeError(); | ||
if (*message_header != kHeaderContentLength) | ||
return createStringError(formatv("expected '{0}' and got '{1}'", | ||
kHeaderContentLength, *message_header) | ||
.str()); | ||
|
||
Expected<std::string> raw_length = ReadUntil(*input, kHeaderSeparator); | ||
if (!raw_length) | ||
return handleErrors(raw_length.takeError(), | ||
[&](const TransportEOFError &E) -> llvm::Error { | ||
return createStringError( | ||
"unexpected EOF while reading header separator"); | ||
}); | ||
|
||
size_t length; | ||
if (!to_integer(*raw_length, length)) | ||
return createStringError( | ||
formatv("invalid content length {0}", *raw_length).str()); | ||
|
||
Expected<std::string> raw_json = ReadFull(*input, length); | ||
if (!raw_json) | ||
return handleErrors( | ||
raw_json.takeError(), [&](const TransportEOFError &E) -> llvm::Error { | ||
return createStringError("unexpected EOF while reading JSON"); | ||
}); | ||
|
||
Log(llvm::formatv("--> {0}", *raw_json).str()); | ||
|
||
return raw_json; | ||
} | ||
|
||
Error HTTPDelimitedJSONTransport::WriteImpl(const std::string &message) { | ||
if (!m_output || !m_output->IsValid()) | ||
return llvm::make_error<TransportClosedError>(); | ||
|
||
Log(llvm::formatv("<-- {0}", message).str()); | ||
|
||
std::string Output; | ||
raw_string_ostream OS(Output); | ||
OS << kHeaderContentLength << message.length() << kHeaderSeparator << message; | ||
size_t num_bytes = Output.size(); | ||
return m_output->Write(Output.data(), num_bytes).takeError(); | ||
} | ||
|
||
char TransportEOFError::ID; | ||
char TransportTimeoutError::ID; | ||
char TransportClosedError::ID; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.