diff --git a/vp/src/core/common/gdb-mc/gdb_server.cpp b/vp/src/core/common/gdb-mc/gdb_server.cpp index e6fc1e637..c8a32b7f5 100644 --- a/vp/src/core/common/gdb-mc/gdb_server.cpp +++ b/vp/src/core/common/gdb-mc/gdb_server.cpp @@ -137,9 +137,11 @@ void GDBServer::exec_thread(thread_func fn, char op) { fn(thread); } -std::vector GDBServer::run_threads(int id, bool single) { +std::vector GDBServer::run_threads(std::vector hartsrun, bool single) { + if (hartsrun.empty()) { + return hartsrun; + } this->single_run = single; - std::vector hartsrun = get_threads(id); /* invoke all selected harts */ sc_core::sc_event_or_list allharts; diff --git a/vp/src/core/common/gdb-mc/gdb_server.h b/vp/src/core/common/gdb-mc/gdb_server.h index eaf26c448..a8b5461af 100644 --- a/vp/src/core/common/gdb-mc/gdb_server.h +++ b/vp/src/core/common/gdb-mc/gdb_server.h @@ -87,7 +87,7 @@ SC_MODULE(GDBServer) { std::vector get_threads(int); uint64_t translate_addr(debug_target_if *, uint64_t, MemoryAccessType type); void exec_thread(thread_func, char = 'g'); - std::vector run_threads(int, bool = false); + std::vector run_threads(std::vector, bool = false); void writeall(int, char *, size_t); void send_packet(int, const char *, gdb_kind_t = GDB_KIND_PACKET); void retransmit(int); diff --git a/vp/src/core/common/gdb-mc/handler.cpp b/vp/src/core/common/gdb-mc/handler.cpp index 2ba72980a..c55d88f35 100644 --- a/vp/src/core/common/gdb-mc/handler.cpp +++ b/vp/src/core/common/gdb-mc/handler.cpp @@ -208,6 +208,7 @@ void GDBServer::vCont(int conn, gdb_command_t *cmd) { gdb_vcont_t *vcont; int stopped_thread = -1; const char *stop_reason = NULL; + std::map matched; /* This handler attempts to implement the all-stop mode. * See: https://sourceware.org/gdb/onlinedocs/gdb/All_002dStop-Mode.html */ @@ -217,12 +218,21 @@ void GDBServer::vCont(int conn, gdb_command_t *cmd) { bool single = false; if (vcont->action == 's') single = true; - else if (vcont->action == 'S') + else if (vcont->action != 'c') throw std::invalid_argument("Unimplemented vCont action"); /* TODO */ std::vector selected_harts; try { - selected_harts = run_threads(vcont->thread.tid, single); + auto run = get_threads(vcont->thread.tid); + for (auto i = run.begin(); i != run.end();) { + debug_target_if *hart = *i; + if (matched.count(hart)) + i = run.erase(i); /* already matched */ + else + i++; + } + + selected_harts = run_threads(run, single); } catch (const std::out_of_range&) { send_packet(conn, "E01"); return; @@ -246,6 +256,16 @@ void GDBServer::vCont(int conn, gdb_command_t *cmd) { continue; } } + + /* The vCont specification mandates that only the leftmost action with + * a matching thread-id is applied. Unfortunately, the specification + * is a bit unclear in regards to handling two actions with no thread + * id (i.e. GDB_THREAD_ALL). */ + if (vcont->thread.tid > 0) { + auto threads = get_threads(vcont->thread.tid); + assert(threads.size() == 1); + matched[threads.front()] = true; + } } assert(stop_reason && stopped_thread >= 1); diff --git a/vp/src/core/common/gdb-mc/libgdb/parser2.c b/vp/src/core/common/gdb-mc/libgdb/parser2.c index fe9568c6b..dd9abf83a 100644 --- a/vp/src/core/common/gdb-mc/libgdb/parser2.c +++ b/vp/src/core/common/gdb-mc/libgdb/parser2.c @@ -223,7 +223,7 @@ gdbf_vcont(int n, mpc_val_t **xs) vcont->next = (gdb_vcont_t *)xs[i]; vcont->next = NULL; - return vcont; + return (gdb_vcont_t *)xs[0]; } gdbf_fold(vcont, GDB_ARG_VCONT, GDBF_ARG_VCONT)