llvm-project/lldb/source/Utility/StringExtractorGDBRemote.cpp

521 lines
18 KiB
C++

//===-- StringExtractorGDBRemote.cpp ----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
#include <string.h>
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "Utility/StringExtractorGDBRemote.h"
StringExtractorGDBRemote::ResponseType
StringExtractorGDBRemote::GetResponseType () const
{
if (m_packet.empty())
return eUnsupported;
switch (m_packet[0])
{
case 'E':
if (m_packet.size() == 3 &&
isxdigit(m_packet[1]) &&
isxdigit(m_packet[2]))
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;
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 ("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 '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;
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;
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 ("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:auxv:read::")) return eServerPacketType_qXfer_auxv_read;
break;
}
break;
case 'j':
if (PACKET_MATCHES("jSignalsInfo")) return eServerPacketType_jSignalsInfo;
if (PACKET_MATCHES("jThreadsInfo")) return eServerPacketType_jThreadsInfo;
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: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;
}
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':
if (packet_size == 1) return eServerPacketType_D;
break;
case 'g':
if (packet_size == 1) return eServerPacketType_g;
break;
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 &&
m_packet.size() == 3 &&
isxdigit(m_packet[1]) &&
isxdigit(m_packet[2]);
}
uint8_t
StringExtractorGDBRemote::GetError ()
{
if (GetResponseType() == eError)
{
SetFilePos(1);
return GetHexU8(255);
}
return 0;
}
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
}