Path: blob/main/contrib/llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp
39587 views
//===-- StringExtractorGDBRemote.cpp --------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Utility/StringExtractorGDBRemote.h"910#include <cctype>11#include <cstring>12#include <optional>1314constexpr lldb::pid_t StringExtractorGDBRemote::AllProcesses;15constexpr lldb::tid_t StringExtractorGDBRemote::AllThreads;1617StringExtractorGDBRemote::ResponseType18StringExtractorGDBRemote::GetResponseType() const {19if (m_packet.empty())20return eUnsupported;2122switch (m_packet[0]) {23case 'E':24if (isxdigit(m_packet[1]) && isxdigit(m_packet[2])) {25if (m_packet.size() == 3)26return eError;27llvm::StringRef packet_ref(m_packet);28if (packet_ref[3] == ';') {29auto err_string = packet_ref.substr(4);30for (auto e : err_string)31if (!isxdigit(e))32return eResponse;33return eError;34}35}36break;3738case 'O':39if (m_packet.size() == 2 && m_packet[1] == 'K')40return eOK;41break;4243case '+':44if (m_packet.size() == 1)45return eAck;46break;4748case '-':49if (m_packet.size() == 1)50return eNack;51break;52}53return eResponse;54}5556StringExtractorGDBRemote::ServerPacketType57StringExtractorGDBRemote::GetServerPacketType() const {58#define PACKET_MATCHES(s) \59((packet_size == (sizeof(s) - 1)) && (strcmp((packet_cstr), (s)) == 0))60#define PACKET_STARTS_WITH(s) \61((packet_size >= (sizeof(s) - 1)) && \62::strncmp(packet_cstr, s, (sizeof(s) - 1)) == 0)6364// Empty is not a supported packet...65if (m_packet.empty())66return eServerPacketType_invalid;6768const size_t packet_size = m_packet.size();69const char *packet_cstr = m_packet.c_str();70switch (m_packet[0]) {7172case '%':73return eServerPacketType_notify;7475case '\x03':76if (packet_size == 1)77return eServerPacketType_interrupt;78break;7980case '-':81if (packet_size == 1)82return eServerPacketType_nack;83break;8485case '+':86if (packet_size == 1)87return eServerPacketType_ack;88break;8990case 'A':91return eServerPacketType_A;9293case 'Q':9495switch (packet_cstr[1]) {96case 'E':97if (PACKET_STARTS_WITH("QEnvironment:"))98return eServerPacketType_QEnvironment;99if (PACKET_STARTS_WITH("QEnvironmentHexEncoded:"))100return eServerPacketType_QEnvironmentHexEncoded;101if (PACKET_STARTS_WITH("QEnableErrorStrings"))102return eServerPacketType_QEnableErrorStrings;103break;104105case 'P':106if (PACKET_STARTS_WITH("QPassSignals:"))107return eServerPacketType_QPassSignals;108break;109110case 'S':111if (PACKET_MATCHES("QStartNoAckMode"))112return eServerPacketType_QStartNoAckMode;113if (PACKET_STARTS_WITH("QSaveRegisterState"))114return eServerPacketType_QSaveRegisterState;115if (PACKET_STARTS_WITH("QSetDisableASLR:"))116return eServerPacketType_QSetDisableASLR;117if (PACKET_STARTS_WITH("QSetDetachOnError:"))118return eServerPacketType_QSetDetachOnError;119if (PACKET_STARTS_WITH("QSetSTDIN:"))120return eServerPacketType_QSetSTDIN;121if (PACKET_STARTS_WITH("QSetSTDOUT:"))122return eServerPacketType_QSetSTDOUT;123if (PACKET_STARTS_WITH("QSetSTDERR:"))124return eServerPacketType_QSetSTDERR;125if (PACKET_STARTS_WITH("QSetWorkingDir:"))126return eServerPacketType_QSetWorkingDir;127if (PACKET_STARTS_WITH("QSetLogging:"))128return eServerPacketType_QSetLogging;129if (PACKET_STARTS_WITH("QSetIgnoredExceptions"))130return eServerPacketType_QSetIgnoredExceptions;131if (PACKET_STARTS_WITH("QSetMaxPacketSize:"))132return eServerPacketType_QSetMaxPacketSize;133if (PACKET_STARTS_WITH("QSetMaxPayloadSize:"))134return eServerPacketType_QSetMaxPayloadSize;135if (PACKET_STARTS_WITH("QSetEnableAsyncProfiling;"))136return eServerPacketType_QSetEnableAsyncProfiling;137if (PACKET_STARTS_WITH("QSyncThreadState:"))138return eServerPacketType_QSyncThreadState;139break;140141case 'L':142if (PACKET_STARTS_WITH("QLaunchArch:"))143return eServerPacketType_QLaunchArch;144if (PACKET_MATCHES("QListThreadsInStopReply"))145return eServerPacketType_QListThreadsInStopReply;146break;147148case 'M':149if (PACKET_STARTS_WITH("QMemTags"))150return eServerPacketType_QMemTags;151break;152153case 'N':154if (PACKET_STARTS_WITH("QNonStop:"))155return eServerPacketType_QNonStop;156break;157158case 'R':159if (PACKET_STARTS_WITH("QRestoreRegisterState:"))160return eServerPacketType_QRestoreRegisterState;161break;162163case 'T':164if (PACKET_MATCHES("QThreadSuffixSupported"))165return eServerPacketType_QThreadSuffixSupported;166break;167}168break;169170case 'q':171switch (packet_cstr[1]) {172case 's':173if (PACKET_MATCHES("qsProcessInfo"))174return eServerPacketType_qsProcessInfo;175if (PACKET_MATCHES("qsThreadInfo"))176return eServerPacketType_qsThreadInfo;177break;178179case 'f':180if (PACKET_STARTS_WITH("qfProcessInfo"))181return eServerPacketType_qfProcessInfo;182if (PACKET_STARTS_WITH("qfThreadInfo"))183return eServerPacketType_qfThreadInfo;184break;185186case 'C':187if (packet_size == 2)188return eServerPacketType_qC;189break;190191case 'E':192if (PACKET_STARTS_WITH("qEcho:"))193return eServerPacketType_qEcho;194break;195196case 'F':197if (PACKET_STARTS_WITH("qFileLoadAddress:"))198return eServerPacketType_qFileLoadAddress;199break;200201case 'G':202if (PACKET_STARTS_WITH("qGroupName:"))203return eServerPacketType_qGroupName;204if (PACKET_MATCHES("qGetWorkingDir"))205return eServerPacketType_qGetWorkingDir;206if (PACKET_MATCHES("qGetPid"))207return eServerPacketType_qGetPid;208if (PACKET_STARTS_WITH("qGetProfileData;"))209return eServerPacketType_qGetProfileData;210if (PACKET_MATCHES("qGDBServerVersion"))211return eServerPacketType_qGDBServerVersion;212break;213214case 'H':215if (PACKET_MATCHES("qHostInfo"))216return eServerPacketType_qHostInfo;217break;218219case 'K':220if (PACKET_STARTS_WITH("qKillSpawnedProcess"))221return eServerPacketType_qKillSpawnedProcess;222break;223224case 'L':225if (PACKET_STARTS_WITH("qLaunchGDBServer"))226return eServerPacketType_qLaunchGDBServer;227if (PACKET_MATCHES("qLaunchSuccess"))228return eServerPacketType_qLaunchSuccess;229break;230231case 'M':232if (PACKET_STARTS_WITH("qMemoryRegionInfo:"))233return eServerPacketType_qMemoryRegionInfo;234if (PACKET_MATCHES("qMemoryRegionInfo"))235return eServerPacketType_qMemoryRegionInfoSupported;236if (PACKET_STARTS_WITH("qModuleInfo:"))237return eServerPacketType_qModuleInfo;238if (PACKET_STARTS_WITH("qMemTags:"))239return eServerPacketType_qMemTags;240break;241242case 'P':243if (PACKET_STARTS_WITH("qProcessInfoPID:"))244return eServerPacketType_qProcessInfoPID;245if (PACKET_STARTS_WITH("qPlatform_shell:"))246return eServerPacketType_qPlatform_shell;247if (PACKET_STARTS_WITH("qPlatform_mkdir:"))248return eServerPacketType_qPlatform_mkdir;249if (PACKET_STARTS_WITH("qPlatform_chmod:"))250return eServerPacketType_qPlatform_chmod;251if (PACKET_MATCHES("qProcessInfo"))252return eServerPacketType_qProcessInfo;253if (PACKET_STARTS_WITH("qPathComplete:"))254return eServerPacketType_qPathComplete;255break;256257case 'Q':258if (PACKET_MATCHES("qQueryGDBServer"))259return eServerPacketType_qQueryGDBServer;260break;261262case 'R':263if (PACKET_STARTS_WITH("qRcmd,"))264return eServerPacketType_qRcmd;265if (PACKET_STARTS_WITH("qRegisterInfo"))266return eServerPacketType_qRegisterInfo;267break;268269case 'S':270if (PACKET_STARTS_WITH("qSaveCore"))271return eServerPacketType_qLLDBSaveCore;272if (PACKET_STARTS_WITH("qSpeedTest:"))273return eServerPacketType_qSpeedTest;274if (PACKET_MATCHES("qShlibInfoAddr"))275return eServerPacketType_qShlibInfoAddr;276if (PACKET_MATCHES("qStepPacketSupported"))277return eServerPacketType_qStepPacketSupported;278if (PACKET_STARTS_WITH("qSupported"))279return eServerPacketType_qSupported;280if (PACKET_MATCHES("qSyncThreadStateSupported"))281return eServerPacketType_qSyncThreadStateSupported;282break;283284case 'T':285if (PACKET_STARTS_WITH("qThreadExtraInfo,"))286return eServerPacketType_qThreadExtraInfo;287if (PACKET_STARTS_WITH("qThreadStopInfo"))288return eServerPacketType_qThreadStopInfo;289break;290291case 'U':292if (PACKET_STARTS_WITH("qUserName:"))293return eServerPacketType_qUserName;294break;295296case 'V':297if (PACKET_MATCHES("qVAttachOrWaitSupported"))298return eServerPacketType_qVAttachOrWaitSupported;299break;300301case 'W':302if (PACKET_STARTS_WITH("qWatchpointSupportInfo:"))303return eServerPacketType_qWatchpointSupportInfo;304if (PACKET_MATCHES("qWatchpointSupportInfo"))305return eServerPacketType_qWatchpointSupportInfoSupported;306break;307308case 'X':309if (PACKET_STARTS_WITH("qXfer:"))310return eServerPacketType_qXfer;311break;312}313break;314315case 'j':316if (PACKET_STARTS_WITH("jModulesInfo:"))317return eServerPacketType_jModulesInfo;318if (PACKET_MATCHES("jSignalsInfo"))319return eServerPacketType_jSignalsInfo;320if (PACKET_MATCHES("jThreadsInfo"))321return eServerPacketType_jThreadsInfo;322323if (PACKET_MATCHES("jLLDBTraceSupported"))324return eServerPacketType_jLLDBTraceSupported;325if (PACKET_STARTS_WITH("jLLDBTraceStop:"))326return eServerPacketType_jLLDBTraceStop;327if (PACKET_STARTS_WITH("jLLDBTraceStart:"))328return eServerPacketType_jLLDBTraceStart;329if (PACKET_STARTS_WITH("jLLDBTraceGetState:"))330return eServerPacketType_jLLDBTraceGetState;331if (PACKET_STARTS_WITH("jLLDBTraceGetBinaryData:"))332return eServerPacketType_jLLDBTraceGetBinaryData;333break;334335case 'v':336if (PACKET_STARTS_WITH("vFile:")) {337if (PACKET_STARTS_WITH("vFile:open:"))338return eServerPacketType_vFile_open;339else if (PACKET_STARTS_WITH("vFile:close:"))340return eServerPacketType_vFile_close;341else if (PACKET_STARTS_WITH("vFile:pread"))342return eServerPacketType_vFile_pread;343else if (PACKET_STARTS_WITH("vFile:pwrite"))344return eServerPacketType_vFile_pwrite;345else if (PACKET_STARTS_WITH("vFile:size"))346return eServerPacketType_vFile_size;347else if (PACKET_STARTS_WITH("vFile:exists"))348return eServerPacketType_vFile_exists;349else if (PACKET_STARTS_WITH("vFile:fstat"))350return eServerPacketType_vFile_fstat;351else if (PACKET_STARTS_WITH("vFile:stat"))352return eServerPacketType_vFile_stat;353else if (PACKET_STARTS_WITH("vFile:mode"))354return eServerPacketType_vFile_mode;355else if (PACKET_STARTS_WITH("vFile:MD5"))356return eServerPacketType_vFile_md5;357else if (PACKET_STARTS_WITH("vFile:symlink"))358return eServerPacketType_vFile_symlink;359else if (PACKET_STARTS_WITH("vFile:unlink"))360return eServerPacketType_vFile_unlink;361362} else {363if (PACKET_STARTS_WITH("vAttach;"))364return eServerPacketType_vAttach;365if (PACKET_STARTS_WITH("vAttachWait;"))366return eServerPacketType_vAttachWait;367if (PACKET_STARTS_WITH("vAttachOrWait;"))368return eServerPacketType_vAttachOrWait;369if (PACKET_STARTS_WITH("vAttachName;"))370return eServerPacketType_vAttachName;371if (PACKET_STARTS_WITH("vCont;"))372return eServerPacketType_vCont;373if (PACKET_MATCHES("vCont?"))374return eServerPacketType_vCont_actions;375if (PACKET_STARTS_WITH("vKill;"))376return eServerPacketType_vKill;377if (PACKET_STARTS_WITH("vRun;"))378return eServerPacketType_vRun;379if (PACKET_MATCHES("vStopped"))380return eServerPacketType_vStopped;381if (PACKET_MATCHES("vCtrlC"))382return eServerPacketType_vCtrlC;383if (PACKET_MATCHES("vStdio"))384return eServerPacketType_vStdio;385break;386387}388break;389case '_':390switch (packet_cstr[1]) {391case 'M':392return eServerPacketType__M;393394case 'm':395return eServerPacketType__m;396}397break;398399case '?':400if (packet_size == 1)401return eServerPacketType_stop_reason;402break;403404case 'c':405return eServerPacketType_c;406407case 'C':408return eServerPacketType_C;409410case 'D':411return eServerPacketType_D;412413case 'g':414return eServerPacketType_g;415416case 'G':417return eServerPacketType_G;418419case 'H':420return eServerPacketType_H;421422case 'I':423return eServerPacketType_I;424425case 'k':426if (packet_size == 1)427return eServerPacketType_k;428break;429430case 'm':431return eServerPacketType_m;432433case 'M':434return eServerPacketType_M;435436case 'p':437return eServerPacketType_p;438439case 'P':440return eServerPacketType_P;441442case 's':443if (packet_size == 1)444return eServerPacketType_s;445break;446447case 'S':448return eServerPacketType_S;449450case 'x':451return eServerPacketType_x;452453case 'X':454return eServerPacketType_X;455456case 'T':457return eServerPacketType_T;458459case 'z':460if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4')461return eServerPacketType_z;462break;463464case 'Z':465if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4')466return eServerPacketType_Z;467break;468}469return eServerPacketType_unimplemented;470}471472bool StringExtractorGDBRemote::IsOKResponse() const {473return GetResponseType() == eOK;474}475476bool StringExtractorGDBRemote::IsUnsupportedResponse() const {477return GetResponseType() == eUnsupported;478}479480bool StringExtractorGDBRemote::IsNormalResponse() const {481return GetResponseType() == eResponse;482}483484bool StringExtractorGDBRemote::IsErrorResponse() const {485return GetResponseType() == eError && isxdigit(m_packet[1]) &&486isxdigit(m_packet[2]);487}488489uint8_t StringExtractorGDBRemote::GetError() {490if (GetResponseType() == eError) {491SetFilePos(1);492return GetHexU8(255);493}494return 0;495}496497lldb_private::Status StringExtractorGDBRemote::GetStatus() {498lldb_private::Status error;499if (GetResponseType() == eError) {500SetFilePos(1);501uint8_t errc = GetHexU8(255);502error.SetError(errc, lldb::eErrorTypeGeneric);503504error.SetErrorStringWithFormat("Error %u", errc);505std::string error_messg;506if (GetChar() == ';') {507GetHexByteString(error_messg);508error.SetErrorString(error_messg);509}510}511return error;512}513514size_t StringExtractorGDBRemote::GetEscapedBinaryData(std::string &str) {515// Just get the data bytes in the string as516// GDBRemoteCommunication::CheckForPacket() already removes any 0x7d escaped517// characters. If any 0x7d characters are left in the packet, then they are518// supposed to be there...519str.clear();520const size_t bytes_left = GetBytesLeft();521if (bytes_left > 0) {522str.assign(m_packet, m_index, bytes_left);523m_index += bytes_left;524}525return str.size();526}527528static bool529OKErrorNotSupportedResponseValidator(void *,530const StringExtractorGDBRemote &response) {531switch (response.GetResponseType()) {532case StringExtractorGDBRemote::eOK:533case StringExtractorGDBRemote::eError:534case StringExtractorGDBRemote::eUnsupported:535return true;536537case StringExtractorGDBRemote::eAck:538case StringExtractorGDBRemote::eNack:539case StringExtractorGDBRemote::eResponse:540break;541}542return false;543}544545static bool JSONResponseValidator(void *,546const StringExtractorGDBRemote &response) {547switch (response.GetResponseType()) {548case StringExtractorGDBRemote::eUnsupported:549case StringExtractorGDBRemote::eError:550return true; // Accept unsupported or EXX as valid responses551552case StringExtractorGDBRemote::eOK:553case StringExtractorGDBRemote::eAck:554case StringExtractorGDBRemote::eNack:555break;556557case StringExtractorGDBRemote::eResponse:558// JSON that is returned in from JSON query packets is currently always559// either a dictionary which starts with a '{', or an array which starts560// with a '['. This is a quick validator to just make sure the response561// could be valid JSON without having to validate all of the562// JSON content.563switch (response.GetStringRef()[0]) {564case '{':565return true;566case '[':567return true;568default:569break;570}571break;572}573return false;574}575576static bool577ASCIIHexBytesResponseValidator(void *,578const StringExtractorGDBRemote &response) {579switch (response.GetResponseType()) {580case StringExtractorGDBRemote::eUnsupported:581case StringExtractorGDBRemote::eError:582return true; // Accept unsupported or EXX as valid responses583584case StringExtractorGDBRemote::eOK:585case StringExtractorGDBRemote::eAck:586case StringExtractorGDBRemote::eNack:587break;588589case StringExtractorGDBRemote::eResponse: {590uint32_t valid_count = 0;591for (const char ch : response.GetStringRef()) {592if (!isxdigit(ch)) {593return false;594}595if (++valid_count >= 16)596break; // Don't validate all the characters in case the packet is very597// large598}599return true;600} break;601}602return false;603}604605void StringExtractorGDBRemote::CopyResponseValidator(606const StringExtractorGDBRemote &rhs) {607m_validator = rhs.m_validator;608m_validator_baton = rhs.m_validator_baton;609}610611void StringExtractorGDBRemote::SetResponseValidator(612ResponseValidatorCallback callback, void *baton) {613m_validator = callback;614m_validator_baton = baton;615}616617void StringExtractorGDBRemote::SetResponseValidatorToOKErrorNotSupported() {618m_validator = OKErrorNotSupportedResponseValidator;619m_validator_baton = nullptr;620}621622void StringExtractorGDBRemote::SetResponseValidatorToASCIIHexBytes() {623m_validator = ASCIIHexBytesResponseValidator;624m_validator_baton = nullptr;625}626627void StringExtractorGDBRemote::SetResponseValidatorToJSON() {628m_validator = JSONResponseValidator;629m_validator_baton = nullptr;630}631632bool StringExtractorGDBRemote::ValidateResponse() const {633// If we have a validator callback, try to validate the callback634if (m_validator)635return m_validator(m_validator_baton, *this);636else637return true; // No validator, so response is valid638}639640std::optional<std::pair<lldb::pid_t, lldb::tid_t>>641StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) {642llvm::StringRef view = llvm::StringRef(m_packet).substr(m_index);643size_t initial_length = view.size();644lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;645lldb::tid_t tid;646647if (view.consume_front("p")) {648// process identifier649if (view.consume_front("-1")) {650// -1 is a special case651pid = AllProcesses;652} else if (view.consumeInteger(16, pid) || pid == 0) {653// not a valid hex integer OR unsupported pid 0654m_index = UINT64_MAX;655return std::nullopt;656}657658// "." must follow if we expect TID too; otherwise, we assume -1659if (!view.consume_front(".")) {660// update m_index661m_index += initial_length - view.size();662663return {{pid, AllThreads}};664}665}666667// thread identifier668if (view.consume_front("-1")) {669// -1 is a special case670tid = AllThreads;671} else if (view.consumeInteger(16, tid) || tid == 0 || pid == AllProcesses) {672// not a valid hex integer OR tid 0 OR pid -1 + a specific tid673m_index = UINT64_MAX;674return std::nullopt;675}676677// update m_index678m_index += initial_length - view.size();679680return {{pid != LLDB_INVALID_PROCESS_ID ? pid : default_pid, tid}};681}682683684