diff options
author | Oxore <oxore@protonmail.com> | 2022-08-27 22:52:31 +0300 |
---|---|---|
committer | Oxore <oxore@protonmail.com> | 2022-08-27 22:52:31 +0300 |
commit | 8c012736814265e198d68654ccdc597c265f4f82 (patch) | |
tree | 67bf114dd4424c89af0862a7ee9fe01afd9443c9 /gdbremote_parser.cpp | |
parent | 9580a9a8daa4426914f396a694903ab880ecd3f2 (diff) |
Introduce sophisticated parsing in GDBRemote
Diffstat (limited to 'gdbremote_parser.cpp')
-rw-r--r-- | gdbremote_parser.cpp | 207 |
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>"; } |