From 4e33cad348c50a77f7e165a618fda8f21561d1ec Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Sun, 17 Mar 2024 09:10:23 +1300 Subject: [PATCH] Create `ExtendedTaskId` to carry aboth a `TaskUid` and a `ThreadGroupUid` and use it instead of `GdbThreadId` where we know we have a specific task that exists. --- src/GdbCommand.cc | 2 +- src/GdbServer.cc | 137 ++++++++++++++++++------------------- src/GdbServer.h | 29 +++----- src/GdbServerConnection.cc | 66 +++++++----------- src/GdbServerConnection.h | 58 ++++++++++++---- 5 files changed, 146 insertions(+), 146 deletions(-) diff --git a/src/GdbCommand.cc b/src/GdbCommand.cc index d3c77fbaa01..cc5df9f4127 100644 --- a/src/GdbCommand.cc +++ b/src/GdbCommand.cc @@ -120,7 +120,7 @@ string invoke_checkpoint(GdbServer& gdb_server, Task*, e = GdbServer::Checkpoint::NOT_EXPLICIT; } gdb_server.checkpoints[checkpoint_id] = GdbServer::Checkpoint( - gdb_server.timeline, gdb_server.last_continue_tuid, e, where); + gdb_server.timeline, gdb_server.last_continue_task, e, where); return string("Checkpoint ") + to_string(checkpoint_id) + " at " + where; } static SimpleGdbCommand checkpoint( diff --git a/src/GdbServer.cc b/src/GdbServer.cc index bbe06ab44b5..69981fd403e 100644 --- a/src/GdbServer.cc +++ b/src/GdbServer.cc @@ -45,12 +45,17 @@ GdbServer::ConnectionFlags::ConnectionFlags() serve_files(false), debugger_params_write_pipe(nullptr) {} +static ExtendedTaskId extended_task_id(Task* t) { + return ExtendedTaskId(t->thread_group()->tguid(), t->tuid()); +} + GdbServer::GdbServer(std::unique_ptr& dbg, Task* t) : dbg(std::move(dbg)), debuggee_tguid(t->thread_group()->tguid()), - last_continue_tuid(t->tuid()), - last_query_tuid(t->tuid()), + last_continue_task(extended_task_id(t)), + last_query_task(extended_task_id(t)), final_event(UINT32_MAX), + in_debuggee_end_state(false), failed_restart(false), stop_replaying_to_target(false), interrupt_pending(false), @@ -60,6 +65,20 @@ GdbServer::GdbServer(std::unique_ptr& dbg, Task* t) memset(&stop_siginfo, 0, sizeof(stop_siginfo)); } +GdbServer::GdbServer(std::shared_ptr session, const Target& target) + : target(target), + final_event(UINT32_MAX), + in_debuggee_end_state(false), + failed_restart(false), + stop_replaying_to_target(false), + interrupt_pending(false), + exit_sigkill_pending(false), + timeline(std::move(session)), + emergency_debug_session(nullptr), + file_scope_pid(0) { + memset(&stop_siginfo, 0, sizeof(stop_siginfo)); +} + /** * Attempt to find the value of |regname| (a DebuggerRegister * name), and if so (i) write it to |buf|; (ii) @@ -116,25 +135,14 @@ GdbRegisterValue GdbServer::get_reg(const Registers& regs, return reg; } -static GdbThreadId get_threadid(const Session& session, const TaskUid& tuid) { - Task* t = session.find_task(tuid); - pid_t pid = t ? t->tgid() : GdbThreadId::ANY.pid; - return GdbThreadId(pid, tuid.tid()); -} - -static GdbThreadId get_threadid(Task* t) { - return GdbThreadId(t->tgid(), t->rec_tid); -} - -static bool matches_threadid(const GdbThreadId& tid, +static bool matches_threadid(const ExtendedTaskId& tid, const GdbThreadId& target) { - return (target.pid <= 0 || target.pid == tid.pid) && - (target.tid <= 0 || target.tid == tid.tid); + return (target.pid <= 0 || target.pid == tid.tguid.tid()) && + (target.tid <= 0 || target.tid == tid.tuid.tid()); } static bool matches_threadid(Task* t, const GdbThreadId& target) { - GdbThreadId tid = get_threadid(t); - return matches_threadid(tid, target); + return matches_threadid(extended_task_id(t), target); } static WatchType watchpoint_type(GdbRequestType req) { @@ -174,7 +182,8 @@ static void maybe_singlestep_for_event(Task* t, GdbRequest* req) { *req = GdbRequest(DREQ_CONT); req->suppress_debugger_stop = true; req->cont().actions.push_back( - GdbContAction(ACTION_STEP, get_threadid(t->session(), t->tuid()))); + GdbContAction(ACTION_STEP, + extended_task_id(t).to_debugger_thread_id())); } } @@ -298,7 +307,7 @@ static vector thread_info(const Session& sessio vector threads; for (auto& kv : session.tasks()) { threads.push_back({ - get_threadid(session, kv.second->tuid()), + extended_task_id(kv.second), kv.second->regs().ip().register_value() }); } @@ -306,7 +315,7 @@ static vector thread_info(const Session& sessio } void GdbServer::notify_stop_internal(const Session& session, - GdbThreadId which, int sig, + ExtendedTaskId which, int sig, const char *reason) { dbg->notify_stop(which, sig, thread_info(session), reason); } @@ -322,34 +331,27 @@ void GdbServer::dispatch_debugger_request(Session& session, DEBUG_ASSERT(false); return; // unreached case DREQ_GET_CURRENT_THREAD: - dbg->reply_get_current_thread(get_threadid(session, last_continue_tuid)); + dbg->reply_get_current_thread(last_continue_task); return; case DREQ_GET_OFFSETS: /* TODO */ dbg->reply_get_offsets(); return; case DREQ_GET_THREAD_LIST: { - vector tids; + vector tids; if (state != REPORT_THREADS_DEAD && !failed_restart) { for (auto& kv : session.tasks()) { - tids.push_back(get_threadid(session, kv.second->tuid())); + tids.push_back(extended_task_id(kv.second)); } } dbg->reply_get_thread_list(tids); return; } - case DREQ_INTERRUPT: { - Task* t = session.find_task(last_continue_tuid); - ASSERT(t, session.is_diversion()) - << "Replay interrupts should be handled at a higher level"; - DEBUG_ASSERT(!t || t->thread_group()->tguid() == debuggee_tguid); - notify_stop_internal(session, t ? get_threadid(t) : GdbThreadId(), 0); + case DREQ_INTERRUPT: + notify_stop_internal(session, last_continue_task, 0); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); - if (t) { - last_query_tuid = last_continue_tuid = t->tuid(); - } + last_query_task = last_continue_task; return; - } case DREQ_GET_EXEC_FILE: { // We shouldn't normally receive this since we try to pass the exe file // name on gdb's command line, but the user might start gdb manually @@ -362,7 +364,7 @@ void GdbServer::dispatch_debugger_request(Session& session, t = *tg->task_set().begin(); } } else { - t = session.find_task(last_continue_tuid); + t = session.find_task(last_continue_task.tuid); } if (t) { dbg->reply_get_exec_file(t->vm()->exe_image()); @@ -379,7 +381,7 @@ void GdbServer::dispatch_debugger_request(Session& session, case DREQ_FILE_OPEN: // We only support reading files if (req.file_open().flags == O_RDONLY) { - Task* t = session.find_task(last_continue_tuid); + Task* t = session.find_task(last_continue_task.tuid); int fd = open_file(session, t, req.file_open().file_name); dbg->reply_open(fd, fd >= 0 ? 0 : ENOENT); } else { @@ -468,12 +470,12 @@ void GdbServer::dispatch_debugger_request(Session& session, Task* target = req.target.tid > 0 ? session.find_task(req.target.tid) - : session.find_task(is_query ? last_query_tuid : last_continue_tuid); + : session.find_task(is_query ? last_query_task.tuid : last_continue_task.tuid); if (target) { if (is_query) { - last_query_tuid = target->tuid(); + last_query_task = extended_task_id(target); } else { - last_continue_tuid = target->tuid(); + last_continue_task = extended_task_id(target); } } // These requests query or manipulate which task is the @@ -630,8 +632,7 @@ void GdbServer::dispatch_debugger_request(Session& session, return; } case DREQ_GET_STOP_REASON: { - dbg->reply_get_stop_reason(get_threadid(session, last_continue_tuid), - stop_siginfo.si_signo, + dbg->reply_get_stop_reason(last_continue_task, stop_siginfo.si_signo, thread_info(session)); return; } @@ -771,12 +772,10 @@ void GdbServer::dispatch_debugger_request(Session& session, } } -static bool any_action_targets_match(const Session& session, - const TaskUid& tuid, +static bool any_action_targets_match(const ExtendedTaskId& thread, const vector& actions) { - GdbThreadId tid = get_threadid(session, tuid); - return any_of(actions.begin(), actions.end(), [tid](GdbContAction action) { - return matches_threadid(tid, action.target); + return any_of(actions.begin(), actions.end(), [&thread](GdbContAction action) { + return matches_threadid(thread, action.target); }); } @@ -807,8 +806,7 @@ bool GdbServer::diverter_process_debugger_requests( // code; selecting any other task runs the risk of resuming // replay, denying the diverted code an opportunity to complete // and end the diversion session. - if (!any_action_targets_match(diversion_session, last_continue_tuid, - actions)) { + if (!any_action_targets_match(last_continue_task, actions)) { // If none of the resumption targets match the task last // resumed, we simply choose any matching task. This ensures // that GDB (and the user) can choose an arbitrary thread to @@ -820,7 +818,7 @@ bool GdbServer::diverter_process_debugger_requests( Task* task = find_first_task_matching_target(diversion_session, actions); DEBUG_ASSERT(task != nullptr); - last_continue_tuid = task->tuid(); + last_continue_task = extended_task_id(task); } return diversion_refcount > 0; } @@ -846,7 +844,7 @@ bool GdbServer::diverter_process_debugger_requests( if (req->target.tid) { Task* next = diversion_session.find_task(req->target.tid); if (next) { - last_query_tuid = next->tuid(); + last_query_task = extended_task_id(next); } } break; @@ -864,7 +862,7 @@ bool GdbServer::diverter_process_debugger_requests( case DREQ_RR_CMD: { DEBUG_ASSERT(req->type == DREQ_RR_CMD); - Task* task = diversion_session.find_task(last_continue_tuid); + Task* task = diversion_session.find_task(last_continue_task.tuid); if (task) { std::string reply = GdbCommandHandler::process_command(*this, task, req->rr_cmd()); @@ -989,9 +987,9 @@ void GdbServer::maybe_notify_stop(const Session& session, if (do_stop && t->thread_group()->tguid() == debuggee_tguid) { /* Notify the debugger and process any new requests * that might have triggered before resuming. */ - notify_stop_internal(session, get_threadid(t), stop_siginfo.si_signo, + notify_stop_internal(session, extended_task_id(t), stop_siginfo.si_signo, watch); - last_query_tuid = last_continue_tuid = t->tuid(); + last_query_task = last_continue_task = extended_task_id(t); } } @@ -1067,8 +1065,8 @@ GdbRequest GdbServer::divert(ReplaySession& replay) { } DiversionSession::shr_ptr diversion_session = replay.clone_diversion(); uint32_t diversion_refcount = 1; - TaskUid saved_query_tuid = last_query_tuid; - TaskUid saved_continue_tuid = last_continue_tuid; + ExtendedTaskId saved_query_task = last_query_task; + ExtendedTaskId saved_continue_task = last_continue_task; while (diverter_process_debugger_requests(*diversion_session, diversion_refcount, &req)) { @@ -1077,14 +1075,13 @@ GdbRequest GdbServer::divert(ReplaySession& replay) { if (req.cont().run_direction == RUN_BACKWARD) { // We don't support reverse execution in a diversion. Just issue // an immediate stop. - notify_stop_internal(*diversion_session, - get_threadid(*diversion_session, last_continue_tuid), 0); + notify_stop_internal(*diversion_session, last_continue_task, 0); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); - last_query_tuid = last_continue_tuid; + last_query_task = last_continue_task; continue; } - Task* t = diversion_session->find_task(last_continue_tuid); + Task* t = diversion_session->find_task(last_continue_task.tuid); DEBUG_ASSERT(t != nullptr); int signal_to_deliver; @@ -1115,8 +1112,8 @@ GdbRequest GdbServer::divert(ReplaySession& replay) { diversion_session->kill_all_tasks(); - last_query_tuid = saved_query_tuid; - last_continue_tuid = saved_continue_tuid; + last_query_task = saved_query_task; + last_continue_task = saved_continue_task; return req; } @@ -1153,7 +1150,7 @@ GdbRequest GdbServer::process_debugger_requests(ReportState state) { } if (req.is_resume_request()) { - Task* t = current_session().find_task(last_continue_tuid); + Task* t = current_session().find_task(last_continue_task.tuid); if (t) { maybe_singlestep_for_event(t, &req); } @@ -1305,7 +1302,7 @@ GdbServer::ContinueOrStop GdbServer::debug_one_step( if (t->thread_group()->tguid() == debuggee_tguid) { interrupt_pending = false; notify_stop_internal(timeline.current_session(), - get_threadid(t), in_debuggee_end_state ? SIGKILL : 0); + extended_task_id(t), in_debuggee_end_state ? SIGKILL : 0); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); return CONTINUE_DEBUGGING; } @@ -1317,7 +1314,7 @@ GdbServer::ContinueOrStop GdbServer::debug_one_step( exit_sigkill_pending = false; if (req.cont().run_direction == RUN_FORWARD) { notify_stop_internal(timeline.current_session(), - get_threadid(t), SIGKILL); + extended_task_id(t), SIGKILL); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); return CONTINUE_DEBUGGING; } @@ -1375,10 +1372,10 @@ GdbServer::ContinueOrStop GdbServer::debug_one_step( result = timeline.reverse_continue(stop_filter, interrupt_check); break; case RUN_SINGLESTEP: { - Task* t = timeline.current_session().find_task(last_continue_tuid); + Task* t = timeline.current_session().find_task(last_continue_task.tuid); DEBUG_ASSERT(t); result = timeline.reverse_singlestep( - last_continue_tuid, t->tick_count(), stop_filter, interrupt_check); + last_continue_task.tuid, t->tick_count(), stop_filter, interrupt_check); break; } default: @@ -1489,7 +1486,7 @@ void GdbServer::activate_debugger() { target.require_exec = false; target.event = completed_event; - last_query_tuid = last_continue_tuid = t->tuid(); + last_query_task = last_continue_task = extended_task_id(t); // Have the "checkpoint" be the original replay // session, and then switch over to using the cloned @@ -1499,9 +1496,9 @@ void GdbServer::activate_debugger() { const char* where = "???"; if (timeline.can_add_checkpoint()) { debugger_restart_checkpoint = - Checkpoint(timeline, last_continue_tuid, Checkpoint::EXPLICIT, where); + Checkpoint(timeline, last_continue_task, Checkpoint::EXPLICIT, where); } else { - debugger_restart_checkpoint = Checkpoint(timeline, last_continue_tuid, + debugger_restart_checkpoint = Checkpoint(timeline, last_continue_task, Checkpoint::NOT_EXPLICIT, where); } } @@ -1601,8 +1598,8 @@ void GdbServer::restart_session(const GdbRequest& req) { if (checkpoint_to_restore.mark) { timeline.seek_to_mark(checkpoint_to_restore.mark); - last_query_tuid = last_continue_tuid = - checkpoint_to_restore.last_continue_tuid; + last_query_task = last_continue_task = + checkpoint_to_restore.last_continue_task; if (debugger_restart_checkpoint.is_explicit == Checkpoint::EXPLICIT) { timeline.remove_explicit_checkpoint(debugger_restart_checkpoint.mark); } diff --git a/src/GdbServer.h b/src/GdbServer.h index 5d25a13347b..cf58a4eb5fd 100644 --- a/src/GdbServer.h +++ b/src/GdbServer.h @@ -61,18 +61,7 @@ class GdbServer { /** * Create a gdbserver serving the replay of 'session'. */ - GdbServer(std::shared_ptr session, const Target& target) - : target(target), - final_event(UINT32_MAX), - in_debuggee_end_state(false), - failed_restart(false), - stop_replaying_to_target(false), - interrupt_pending(false), - exit_sigkill_pending(false), - timeline(std::move(session)), - emergency_debug_session(nullptr) { - memset(&stop_siginfo, 0, sizeof(stop_siginfo)); - } + GdbServer(std::shared_ptr session, const Target& target); /** * Actually run the server. Returns only when the debugger disconnects. @@ -174,7 +163,7 @@ class GdbServer { const BreakStatus& break_status); void notify_stop_internal(const Session& session, - GdbThreadId which, int sig, + ExtendedTaskId which, int sig, const char *reason = nullptr); /** @@ -202,10 +191,10 @@ class GdbServer { #ifdef PROC_SERVICE_H std::unique_ptr thread_db; #endif - // The TaskUid of the last continued task. - TaskUid last_continue_tuid; - // The TaskUid of the last queried task. - TaskUid last_query_tuid; + // The last continued task. + ExtendedTaskId last_continue_task; + // The last queried task. + ExtendedTaskId last_query_task; FrameTime final_event; // siginfo for last notified stop. siginfo_t stop_siginfo; @@ -225,9 +214,9 @@ class GdbServer { struct Checkpoint { enum Explicit { EXPLICIT, NOT_EXPLICIT }; - Checkpoint(ReplayTimeline& timeline, TaskUid last_continue_tuid, Explicit e, + Checkpoint(ReplayTimeline& timeline, ExtendedTaskId last_continue_task, Explicit e, const std::string& where) - : last_continue_tuid(last_continue_tuid), is_explicit(e), where(where) { + : last_continue_task(last_continue_task), is_explicit(e), where(where) { if (e == EXPLICIT) { mark = timeline.add_explicit_checkpoint(); } else { @@ -236,7 +225,7 @@ class GdbServer { } Checkpoint() : is_explicit(NOT_EXPLICIT) {} ReplayTimeline::Mark mark; - TaskUid last_continue_tuid; + ExtendedTaskId last_continue_task; Explicit is_explicit; std::string where; }; diff --git a/src/GdbServerConnection.cc b/src/GdbServerConnection.cc index 4c873d1e2c8..72083b43dd0 100644 --- a/src/GdbServerConnection.cc +++ b/src/GdbServerConnection.cc @@ -58,8 +58,8 @@ static bool request_needs_immediate_response(const GdbRequest* req) { } #endif -GdbServerConnection::GdbServerConnection(pid_t tgid, const Features& features) - : tgid(tgid), +GdbServerConnection::GdbServerConnection(ThreadGroupUid tguid, const Features& features) + : tguid(tguid), cpu_features_(0), no_ack(false), features_(features), @@ -107,29 +107,10 @@ static uint32_t get_cpu_features(SupportedArch arch) { return cpu_features; } -/** - * Wait for exactly one debugger host to connect to this remote target on - * the specified IP address |host|, port |port|. If |probe| is nonzero, - * a unique port based on |start_port| will be searched for. Otherwise, - * if |port| is already bound, this function will fail. - * - * Pass the |tgid| of the task on which this debug-connection request - * is being made. The remaining debugging session will be limited to - * traffic regarding |tgid|, but clients don't need to and shouldn't - * need to assume that. - * - * If we're opening this connection on behalf of a known client, pass - * an fd in |client_params_fd|; we'll write the allocated port and |exe_image| - * through the fd before waiting for a connection. |exe_image| is the - * process that will be debugged by the client, or null ptr if there isn't - * a client. - * - * This function is infallible: either it will return a valid - * debugging context, or it won't return. - */ unique_ptr GdbServerConnection::await_connection( Task* t, ScopedFd& listen_fd, const GdbServerConnection::Features& features) { - auto dbg = unique_ptr(new GdbServerConnection(t->tgid(), features)); + auto dbg = unique_ptr( + new GdbServerConnection(t->thread_group()->tguid(), features)); dbg->set_cpu_features(get_cpu_features(t->arch())); dbg->await_debugger(listen_fd); return dbg; @@ -1692,7 +1673,7 @@ static int to_gdb_signum(int sig) { } } -void GdbServerConnection::send_stop_reply_packet(GdbThreadId thread, int sig, +void GdbServerConnection::send_stop_reply_packet(ExtendedTaskId thread, int sig, const vector& threads, const char *reason) { if (sig < 0) { @@ -1710,19 +1691,19 @@ void GdbServerConnection::send_stop_reply_packet(GdbThreadId thread, int sig, sstr << "threads:"; bool first = true; for (const auto& thread : threads) { - if (thread.id.pid != tgid) { + if (thread.id.tguid != tguid) { continue; } if (!first) { sstr << ","; } first = false; - sstr << thread.id.tid; + sstr << thread.id.tuid.tid(); } sstr << ";thread-pcs:"; first = true; for (const auto& thread : threads) { - if (thread.id.pid != tgid) { + if (thread.id.tguid != tguid) { continue; } if (!first) { @@ -1737,7 +1718,7 @@ void GdbServerConnection::send_stop_reply_packet(GdbThreadId thread, int sig, write_packet(sstr.str().c_str()); } -void GdbServerConnection::notify_stop(GdbThreadId thread, int sig, +void GdbServerConnection::notify_stop(ExtendedTaskId thread, int sig, const vector& threads, const char *reason) { DEBUG_ASSERT(req.is_resume_request() || req.type == DREQ_INTERRUPT); @@ -1750,9 +1731,9 @@ void GdbServerConnection::notify_stop(GdbThreadId thread, int sig, return; } - if (tgid != thread.pid) { + if (tguid != thread.tguid) { LOG(debug) << "ignoring stop of " << thread - << " because we're debugging tgid " << tgid; + << " because we're debugging tgid " << tguid.tid(); // Re-use the existing continue request to advance to // the next stop we're willing to tell gdb about. return; @@ -1775,7 +1756,7 @@ void GdbServerConnection::notify_stop(GdbThreadId thread, int sig, } else { LOG(debug) << "Setting query/resume_thread to " << thread << " after forward continue or interrupt"; - query_thread = resume_thread = thread; + query_thread = resume_thread = thread.to_debugger_thread_id(); } consume_request(); @@ -1791,17 +1772,18 @@ void GdbServerConnection::notify_restart_failed() { consume_request(); } -string GdbServerConnection::format_thread_id(GdbThreadId thread) { +string GdbServerConnection::format_thread_id(ExtendedTaskId thread) { char buf[32]; if (multiprocess_supported_) { - snprintf(buf, sizeof(buf), "p%x.%x", thread.pid, thread.tid); + snprintf(buf, sizeof(buf), "p%x.%x", thread.tguid.tid(), + thread.tuid.tid()); } else { - snprintf(buf, sizeof(buf), "%x", thread.tid); + snprintf(buf, sizeof(buf), "%x", thread.tuid.tid()); } return buf; } -void GdbServerConnection::reply_get_current_thread(GdbThreadId thread) { +void GdbServerConnection::reply_get_current_thread(ExtendedTaskId thread) { DEBUG_ASSERT(DREQ_GET_CURRENT_THREAD == req.type); write_packet(("QC" + format_thread_id(thread)).c_str()); @@ -1997,7 +1979,7 @@ void GdbServerConnection::reply_set_reg(bool ok) { consume_request(); } -void GdbServerConnection::reply_get_stop_reason(GdbThreadId which, int sig, +void GdbServerConnection::reply_get_stop_reason(ExtendedTaskId which, int sig, const std::vector& threads) { DEBUG_ASSERT(DREQ_GET_STOP_REASON == req.type); @@ -2006,7 +1988,7 @@ void GdbServerConnection::reply_get_stop_reason(GdbThreadId which, int sig, consume_request(); } -void GdbServerConnection::reply_get_thread_list(const vector& threads) { +void GdbServerConnection::reply_get_thread_list(const vector& threads) { DEBUG_ASSERT(DREQ_GET_THREAD_LIST == req.type); if (threads.empty()) { write_packet("l"); @@ -2014,15 +1996,15 @@ void GdbServerConnection::reply_get_thread_list(const vector& threa stringstream sstr; sstr << 'm'; for (size_t i = 0; i < threads.size(); ++i) { - const GdbThreadId& t = threads[i]; - if (tgid != t.pid) { + const ExtendedTaskId& t = threads[i]; + if (tguid != t.tguid) { continue; } if (multiprocess_supported_) { - sstr << 'p' << setw(2) << setfill('0') << hex << t.pid << dec << '.' - << setw(2) << setfill('0') << hex << t.tid << ','; + sstr << 'p' << setw(2) << setfill('0') << hex << t.tguid.tid() << dec << '.' + << setw(2) << setfill('0') << hex << t.tuid.tid() << ','; } else { - sstr << setw(2) << setfill('0') << hex << t.tid << ','; + sstr << setw(2) << setfill('0') << hex << t.tuid.tid() << ','; } } diff --git a/src/GdbServerConnection.h b/src/GdbServerConnection.h index 29a6f0f426d..ce1f6b3f2c5 100644 --- a/src/GdbServerConnection.h +++ b/src/GdbServerConnection.h @@ -17,13 +17,22 @@ #include "ReplaySession.h" #include "ReplayTimeline.h" #include "ScopedFd.h" +#include "TaskishUid.h" #include "core.h" namespace rr { /** - * Descriptor for task. Note: on linux, we can uniquely identify any thread - * by its |tid| (in rr's pid namespace). + * Descriptor for task, that carries both the pid (thread-group ID) + * and the thread ID. On Linux the thread ID is unique in rr's pid namespace + * *at a specific point in time*. Thread IDs can potentially be reused + * over long periods of time. + * Also has special `ANY` and `ALL` values used by the debugger protocol. + * These values can be passed from the debugger so the task might not + * actually exist. + * Because of the special values and the fact that thread IDs can be + * reused, this is more like a pattern that can match specific tasks than + * a unique task ID. */ struct GdbThreadId { GdbThreadId(pid_t pid = -1, pid_t tid = -1) : pid(pid), tid(tid) {} @@ -44,6 +53,29 @@ inline std::ostream& operator<<(std::ostream& o, const GdbThreadId& t) { return o; } +/** + * A really-unique `TaskUid` and its `ThreadGroupUid`. This always corresponds + * to a specific task that exist(ed) somewhere in the recording, unless + * tguid and tuid are zero. + */ +struct ExtendedTaskId { + ThreadGroupUid tguid; + TaskUid tuid; + + ExtendedTaskId(ThreadGroupUid tguid, TaskUid tuid) + : tguid(tguid), tuid(tuid) {} + ExtendedTaskId() {} + + GdbThreadId to_debugger_thread_id() const { + return GdbThreadId(tguid.tid(), tuid.tid()); + } +}; + +inline std::ostream& operator<<(std::ostream& o, const ExtendedTaskId& t) { + o << t.tguid.tid() << "." << t.tuid.tid(); + return o; +} + /** * Represents a possibly-undefined register |name|. |size| indicates how * many bytes of |value| are valid, if any. @@ -389,9 +421,9 @@ class GdbServerConnection { * a unique port based on |start_port| will be searched for. Otherwise, * if |port| is already bound, this function will fail. * - * Pass the |tgid| of the task on which this debug-connection request + * Pass the `Task` on which this debug-connection request * is being made. The remaining debugging session will be limited to - * traffic regarding |tgid|, but clients don't need to and shouldn't + * traffic regarding this task, but clients don't need to and shouldn't * need to assume that. * * If we're opening this connection on behalf of a known client, pass @@ -443,7 +475,7 @@ class GdbServerConnection { void notify_exit_signal(int sig); struct ThreadInfo { - GdbThreadId id; + ExtendedTaskId id; uintptr_t pc; }; @@ -452,7 +484,7 @@ class GdbServerConnection { * target has stopped executing for some reason. |sig| is the signal * that stopped execution, or 0 if execution stopped otherwise. */ - void notify_stop(GdbThreadId which, int sig, + void notify_stop(ExtendedTaskId which, int sig, const std::vector& threads, const char *reason); @@ -462,7 +494,7 @@ class GdbServerConnection { /** * Tell the host that |thread| is the current thread. */ - void reply_get_current_thread(GdbThreadId thread); + void reply_get_current_thread(ExtendedTaskId thread); /** * Reply with the target thread's |auxv| pairs. |auxv.empty()| @@ -547,14 +579,14 @@ class GdbServerConnection { /** * Reply to the DREQ_GET_STOP_REASON request. */ - void reply_get_stop_reason(GdbThreadId which, int sig, + void reply_get_stop_reason(ExtendedTaskId which, int sig, const std::vector& threads); /** * |threads| contains the list of live threads, of which there are * |len|. */ - void reply_get_thread_list(const std::vector& threads); + void reply_get_thread_list(const std::vector& threads); /** * |ok| is true if the request was successfully applied, false if @@ -657,7 +689,7 @@ class GdbServerConnection { void set_cpu_features(uint32_t features) { cpu_features_ = features; } uint32_t cpu_features() const { return cpu_features_; } - GdbServerConnection(pid_t tgid, const Features& features); + GdbServerConnection(ThreadGroupUid tguid, const Features& features); /** * Wait for a debugger client to connect to |dbg|'s socket. Blocks @@ -742,11 +774,11 @@ class GdbServerConnection { */ bool process_packet(); void consume_request(); - void send_stop_reply_packet(GdbThreadId thread, int sig, + void send_stop_reply_packet(ExtendedTaskId thread, int sig, const std::vector& threads, const char *reason); void send_file_error_reply(int system_errno); - std::string format_thread_id(GdbThreadId thread); + std::string format_thread_id(ExtendedTaskId thread); // Current request to be processed. GdbRequest req; @@ -757,7 +789,7 @@ class GdbServerConnection { // gdb and rr don't work well together in multi-process and // multi-exe-image debugging scenarios, so we pretend only // this thread group exists when interfacing with gdb - pid_t tgid; + ThreadGroupUid tguid; uint32_t cpu_features_; // true when "no-ack mode" enabled, in which we don't have // to send ack packets back to gdb. This is a huge perf win.