diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp')
-rw-r--r-- | contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp b/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp new file mode 100644 index 000000000000..9f79d2271b1e --- /dev/null +++ b/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -0,0 +1,682 @@ +//===-- StringExtractorGDBRemote.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/Utility/StringExtractorGDBRemote.h" + +#include <cctype> +#include <cstring> +#include <optional> + +constexpr lldb::pid_t StringExtractorGDBRemote::AllProcesses; +constexpr lldb::tid_t StringExtractorGDBRemote::AllThreads; + +StringExtractorGDBRemote::ResponseType +StringExtractorGDBRemote::GetResponseType() const { + if (m_packet.empty()) + return eUnsupported; + + switch (m_packet[0]) { + case 'E': + if (isxdigit(m_packet[1]) && isxdigit(m_packet[2])) { + if (m_packet.size() == 3) + return eError; + llvm::StringRef packet_ref(m_packet); + if (packet_ref[3] == ';') { + auto err_string = packet_ref.substr(4); + for (auto e : err_string) + if (!isxdigit(e)) + return eResponse; + return eError; + } + } + break; + + case 'O': + if (m_packet.size() == 2 && m_packet[1] == 'K') + return eOK; + break; + + case '+': + if (m_packet.size() == 1) + return eAck; + break; + + case '-': + if (m_packet.size() == 1) + return eNack; + break; + } + return eResponse; +} + +StringExtractorGDBRemote::ServerPacketType +StringExtractorGDBRemote::GetServerPacketType() const { +#define PACKET_MATCHES(s) \ + ((packet_size == (sizeof(s) - 1)) && (strcmp((packet_cstr), (s)) == 0)) +#define PACKET_STARTS_WITH(s) \ + ((packet_size >= (sizeof(s) - 1)) && \ + ::strncmp(packet_cstr, s, (sizeof(s) - 1)) == 0) + + // Empty is not a supported packet... + if (m_packet.empty()) + return eServerPacketType_invalid; + + const size_t packet_size = m_packet.size(); + const char *packet_cstr = m_packet.c_str(); + switch (m_packet[0]) { + + case '%': + return eServerPacketType_notify; + + case '\x03': + if (packet_size == 1) + return eServerPacketType_interrupt; + break; + + case '-': + if (packet_size == 1) + return eServerPacketType_nack; + break; + + case '+': + if (packet_size == 1) + return eServerPacketType_ack; + break; + + case 'A': + return eServerPacketType_A; + + case 'Q': + + switch (packet_cstr[1]) { + case 'E': + if (PACKET_STARTS_WITH("QEnvironment:")) + return eServerPacketType_QEnvironment; + if (PACKET_STARTS_WITH("QEnvironmentHexEncoded:")) + return eServerPacketType_QEnvironmentHexEncoded; + if (PACKET_STARTS_WITH("QEnableErrorStrings")) + return eServerPacketType_QEnableErrorStrings; + break; + + case 'P': + if (PACKET_STARTS_WITH("QPassSignals:")) + return eServerPacketType_QPassSignals; + break; + + case 'S': + if (PACKET_MATCHES("QStartNoAckMode")) + return eServerPacketType_QStartNoAckMode; + if (PACKET_STARTS_WITH("QSaveRegisterState")) + return eServerPacketType_QSaveRegisterState; + if (PACKET_STARTS_WITH("QSetDisableASLR:")) + return eServerPacketType_QSetDisableASLR; + if (PACKET_STARTS_WITH("QSetDetachOnError:")) + return eServerPacketType_QSetDetachOnError; + if (PACKET_STARTS_WITH("QSetSTDIN:")) + return eServerPacketType_QSetSTDIN; + if (PACKET_STARTS_WITH("QSetSTDOUT:")) + return eServerPacketType_QSetSTDOUT; + if (PACKET_STARTS_WITH("QSetSTDERR:")) + return eServerPacketType_QSetSTDERR; + if (PACKET_STARTS_WITH("QSetWorkingDir:")) + return eServerPacketType_QSetWorkingDir; + if (PACKET_STARTS_WITH("QSetLogging:")) + return eServerPacketType_QSetLogging; + if (PACKET_STARTS_WITH("QSetIgnoredExceptions")) + return eServerPacketType_QSetIgnoredExceptions; + if (PACKET_STARTS_WITH("QSetMaxPacketSize:")) + return eServerPacketType_QSetMaxPacketSize; + if (PACKET_STARTS_WITH("QSetMaxPayloadSize:")) + return eServerPacketType_QSetMaxPayloadSize; + if (PACKET_STARTS_WITH("QSetEnableAsyncProfiling;")) + return eServerPacketType_QSetEnableAsyncProfiling; + if (PACKET_STARTS_WITH("QSyncThreadState:")) + return eServerPacketType_QSyncThreadState; + break; + + case 'L': + if (PACKET_STARTS_WITH("QLaunchArch:")) + return eServerPacketType_QLaunchArch; + if (PACKET_MATCHES("QListThreadsInStopReply")) + return eServerPacketType_QListThreadsInStopReply; + break; + + case 'M': + if (PACKET_STARTS_WITH("QMemTags")) + return eServerPacketType_QMemTags; + break; + + case 'N': + if (PACKET_STARTS_WITH("QNonStop:")) + return eServerPacketType_QNonStop; + break; + + case 'R': + if (PACKET_STARTS_WITH("QRestoreRegisterState:")) + return eServerPacketType_QRestoreRegisterState; + break; + + case 'T': + if (PACKET_MATCHES("QThreadSuffixSupported")) + return eServerPacketType_QThreadSuffixSupported; + break; + } + break; + + case 'q': + switch (packet_cstr[1]) { + case 's': + if (PACKET_MATCHES("qsProcessInfo")) + return eServerPacketType_qsProcessInfo; + if (PACKET_MATCHES("qsThreadInfo")) + return eServerPacketType_qsThreadInfo; + break; + + case 'f': + if (PACKET_STARTS_WITH("qfProcessInfo")) + return eServerPacketType_qfProcessInfo; + if (PACKET_STARTS_WITH("qfThreadInfo")) + return eServerPacketType_qfThreadInfo; + break; + + case 'C': + if (packet_size == 2) + return eServerPacketType_qC; + break; + + case 'E': + if (PACKET_STARTS_WITH("qEcho:")) + return eServerPacketType_qEcho; + break; + + case 'F': + if (PACKET_STARTS_WITH("qFileLoadAddress:")) + return eServerPacketType_qFileLoadAddress; + break; + + case 'G': + if (PACKET_STARTS_WITH("qGroupName:")) + return eServerPacketType_qGroupName; + if (PACKET_MATCHES("qGetWorkingDir")) + return eServerPacketType_qGetWorkingDir; + if (PACKET_MATCHES("qGetPid")) + return eServerPacketType_qGetPid; + if (PACKET_STARTS_WITH("qGetProfileData;")) + return eServerPacketType_qGetProfileData; + if (PACKET_MATCHES("qGDBServerVersion")) + return eServerPacketType_qGDBServerVersion; + break; + + case 'H': + if (PACKET_MATCHES("qHostInfo")) + return eServerPacketType_qHostInfo; + break; + + case 'K': + if (PACKET_STARTS_WITH("qKillSpawnedProcess")) + return eServerPacketType_qKillSpawnedProcess; + break; + + case 'L': + if (PACKET_STARTS_WITH("qLaunchGDBServer")) + return eServerPacketType_qLaunchGDBServer; + if (PACKET_MATCHES("qLaunchSuccess")) + return eServerPacketType_qLaunchSuccess; + break; + + case 'M': + if (PACKET_STARTS_WITH("qMemoryRegionInfo:")) + return eServerPacketType_qMemoryRegionInfo; + if (PACKET_MATCHES("qMemoryRegionInfo")) + return eServerPacketType_qMemoryRegionInfoSupported; + if (PACKET_STARTS_WITH("qModuleInfo:")) + return eServerPacketType_qModuleInfo; + if (PACKET_STARTS_WITH("qMemTags:")) + return eServerPacketType_qMemTags; + break; + + case 'P': + if (PACKET_STARTS_WITH("qProcessInfoPID:")) + return eServerPacketType_qProcessInfoPID; + if (PACKET_STARTS_WITH("qPlatform_shell:")) + return eServerPacketType_qPlatform_shell; + if (PACKET_STARTS_WITH("qPlatform_mkdir:")) + return eServerPacketType_qPlatform_mkdir; + if (PACKET_STARTS_WITH("qPlatform_chmod:")) + return eServerPacketType_qPlatform_chmod; + if (PACKET_MATCHES("qProcessInfo")) + return eServerPacketType_qProcessInfo; + if (PACKET_STARTS_WITH("qPathComplete:")) + return eServerPacketType_qPathComplete; + break; + + case 'Q': + if (PACKET_MATCHES("qQueryGDBServer")) + return eServerPacketType_qQueryGDBServer; + break; + + case 'R': + if (PACKET_STARTS_WITH("qRcmd,")) + return eServerPacketType_qRcmd; + if (PACKET_STARTS_WITH("qRegisterInfo")) + return eServerPacketType_qRegisterInfo; + break; + + case 'S': + if (PACKET_STARTS_WITH("qSaveCore")) + return eServerPacketType_qLLDBSaveCore; + if (PACKET_STARTS_WITH("qSpeedTest:")) + return eServerPacketType_qSpeedTest; + if (PACKET_MATCHES("qShlibInfoAddr")) + return eServerPacketType_qShlibInfoAddr; + if (PACKET_MATCHES("qStepPacketSupported")) + return eServerPacketType_qStepPacketSupported; + if (PACKET_STARTS_WITH("qSupported")) + return eServerPacketType_qSupported; + if (PACKET_MATCHES("qSyncThreadStateSupported")) + return eServerPacketType_qSyncThreadStateSupported; + break; + + case 'T': + if (PACKET_STARTS_WITH("qThreadExtraInfo,")) + return eServerPacketType_qThreadExtraInfo; + if (PACKET_STARTS_WITH("qThreadStopInfo")) + return eServerPacketType_qThreadStopInfo; + break; + + case 'U': + if (PACKET_STARTS_WITH("qUserName:")) + return eServerPacketType_qUserName; + break; + + case 'V': + if (PACKET_MATCHES("qVAttachOrWaitSupported")) + return eServerPacketType_qVAttachOrWaitSupported; + break; + + case 'W': + if (PACKET_STARTS_WITH("qWatchpointSupportInfo:")) + return eServerPacketType_qWatchpointSupportInfo; + if (PACKET_MATCHES("qWatchpointSupportInfo")) + return eServerPacketType_qWatchpointSupportInfoSupported; + break; + + case 'X': + if (PACKET_STARTS_WITH("qXfer:")) + return eServerPacketType_qXfer; + break; + } + break; + + case 'j': + if (PACKET_STARTS_WITH("jModulesInfo:")) + return eServerPacketType_jModulesInfo; + if (PACKET_MATCHES("jSignalsInfo")) + return eServerPacketType_jSignalsInfo; + if (PACKET_MATCHES("jThreadsInfo")) + return eServerPacketType_jThreadsInfo; + + if (PACKET_MATCHES("jLLDBTraceSupported")) + return eServerPacketType_jLLDBTraceSupported; + if (PACKET_STARTS_WITH("jLLDBTraceStop:")) + return eServerPacketType_jLLDBTraceStop; + if (PACKET_STARTS_WITH("jLLDBTraceStart:")) + return eServerPacketType_jLLDBTraceStart; + if (PACKET_STARTS_WITH("jLLDBTraceGetState:")) + return eServerPacketType_jLLDBTraceGetState; + if (PACKET_STARTS_WITH("jLLDBTraceGetBinaryData:")) + return eServerPacketType_jLLDBTraceGetBinaryData; + break; + + case 'v': + if (PACKET_STARTS_WITH("vFile:")) { + if (PACKET_STARTS_WITH("vFile:open:")) + return eServerPacketType_vFile_open; + else if (PACKET_STARTS_WITH("vFile:close:")) + return eServerPacketType_vFile_close; + else if (PACKET_STARTS_WITH("vFile:pread")) + return eServerPacketType_vFile_pread; + else if (PACKET_STARTS_WITH("vFile:pwrite")) + return eServerPacketType_vFile_pwrite; + else if (PACKET_STARTS_WITH("vFile:size")) + return eServerPacketType_vFile_size; + else if (PACKET_STARTS_WITH("vFile:exists")) + return eServerPacketType_vFile_exists; + else if (PACKET_STARTS_WITH("vFile:fstat")) + return eServerPacketType_vFile_fstat; + else if (PACKET_STARTS_WITH("vFile:stat")) + return eServerPacketType_vFile_stat; + else if (PACKET_STARTS_WITH("vFile:mode")) + return eServerPacketType_vFile_mode; + else if (PACKET_STARTS_WITH("vFile:MD5")) + return eServerPacketType_vFile_md5; + else if (PACKET_STARTS_WITH("vFile:symlink")) + return eServerPacketType_vFile_symlink; + else if (PACKET_STARTS_WITH("vFile:unlink")) + return eServerPacketType_vFile_unlink; + + } else { + if (PACKET_STARTS_WITH("vAttach;")) + return eServerPacketType_vAttach; + if (PACKET_STARTS_WITH("vAttachWait;")) + return eServerPacketType_vAttachWait; + if (PACKET_STARTS_WITH("vAttachOrWait;")) + return eServerPacketType_vAttachOrWait; + if (PACKET_STARTS_WITH("vAttachName;")) + return eServerPacketType_vAttachName; + if (PACKET_STARTS_WITH("vCont;")) + return eServerPacketType_vCont; + if (PACKET_MATCHES("vCont?")) + return eServerPacketType_vCont_actions; + if (PACKET_STARTS_WITH("vKill;")) + return eServerPacketType_vKill; + if (PACKET_STARTS_WITH("vRun;")) + return eServerPacketType_vRun; + if (PACKET_MATCHES("vStopped")) + return eServerPacketType_vStopped; + if (PACKET_MATCHES("vCtrlC")) + return eServerPacketType_vCtrlC; + if (PACKET_MATCHES("vStdio")) + return eServerPacketType_vStdio; + break; + + } + break; + case '_': + switch (packet_cstr[1]) { + case 'M': + return eServerPacketType__M; + + case 'm': + return eServerPacketType__m; + } + break; + + case '?': + if (packet_size == 1) + return eServerPacketType_stop_reason; + break; + + case 'c': + return eServerPacketType_c; + + case 'C': + return eServerPacketType_C; + + case 'D': + return eServerPacketType_D; + + case 'g': + return eServerPacketType_g; + + case 'G': + return eServerPacketType_G; + + case 'H': + return eServerPacketType_H; + + case 'I': + return eServerPacketType_I; + + case 'k': + if (packet_size == 1) + return eServerPacketType_k; + break; + + case 'm': + return eServerPacketType_m; + + case 'M': + return eServerPacketType_M; + + case 'p': + return eServerPacketType_p; + + case 'P': + return eServerPacketType_P; + + case 's': + if (packet_size == 1) + return eServerPacketType_s; + break; + + case 'S': + return eServerPacketType_S; + + case 'x': + return eServerPacketType_x; + + case 'X': + return eServerPacketType_X; + + case 'T': + return eServerPacketType_T; + + case 'z': + if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4') + return eServerPacketType_z; + break; + + case 'Z': + if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4') + return eServerPacketType_Z; + break; + } + return eServerPacketType_unimplemented; +} + +bool StringExtractorGDBRemote::IsOKResponse() const { + return GetResponseType() == eOK; +} + +bool StringExtractorGDBRemote::IsUnsupportedResponse() const { + return GetResponseType() == eUnsupported; +} + +bool StringExtractorGDBRemote::IsNormalResponse() const { + return GetResponseType() == eResponse; +} + +bool StringExtractorGDBRemote::IsErrorResponse() const { + return GetResponseType() == eError && isxdigit(m_packet[1]) && + isxdigit(m_packet[2]); +} + +uint8_t StringExtractorGDBRemote::GetError() { + if (GetResponseType() == eError) { + SetFilePos(1); + return GetHexU8(255); + } + return 0; +} + +lldb_private::Status StringExtractorGDBRemote::GetStatus() { + lldb_private::Status error; + if (GetResponseType() == eError) { + SetFilePos(1); + uint8_t errc = GetHexU8(255); + error.SetError(errc, lldb::eErrorTypeGeneric); + + error.SetErrorStringWithFormat("Error %u", errc); + std::string error_messg; + if (GetChar() == ';') { + GetHexByteString(error_messg); + error.SetErrorString(error_messg); + } + } + return error; +} + +size_t StringExtractorGDBRemote::GetEscapedBinaryData(std::string &str) { + // Just get the data bytes in the string as + // GDBRemoteCommunication::CheckForPacket() already removes any 0x7d escaped + // characters. If any 0x7d characters are left in the packet, then they are + // supposed to be there... + str.clear(); + const size_t bytes_left = GetBytesLeft(); + if (bytes_left > 0) { + str.assign(m_packet, m_index, bytes_left); + m_index += bytes_left; + } + return str.size(); +} + +static bool +OKErrorNotSupportedResponseValidator(void *, + const StringExtractorGDBRemote &response) { + switch (response.GetResponseType()) { + case StringExtractorGDBRemote::eOK: + case StringExtractorGDBRemote::eError: + case StringExtractorGDBRemote::eUnsupported: + return true; + + case StringExtractorGDBRemote::eAck: + case StringExtractorGDBRemote::eNack: + case StringExtractorGDBRemote::eResponse: + break; + } + return false; +} + +static bool JSONResponseValidator(void *, + const StringExtractorGDBRemote &response) { + switch (response.GetResponseType()) { + case StringExtractorGDBRemote::eUnsupported: + case StringExtractorGDBRemote::eError: + return true; // Accept unsupported or EXX as valid responses + + case StringExtractorGDBRemote::eOK: + case StringExtractorGDBRemote::eAck: + case StringExtractorGDBRemote::eNack: + break; + + case StringExtractorGDBRemote::eResponse: + // JSON that is returned in from JSON query packets is currently always + // either a dictionary which starts with a '{', or an array which starts + // with a '['. This is a quick validator to just make sure the response + // could be valid JSON without having to validate all of the + // JSON content. + switch (response.GetStringRef()[0]) { + case '{': + return true; + case '[': + return true; + default: + break; + } + break; + } + return false; +} + +static bool +ASCIIHexBytesResponseValidator(void *, + const StringExtractorGDBRemote &response) { + switch (response.GetResponseType()) { + case StringExtractorGDBRemote::eUnsupported: + case StringExtractorGDBRemote::eError: + return true; // Accept unsupported or EXX as valid responses + + case StringExtractorGDBRemote::eOK: + case StringExtractorGDBRemote::eAck: + case StringExtractorGDBRemote::eNack: + break; + + case StringExtractorGDBRemote::eResponse: { + uint32_t valid_count = 0; + for (const char ch : response.GetStringRef()) { + if (!isxdigit(ch)) { + return false; + } + if (++valid_count >= 16) + break; // Don't validate all the characters in case the packet is very + // large + } + return true; + } break; + } + return false; +} + +void StringExtractorGDBRemote::CopyResponseValidator( + const StringExtractorGDBRemote &rhs) { + m_validator = rhs.m_validator; + m_validator_baton = rhs.m_validator_baton; +} + +void StringExtractorGDBRemote::SetResponseValidator( + ResponseValidatorCallback callback, void *baton) { + m_validator = callback; + m_validator_baton = baton; +} + +void StringExtractorGDBRemote::SetResponseValidatorToOKErrorNotSupported() { + m_validator = OKErrorNotSupportedResponseValidator; + m_validator_baton = nullptr; +} + +void StringExtractorGDBRemote::SetResponseValidatorToASCIIHexBytes() { + m_validator = ASCIIHexBytesResponseValidator; + m_validator_baton = nullptr; +} + +void StringExtractorGDBRemote::SetResponseValidatorToJSON() { + m_validator = JSONResponseValidator; + m_validator_baton = nullptr; +} + +bool StringExtractorGDBRemote::ValidateResponse() const { + // If we have a validator callback, try to validate the callback + if (m_validator) + return m_validator(m_validator_baton, *this); + else + return true; // No validator, so response is valid +} + +std::optional<std::pair<lldb::pid_t, lldb::tid_t>> +StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) { + llvm::StringRef view = llvm::StringRef(m_packet).substr(m_index); + size_t initial_length = view.size(); + lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + lldb::tid_t tid; + + if (view.consume_front("p")) { + // process identifier + if (view.consume_front("-1")) { + // -1 is a special case + pid = AllProcesses; + } else if (view.consumeInteger(16, pid) || pid == 0) { + // not a valid hex integer OR unsupported pid 0 + m_index = UINT64_MAX; + return std::nullopt; + } + + // "." must follow if we expect TID too; otherwise, we assume -1 + if (!view.consume_front(".")) { + // update m_index + m_index += initial_length - view.size(); + + return {{pid, AllThreads}}; + } + } + + // thread identifier + if (view.consume_front("-1")) { + // -1 is a special case + tid = AllThreads; + } else if (view.consumeInteger(16, tid) || tid == 0 || pid == AllProcesses) { + // not a valid hex integer OR tid 0 OR pid -1 + a specific tid + m_index = UINT64_MAX; + return std::nullopt; + } + + // update m_index + m_index += initial_length - view.size(); + + return {{pid != LLDB_INVALID_PROCESS_ID ? pid : default_pid, tid}}; +} |