Although I cannot find any mention of this in the specification, both gdb and lldb agree on sending an initial + packet after establishing the connection. OTOH, gdbserver and lldb-server behavior is subtly different. While lldb-server *expects* the initial ack, and drops the connection if it is not received, gdbserver will just ignore a spurious ack at _any_ point in the connection. This patch changes lldb's behavior to match that of gdb. An ACK packet is ignored at any point in the connection (except when expecting an ACK packet, of course). This is inline with the "be strict in what you generate, and lenient in what you accept" philosophy, and also enables us to remove some special cases from the server code. I've extended the same handling to NAK (-) packets, mainly because I don't see a reason to treat them differently here. (The background here is that we had a stub which was sending spurious + packets. This bug has since been fixed, but I think this change makes sense nonetheless.) Differential Revision: https://reviews.llvm.org/D114520
170 lines
5.7 KiB
C++
170 lines
5.7 KiB
C++
//===-- GDBRemoteCommunicationServer.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 <cerrno>
|
|
|
|
#include "lldb/Host/Config.h"
|
|
|
|
#include "GDBRemoteCommunicationServer.h"
|
|
|
|
#include "ProcessGDBRemoteLog.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "lldb/Utility/StringExtractorGDBRemote.h"
|
|
#include "lldb/Utility/UnimplementedError.h"
|
|
#include "llvm/Support/JSON.h"
|
|
#include <cstring>
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
using namespace lldb_private::process_gdb_remote;
|
|
using namespace llvm;
|
|
|
|
GDBRemoteCommunicationServer::GDBRemoteCommunicationServer(
|
|
const char *comm_name, const char *listener_name)
|
|
: GDBRemoteCommunication(comm_name, listener_name), m_exit_now(false) {
|
|
RegisterPacketHandler(
|
|
StringExtractorGDBRemote::eServerPacketType_QEnableErrorStrings,
|
|
[this](StringExtractorGDBRemote packet, Status &error, bool &interrupt,
|
|
bool &quit) { return this->Handle_QErrorStringEnable(packet); });
|
|
}
|
|
|
|
GDBRemoteCommunicationServer::~GDBRemoteCommunicationServer() = default;
|
|
|
|
void GDBRemoteCommunicationServer::RegisterPacketHandler(
|
|
StringExtractorGDBRemote::ServerPacketType packet_type,
|
|
PacketHandler handler) {
|
|
m_packet_handlers[packet_type] = std::move(handler);
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::GetPacketAndSendResponse(
|
|
Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) {
|
|
StringExtractorGDBRemote packet;
|
|
|
|
PacketResult packet_result = ReadPacket(packet, timeout, false);
|
|
if (packet_result == PacketResult::Success) {
|
|
const StringExtractorGDBRemote::ServerPacketType packet_type =
|
|
packet.GetServerPacketType();
|
|
switch (packet_type) {
|
|
case StringExtractorGDBRemote::eServerPacketType_nack:
|
|
case StringExtractorGDBRemote::eServerPacketType_ack:
|
|
break;
|
|
|
|
case StringExtractorGDBRemote::eServerPacketType_invalid:
|
|
error.SetErrorString("invalid packet");
|
|
quit = true;
|
|
break;
|
|
|
|
case StringExtractorGDBRemote::eServerPacketType_unimplemented:
|
|
packet_result = SendUnimplementedResponse(packet.GetStringRef().data());
|
|
break;
|
|
|
|
default:
|
|
auto handler_it = m_packet_handlers.find(packet_type);
|
|
if (handler_it == m_packet_handlers.end())
|
|
packet_result = SendUnimplementedResponse(packet.GetStringRef().data());
|
|
else
|
|
packet_result = handler_it->second(packet, error, interrupt, quit);
|
|
break;
|
|
}
|
|
} else {
|
|
if (!IsConnected()) {
|
|
error.SetErrorString("lost connection");
|
|
quit = true;
|
|
} else {
|
|
error.SetErrorString("timeout");
|
|
}
|
|
}
|
|
|
|
// Check if anything occurred that would force us to want to exit.
|
|
if (m_exit_now)
|
|
quit = true;
|
|
|
|
return packet_result;
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendUnimplementedResponse(const char *) {
|
|
// TODO: Log the packet we aren't handling...
|
|
return SendPacketNoLock("");
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendErrorResponse(uint8_t err) {
|
|
char packet[16];
|
|
int packet_len = ::snprintf(packet, sizeof(packet), "E%2.2x", err);
|
|
assert(packet_len < (int)sizeof(packet));
|
|
return SendPacketNoLock(llvm::StringRef(packet, packet_len));
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendErrorResponse(const Status &error) {
|
|
if (m_send_error_strings) {
|
|
lldb_private::StreamString packet;
|
|
packet.Printf("E%2.2x;", static_cast<uint8_t>(error.GetError()));
|
|
packet.PutStringAsRawHex8(error.AsCString());
|
|
return SendPacketNoLock(packet.GetString());
|
|
} else
|
|
return SendErrorResponse(error.GetError());
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendErrorResponse(llvm::Error error) {
|
|
assert(error);
|
|
std::unique_ptr<llvm::ErrorInfoBase> EIB;
|
|
std::unique_ptr<UnimplementedError> UE;
|
|
llvm::handleAllErrors(
|
|
std::move(error),
|
|
[&](std::unique_ptr<UnimplementedError> E) { UE = std::move(E); },
|
|
[&](std::unique_ptr<llvm::ErrorInfoBase> E) { EIB = std::move(E); });
|
|
|
|
if (EIB)
|
|
return SendErrorResponse(Status(llvm::Error(std::move(EIB))));
|
|
return SendUnimplementedResponse("");
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::Handle_QErrorStringEnable(
|
|
StringExtractorGDBRemote &packet) {
|
|
m_send_error_strings = true;
|
|
return SendOKResponse();
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendIllFormedResponse(
|
|
const StringExtractorGDBRemote &failed_packet, const char *message) {
|
|
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
|
|
LLDB_LOGF(log, "GDBRemoteCommunicationServer::%s: ILLFORMED: '%s' (%s)",
|
|
__FUNCTION__, failed_packet.GetStringRef().data(),
|
|
message ? message : "");
|
|
return SendErrorResponse(0x03);
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendOKResponse() {
|
|
return SendPacketNoLock("OK");
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendJSONResponse(const json::Value &value) {
|
|
std::string json_string;
|
|
raw_string_ostream os(json_string);
|
|
os << value;
|
|
os.flush();
|
|
StreamGDBRemote escaped_response;
|
|
escaped_response.PutEscapedBytes(json_string.c_str(), json_string.size());
|
|
return SendPacketNoLock(escaped_response.GetString());
|
|
}
|
|
|
|
GDBRemoteCommunication::PacketResult
|
|
GDBRemoteCommunicationServer::SendJSONResponse(Expected<json::Value> value) {
|
|
if (!value)
|
|
return SendErrorResponse(value.takeError());
|
|
return SendJSONResponse(*value);
|
|
}
|