base: Generalize remote GDB query commands.

Dispatching qFoo style commands now use a lookup table instead of a
hand coded sequence of one off checks. It also splits the handling of
different queries into different functions.

Change-Id: I8f774760e856377c5cce90b23e57de6a7f828395
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/44028
Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabe Black
2020-06-07 21:35:35 -07:00
committed by Gabe Black
parent 451f731748
commit 6d6fffe964
2 changed files with 136 additions and 36 deletions

View File

@@ -137,6 +137,7 @@
#include <cstdio>
#include <sstream>
#include <string>
#include <utility>
#include "base/intmath.hh"
#include "base/socket.hh"
@@ -916,48 +917,120 @@ BaseRemoteGDB::cmdMemW(GdbCommand::Context &ctx)
return true;
}
namespace {
std::pair<std::string, std::string>
splitAt(std::string str, const char * const delim)
{
size_t pos = str.find_first_of(delim);
if (pos == std::string::npos)
return std::pair<std::string, std::string>(str, "");
else
return std::pair<std::string, std::string>(
str.substr(0, pos), str.substr(pos + 1));
}
} // anonymous namespace
std::map<std::string, BaseRemoteGDB::QuerySetCommand>
BaseRemoteGDB::queryMap = {
{ "C", { &BaseRemoteGDB::queryC } },
{ "Supported", { &BaseRemoteGDB::querySupported, ";" } },
{ "Xfer", { &BaseRemoteGDB::queryXfer } },
};
void
BaseRemoteGDB::queryC(QuerySetCommand::Context &ctx)
{
send("QC0");
}
void
BaseRemoteGDB::querySupported(QuerySetCommand::Context &ctx)
{
std::ostringstream oss;
// This reply field mandatory. We can receive arbitrarily
// long packets, so we could choose it to be arbitrarily large.
// This is just an arbitrary filler value that seems to work.
oss << "PacketSize=1024";
for (const auto& feature : availableFeatures())
oss << ';' << feature;
send(oss.str().c_str());
}
void
BaseRemoteGDB::queryXfer(QuerySetCommand::Context &ctx)
{
auto split = splitAt(ctx.args.at(0), ":");
auto object = split.first;
split = splitAt(split.second, ":");
auto operation = split.first;
// Only the "features" object and "read"ing are supported currently.
if (object != "features" || operation != "read")
throw Unsupported();
// Extract the annex name.
split = splitAt(split.second, ":");
auto annex = split.first;
// Read the contents of the annex.
std::string content;
if (!getXferFeaturesRead(annex, content))
throw CmdError("E00");
// Extract the offset and length.
split = splitAt(split.second, ",");
auto offset_str = split.first;
auto length_str = split.second;
const char *offset_ptr = offset_str.c_str();
const char *length_ptr = length_str.c_str();
auto offset = hex2i(&offset_ptr);
auto length = hex2i(&length_ptr);
if (offset_ptr != offset_str.c_str() + offset_str.length() ||
length_ptr != length_str.c_str() + length_str.length()) {
throw CmdError("E00");
}
std::string encoded;
encodeXferResponse(content, encoded, offset, length);
send(encoded.c_str());
}
bool
BaseRemoteGDB::cmdQueryVar(GdbCommand::Context &ctx)
{
// The query command goes until the first ':', or the end of the string.
std::string s(ctx.data, ctx.len);
std::string xfer_read_prefix = "Xfer:features:read:";
if (s.rfind("Supported:", 0) == 0) {
std::ostringstream oss;
// This reply field mandatory. We can receive arbitrarily
// long packets, so we could choose it to be arbitrarily large.
// This is just an arbitrary filler value that seems to work.
oss << "PacketSize=1024";
for (const auto& feature : availableFeatures())
oss << ';' << feature;
send(oss.str().c_str());
} else if (s.rfind(xfer_read_prefix, 0) == 0) {
size_t offset, length;
auto value_string = s.substr(xfer_read_prefix.length());
auto colon_pos = value_string.find(':');
auto comma_pos = value_string.find(',');
if (colon_pos == std::string::npos || comma_pos == std::string::npos)
throw CmdError("E00");
std::string annex;
if (!getXferFeaturesRead(value_string.substr(0, colon_pos), annex))
throw CmdError("E00");
try {
offset = std::stoull(
value_string.substr(colon_pos + 1, comma_pos), NULL, 16);
length = std::stoull(
value_string.substr(comma_pos + 1), NULL, 16);
} catch (std::invalid_argument& e) {
throw CmdError("E00");
} catch (std::out_of_range& e) {
throw CmdError("E00");
}
std::string encoded;
encodeXferResponse(annex, encoded, offset, length);
send(encoded.c_str());
} else if (s == "C") {
send("QC0");
} else {
auto query_split = splitAt({ ctx.data, (size_t)ctx.len }, ":");
const auto &query_str = query_split.first;
// Look up the query command, and report if it isn't found.
auto query_it = queryMap.find(query_str);
if (query_it == queryMap.end()) {
DPRINTF(GDBMisc, "Unknown query %s\n", s);
throw Unsupported();
}
// If it was found, construct a context.
QuerySetCommand::Context qctx(query_str);
const auto &query = query_it->second;
auto remaining = std::move(query_split.second);
if (!query.argSep) {
qctx.args.emplace_back(std::move(remaining));
} else {
while (remaining != "") {
auto arg_split = splitAt(remaining, query.argSep);
qctx.args.emplace_back(std::move(arg_split.first));
remaining = std::move(arg_split.second);
}
}
(this->*(query.func))(qctx);
return true;
}

View File

@@ -48,6 +48,7 @@
#include <exception>
#include <map>
#include <string>
#include <vector>
#include "arch/types.hh"
#include "base/pollevent.hh"
@@ -298,6 +299,32 @@ class BaseRemoteGDB
bool cmdClrHwBkpt(GdbCommand::Context &ctx);
bool cmdSetHwBkpt(GdbCommand::Context &ctx);
struct QuerySetCommand
{
struct Context
{
const std::string &name;
std::vector<std::string> args;
Context(const std::string &_name) : name(_name) {}
};
using Func = void (BaseRemoteGDB::*)(Context &ctx);
const char * const argSep;
const Func func;
QuerySetCommand(Func _func, const char *_argSep=nullptr) :
argSep(_argSep), func(_func)
{}
};
static std::map<std::string, QuerySetCommand> queryMap;
void queryC(QuerySetCommand::Context &ctx);
void querySupported(QuerySetCommand::Context &ctx);
void queryXfer(QuerySetCommand::Context &ctx);
protected:
ThreadContext *context() { return tc; }
System *system() { return sys; }