summaryrefslogtreecommitdiff
path: root/gdbremote_parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gdbremote_parser.cpp')
-rw-r--r--gdbremote_parser.cpp207
1 files changed, 195 insertions, 12 deletions
diff --git a/gdbremote_parser.cpp b/gdbremote_parser.cpp
index e3f4aae..eb2e29a 100644
--- a/gdbremote_parser.cpp
+++ b/gdbremote_parser.cpp
@@ -3,13 +3,176 @@
#include "gdbremote_parser.hpp"
+#include <cctype>
+#include <cassert>
+#include <cstring>
+
using namespace GDBRemote;
-static inline bool is_hex_digit(uint8_t byte)
+enum class TokenType: uint8_t {
+ kUnknown = 0,
+ kSeparatorComma,
+ kSeparatorColon,
+ kSeparatorSemicolon,
+ kNumeric,
+ kHexNumeric,
+ kArbitrary,
+};
+
+static inline bool isseparator(uint8_t byte)
+{
+ return ':' == byte || ';' == byte || ',' == byte;
+}
+
+static inline TokenType separatorTypeFromByte(uint8_t byte)
{
- return byte >= '0' && byte <= '9'
- || byte >= 'A' && byte < 'F'
- || byte >= 'a' && byte <= 'f';
+ if (':' == byte) return TokenType::kSeparatorColon;
+ if (';' == byte) return TokenType::kSeparatorSemicolon;
+ return TokenType::kSeparatorComma;
+}
+
+struct Token {
+ TokenType type{};
+ std::string data{};
+
+ static std::vector<Token> Tokenize(std::string packet_data)
+ {
+ if (packet_data.length() == 0) return std::vector<Token>{};
+ std::vector<Token> tokens{};
+ TokenType type{};
+ size_t i, offset;
+ for (i = 0, offset = 0; i < packet_data.length(); i++) {
+ assert(type != TokenType::kSeparatorColon);
+ assert(type != TokenType::kSeparatorSemicolon);
+ assert(type != TokenType::kSeparatorComma);
+ const uint8_t byte = packet_data[i];
+ if (isseparator(byte)) {
+ if (i > offset) {
+ assert(type != TokenType::kUnknown);
+ tokens.push_back(Token{type, packet_data.substr(offset, i - offset)});
+ offset = i;
+ }
+ tokens.push_back(Token{
+ separatorTypeFromByte(byte),
+ packet_data.substr(offset, 1),
+ });
+ offset++;
+ type = TokenType::kUnknown;
+ } else if (isdigit(byte)) {
+ if (type == TokenType::kUnknown) {
+ type = TokenType::kNumeric;
+ }
+ } else if (isxdigit(byte)) {
+ if (type != TokenType::kArbitrary) {
+ type = TokenType::kHexNumeric;
+ }
+ } else {
+ type = TokenType::kArbitrary;
+ }
+ }
+ if (i > offset) {
+ assert(type != TokenType::kUnknown);
+ tokens.push_back(Token{type, packet_data.substr(offset, i - offset)});
+ }
+ return tokens;
+ }
+};
+
+struct Command {
+ std::string name;
+ Packet (*parse)(const std::vector<Token>&&);
+ static Packet parseNone(const std::vector<Token>&&)
+ {
+ return Packet::None();
+ }
+ static Packet parseQuerySupported(const std::vector<Token>&&)
+ {
+ // TODO arguments
+ return Packet{PacketType::kQuerySupported};
+ }
+ static Packet parseQueryHaltReason(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kQueryHaltReason};
+ }
+ static Packet parseQueryC(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kQueryC};
+ }
+ static Packet parseQueryFThreadInfo(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kQueryFThreadInfo};
+ }
+ static Packet parseQuerySThreadInfo(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kQuerySThreadInfo};
+ }
+ static Packet parseQueryRTOSThreadInfo(const std::vector<Token>&&)
+ {
+ // TODO arguments
+ return Packet{PacketType::kQueryRTOSThreadInfo};
+ }
+ static Packet parseQueryAttached(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kQueryAttached};
+ }
+ static Packet parseQueryTraceStatus(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kQueryTraceStatus};
+ }
+ static Packet parseSetThreadForCont(const std::vector<Token>&&)
+ {
+ // TODO arguments
+ return Packet{PacketType::kSetThreadForCont};
+ }
+ static Packet parseSetThreadForOps(const std::vector<Token>&&)
+ {
+ // TODO arguments
+ return Packet{PacketType::kSetThreadForOps};
+ }
+ static Packet parseMustReplyEmpty(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kVMustReplyEmpty};
+ }
+ static Packet parseEnableExtendedMode(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kEnableExtendedMode};
+ }
+};
+
+static const Command commands[] = {
+ { "qSupported", Command::parseQuerySupported },
+ { "?", Command::parseQueryHaltReason },
+ { "qC", Command::parseQueryC },
+ { "qfThreadInfo", Command::parseQueryFThreadInfo },
+ { "qsThreadInfo", Command::parseQuerySThreadInfo },
+ { "qL", Command::parseQueryRTOSThreadInfo },
+ { "qAttached", Command::parseQueryAttached },
+ { "qTStatus", Command::parseQueryTraceStatus },
+ { "Hc", Command::parseSetThreadForCont },
+ { "Hg", Command::parseSetThreadForOps },
+ { "vMustReplyEmpty", Command::parseMustReplyEmpty },
+ { "!", Command::parseEnableExtendedMode },
+};
+
+Packet Packet::Parse(std::string packet_data)
+{
+ const auto tokens = Token::Tokenize(packet_data);
+ if (tokens.size() == 0) return Packet::None();
+ const auto first_token = tokens[0];
+ if (first_token.type != TokenType::kArbitrary) return Packet::None();
+ for (const auto& command: commands) {
+ const auto cmdlen = command.name.length();
+ const auto toklen = first_token.data.length();
+ const auto len = std::min(cmdlen, cmdlen);
+ // Make sure first token fully includes the command from the beginning
+ // and not necessary fully equals to it.
+ if (cmdlen <= toklen && 0 == memcmp(
+ &command.name[0], &first_token.data[0], len))
+ {
+ return command.parse(std::move(tokens));
+ }
+ }
+ return Packet::None();
}
static inline uint8_t parseChecksumFromHexChars(uint8_t first, uint8_t second)
@@ -22,6 +185,7 @@ static inline uint8_t parseChecksumFromHexChars(uint8_t first, uint8_t second)
std::unique_ptr<ExchangeResult> ExchangeContext::Consume(uint8_t byte)
{
+ _last_packet += byte;
switch (_parsing_state) {
case ParsingState::kIdle:
if (byte == '$') {
@@ -40,7 +204,7 @@ std::unique_ptr<ExchangeResult> ExchangeContext::Consume(uint8_t byte)
// TODO escaped and binary data
break;
case ParsingState::kChecksum1:
- if (is_hex_digit(byte)) {
+ if (isxdigit(byte)) {
transit(ParsingState::kChecksum2);
_given_checksum_first_byte = byte;
} else {
@@ -52,7 +216,7 @@ std::unique_ptr<ExchangeResult> ExchangeContext::Consume(uint8_t byte)
const uint8_t checksum = _checksum;
std::string packet_data(std::move(_packet_data));
transit(ParsingState::kIdle);
- if (is_hex_digit(byte)) {
+ if (isxdigit(byte)) {
const uint8_t given_checksum =
parseChecksumFromHexChars(_given_checksum_first_byte, byte);
if (given_checksum == checksum) {
@@ -74,22 +238,41 @@ std::unique_ptr<ExchangeResult> ExchangeContext::Consume(uint8_t byte)
std::string ExchangeContext::WrapDataToSend(Packet packet)
{
- (void) packet;
+ switch (packet.type) {
+ case PacketType::kNone:
+ case PacketType::kVMustReplyEmpty:
+ case PacketType::kQuerySupported:
+ case PacketType::kQueryHaltReason:
+ case PacketType::kQueryC:
+ case PacketType::kQueryFThreadInfo:
+ case PacketType::kQuerySThreadInfo:
+ case PacketType::kQueryRTOSThreadInfo:
+ case PacketType::kQueryAttached:
+ case PacketType::kQueryTraceStatus:
+ case PacketType::kSetThreadForCont:
+ case PacketType::kSetThreadForOps:
+ case PacketType::kEnableExtendedMode:
+ return "$#00";
+ break;
+ }
return std::string{};
}
void ExchangeContext::transit(ParsingState new_state)
{
- printf(
- "GDBRemote ParsingState: %s -> %s\n",
- parsingStateToString(_parsing_state),
- parsingStateToString(new_state));
+ if (0) {
+ printf(
+ "GDBRemote ParsingState: %s -> %s\n",
+ parsingStateToString(_parsing_state),
+ parsingStateToString(new_state));
+ }
switch (new_state) {
case ParsingState::kIdle:
_checksum = 0;
_packet_data.clear();
break;
case ParsingState::kPacketData:
+ _last_packet = "$";
break;
case ParsingState::kPacketDataBinSecond:
break;
@@ -115,5 +298,5 @@ const char* ExchangeContext::parsingStateToString(ParsingState state)
case ParsingState::kChecksum2:
return "Checksum2";
}
- return "?";
+ return "<unknown>";
}