feat: process custom gdb commands
This commit is contained in:
parent
46c943dea9
commit
5228b6117d
9 changed files with 164 additions and 39 deletions
|
@ -112,11 +112,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1714033851,
|
||||
"narHash": "sha256-Mi7m3p9vmtNOdyD1hLse/tzxDuV3bwP0gKrmOBPiQ4c=",
|
||||
"lastModified": 1721457008,
|
||||
"narHash": "sha256-ekpve0om5hzC1Ntd3zm1cZ9oS5pnr7a2n/tueyqFOsg=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "809554f41ac44acc4b1ec21473746c2af9993f2f",
|
||||
"revCount": 149,
|
||||
"rev": "e7aa3319d52fa987ac2192f63aef3dcb1b057e3a",
|
||||
"revCount": 151,
|
||||
"type": "git",
|
||||
"url": "https://git.xinyang.life/xin/nur.git"
|
||||
},
|
||||
|
|
|
@ -52,4 +52,3 @@
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,10 +53,11 @@ class Target {
|
|||
public:
|
||||
DiffTargetApi ops;
|
||||
TargetMeta meta;
|
||||
arch_info_t arch;
|
||||
size_t argsize;
|
||||
std::vector<uint8_t> args; // used as a buffer to store target specific values
|
||||
|
||||
bool *do_difftest;
|
||||
arch_info_t *isa_arch_info;
|
||||
size_t *dbg_state_size;
|
||||
gdb_action_t last_res;
|
||||
|
||||
Target(){};
|
||||
|
|
|
@ -10,6 +10,8 @@ struct Config {
|
|||
std::vector<std::string> refs_prefix;
|
||||
std::filesystem::path dut;
|
||||
std::string dut_prefix = "";
|
||||
std::string gdbstub_addr = "/tmp/gdbstub-diffu.sock";
|
||||
bool use_debugger = false;
|
||||
|
||||
int cli_parse(int argc, char **argv);
|
||||
};
|
||||
|
|
|
@ -16,8 +16,19 @@ private:
|
|||
|
||||
// target used for read_reg, write_reg, read_mem, write_mem
|
||||
Target *current_target = &dut;
|
||||
bool halt_status = false;
|
||||
inline void start_run() {
|
||||
__atomic_store_n(&halt_status, false, __ATOMIC_RELAXED);
|
||||
};
|
||||
inline bool is_halt() {
|
||||
return __atomic_load_n(&halt_status, __ATOMIC_RELAXED);
|
||||
};
|
||||
|
||||
bool exec(size_t n, gdb_action_t *ret);
|
||||
struct ExecRet {
|
||||
bool at_breakpoint;
|
||||
bool do_difftest;
|
||||
};
|
||||
ExecRet exec(size_t n, gdb_action_t *ret);
|
||||
|
||||
public:
|
||||
Difftest(Target &&dut, std::vector<Target> &&refs);
|
||||
|
@ -35,14 +46,16 @@ public:
|
|||
bool del_bp(size_t addr, bp_type_t type);
|
||||
|
||||
bool check_all();
|
||||
int sync_regs_to_ref(void);
|
||||
|
||||
arch_info_t get_arch() const {
|
||||
std::cout << dut.arch.reg_num << std::endl;
|
||||
return dut.arch;
|
||||
}
|
||||
inline void halt() {
|
||||
__atomic_store_n(&halt_status, true, __ATOMIC_RELAXED);
|
||||
};
|
||||
|
||||
arch_info_t get_arch() const { return *dut.isa_arch_info; }
|
||||
|
||||
static bool check(Target &dut, Target &ref) {
|
||||
for (int r = 0; r < dut.arch.reg_num; r++) {
|
||||
for (int r = 0; r < dut.isa_arch_info->reg_num; r++) {
|
||||
size_t regdut = 0, regref = 0;
|
||||
dut.ops.read_reg(dut.args.data(), r, ®dut);
|
||||
ref.ops.read_reg(ref.args.data(), r, ®ref);
|
||||
|
|
|
@ -24,8 +24,12 @@ int Config::cli_parse(int argc, char **argv) {
|
|||
app.add_option("--dut-prefix", dut_prefix,
|
||||
"Optional prefix for design under test");
|
||||
|
||||
app.add_option("--listen", gdbstub_addr, "Gdb remote listen address");
|
||||
|
||||
app.add_flag("-g", use_debugger, "Launch gdb remote stub");
|
||||
|
||||
app.set_config("-c,--config")
|
||||
->transform(CLI::FileOnDefaultPath("./difftest.toml"));
|
||||
->transform(CLI::FileOnDefaultPath("difftest.toml"));
|
||||
|
||||
// Default value for refs_prefix
|
||||
app.callback([&]() {
|
||||
|
|
|
@ -11,8 +11,8 @@ Difftest::Difftest(Target &&dut, std::vector<Target> &&refs) {
|
|||
this->refs = std::move(refs);
|
||||
|
||||
for (const auto &ref : refs) {
|
||||
if (dut.arch.reg_byte != ref.arch.reg_byte ||
|
||||
dut.arch.reg_num != ref.arch.reg_num) {
|
||||
if (dut.isa_arch_info->reg_byte != ref.isa_arch_info->reg_byte ||
|
||||
dut.isa_arch_info->reg_num != ref.isa_arch_info->reg_num) {
|
||||
throw std::runtime_error("Ref and dut must have the same architecture");
|
||||
}
|
||||
}
|
||||
|
@ -36,9 +36,9 @@ void Difftest::setup(const std::filesystem::path &memory_file) {
|
|||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
auto &target = *it;
|
||||
target.ops.init(target.args.data());
|
||||
target.ops.write_mem(target.args.data(), 0x80000000UL, membuf.size(),
|
||||
membuf.data());
|
||||
target.ops.write_reg(target.args.data(), 32, 0x80000000UL);
|
||||
if (target.ops.write_mem(target.args.data(), 0x80000000UL, membuf.size(),
|
||||
membuf.data()) != 0)
|
||||
throw std::runtime_error("write_mem failed");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,26 +49,29 @@ bool Difftest::check_all() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Difftest::exec(size_t n, gdb_action_t *ret) {
|
||||
Difftest::ExecRet Difftest::exec(size_t n, gdb_action_t *ret) {
|
||||
ExecRet exec_ret = {.at_breakpoint = false, .do_difftest = true};
|
||||
while (n--) {
|
||||
bool breakflag = false;
|
||||
Target *pbreak = &(*(this->begin()));
|
||||
// TODO: For improvement, use ThreadPool here for concurrent execution?
|
||||
for (auto it = this->begin(); it != this->end(); ++it) {
|
||||
auto &target = *it;
|
||||
*target.do_difftest = true;
|
||||
target.ops.stepi(target.args.data(), &target.last_res);
|
||||
if (target.is_on_breakpoint()) {
|
||||
breakflag = true;
|
||||
exec_ret.at_breakpoint = true;
|
||||
pbreak = ⌖
|
||||
}
|
||||
exec_ret.do_difftest = *target.do_difftest && exec_ret.do_difftest;
|
||||
}
|
||||
|
||||
if (breakflag) {
|
||||
if (exec_ret.at_breakpoint) {
|
||||
ret->reason = pbreak->last_res.reason;
|
||||
ret->data = pbreak->last_res.data;
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return exec_ret;
|
||||
}
|
||||
|
||||
gdb_action_t Difftest::stepi() {
|
||||
|
@ -80,8 +83,14 @@ gdb_action_t Difftest::stepi() {
|
|||
|
||||
gdb_action_t Difftest::cont() {
|
||||
gdb_action_t ret = {.reason = gdb_action_t::ACT_NONE};
|
||||
while (exec(1, &ret)) {
|
||||
check_all();
|
||||
ExecRet exec_ret;
|
||||
start_run();
|
||||
while (!is_halt()) {
|
||||
exec_ret = exec(1, &ret);
|
||||
if (exec_ret.do_difftest)
|
||||
check_all();
|
||||
if (exec_ret.at_breakpoint)
|
||||
break;
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
@ -96,6 +105,26 @@ int Difftest::write_reg(int regno, size_t value) {
|
|||
value);
|
||||
}
|
||||
|
||||
int Difftest::sync_regs_to_ref(void) {
|
||||
std::vector<size_t> regs;
|
||||
int ret = 0;
|
||||
for (int i = 0; i <= get_arch().reg_num; i++) {
|
||||
size_t r;
|
||||
ret = dut.ops.read_reg(dut.args.data(), i, &r);
|
||||
if (ret)
|
||||
return ret;
|
||||
regs.push_back(r);
|
||||
}
|
||||
for (auto &ref : refs) {
|
||||
for (int i = 0; i <= get_arch().reg_num; i++) {
|
||||
ret = ref.ops.write_reg(ref.args.data(), i, regs.at(i));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Difftest::read_mem(size_t addr, size_t len, void *val) {
|
||||
return current_target->ops.read_mem(current_target->args.data(), addr, len,
|
||||
val);
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
#include <CLI/App.hpp>
|
||||
#include <CLI/Error.hpp>
|
||||
#include <cstring>
|
||||
#include <difftest.hpp>
|
||||
#include <sstream>
|
||||
extern "C" {
|
||||
#include <gdbstub.h>
|
||||
}
|
||||
|
@ -43,7 +47,71 @@ static bool difftest_del_bp(void *args, size_t addr, bp_type_t type) {
|
|||
return diff->del_bp(addr, type);
|
||||
}
|
||||
|
||||
int gdbstub_loop(Difftest *diff) {
|
||||
static void difftest_on_interrupt(void *args) {
|
||||
Difftest *diff = (Difftest *)args;
|
||||
puts("interrupt");
|
||||
diff->halt();
|
||||
}
|
||||
|
||||
std::vector<std::string> split_into_args(const std::string &command) {
|
||||
std::istringstream iss(command);
|
||||
std::vector<std::string> args;
|
||||
std::string token;
|
||||
while (iss >> token) {
|
||||
args.push_back(token);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
static char *gdbstub_monitor(void *args, const char *s) {
|
||||
Difftest *diff = (Difftest *)args;
|
||||
CLI::App parser;
|
||||
std::string ret = "";
|
||||
|
||||
parser.add_subcommand("help", "Print help message")->callback([&]() {
|
||||
ret = parser.help();
|
||||
});
|
||||
auto sync = parser.add_subcommand("sync", "Sync states between targets")
|
||||
->callback([&]() { diff->sync_regs_to_ref(); });
|
||||
|
||||
std::string cmdstr;
|
||||
int slen = strlen(s);
|
||||
int ch;
|
||||
for (int i = 0; i < slen; i += 2) {
|
||||
sscanf(&s[i], "%02x", &ch);
|
||||
cmdstr.push_back(ch);
|
||||
}
|
||||
|
||||
auto arglist = split_into_args(cmdstr);
|
||||
std::vector<const char *> argv = {""};
|
||||
for (const auto &arg : arglist) {
|
||||
argv.push_back(static_cast<const char *>(arg.c_str()));
|
||||
}
|
||||
|
||||
try {
|
||||
(parser).parse((argv.size()), (argv.data()));
|
||||
} catch (const CLI ::ParseError &e) {
|
||||
std::ostringstream os;
|
||||
os << "Failed to parse " << cmdstr << std::endl
|
||||
<< parser.help() << std::endl;
|
||||
ret = os.str();
|
||||
}
|
||||
|
||||
if (ret[0] == '\0') {
|
||||
return NULL;
|
||||
} else {
|
||||
std::ostringstream ret_stream;
|
||||
// Set formatting options for the stream
|
||||
ret_stream << std::hex << std::setfill('0');
|
||||
|
||||
for (unsigned char c : ret) {
|
||||
ret_stream << std::setw(2) << static_cast<int>(c);
|
||||
}
|
||||
return strdup(ret_stream.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
int gdbstub_loop(Difftest *diff, std::string socket_addr) {
|
||||
target_ops gdbstub_ops = {.cont = difftest_cont,
|
||||
.stepi = difftest_stepi,
|
||||
.read_reg = difftest_read_reg,
|
||||
|
@ -52,12 +120,18 @@ int gdbstub_loop(Difftest *diff) {
|
|||
.write_mem = difftest_write_mem,
|
||||
.set_bp = difftest_set_bp,
|
||||
.del_bp = difftest_del_bp,
|
||||
.on_interrupt = NULL};
|
||||
.on_interrupt = difftest_on_interrupt,
|
||||
.monitor = gdbstub_monitor};
|
||||
gdbstub_t gdbstub_priv;
|
||||
char socket_addr[] = "127.0.0.1:1234";
|
||||
gdbstub_init(&gdbstub_priv, &gdbstub_ops, diff->get_arch(), socket_addr);
|
||||
|
||||
std::cout << "Waiting for gdb connection at " << socket_addr << std::endl;
|
||||
|
||||
if (!gdbstub_init(&gdbstub_priv, &gdbstub_ops, diff->get_arch(),
|
||||
socket_addr.c_str())) {
|
||||
std::cerr << "Failed to init socket at: " << socket_addr << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = gdbstub_run(&gdbstub_priv, diff);
|
||||
gdbstub_close(&gdbstub_priv);
|
||||
return !success;
|
||||
|
|
11
src/main.cpp
11
src/main.cpp
|
@ -2,9 +2,8 @@
|
|||
#include "config.hpp"
|
||||
#include "difftest.hpp"
|
||||
|
||||
// extern "C" {
|
||||
int gdbstub_loop(Difftest *);
|
||||
// }
|
||||
int gdbstub_loop(Difftest *, std::string);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Config config;
|
||||
int ret = 0;
|
||||
|
@ -27,7 +26,11 @@ int main(int argc, char **argv) {
|
|||
|
||||
difftest.setup(config.memory_file);
|
||||
|
||||
gdbstub_loop(&difftest);
|
||||
if (config.use_debugger) {
|
||||
gdbstub_loop(&difftest, config.gdbstub_addr);
|
||||
} else {
|
||||
difftest.cont();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue