diff --git a/src/GdbServer.cc b/src/GdbServer.cc index 5c3cbb9dfb7..a5ce98b4bf2 100644 --- a/src/GdbServer.cc +++ b/src/GdbServer.cc @@ -294,6 +294,23 @@ void GdbServer::maybe_intercept_mem_request(Task* target, const GdbRequest& req, } } +static vector thread_info(const Session& session) { + vector threads; + for (auto& kv : session.tasks()) { + threads.push_back({ + get_threadid(session, kv.second->tuid()), + kv.second->regs().ip().register_value() + }); + } + return threads; +} + +void GdbServer::notify_stop_internal(const Session& session, + GdbThreadId which, int sig, + const char *reason) { + dbg->notify_stop(which, sig, thread_info(session), reason); +} + void GdbServer::dispatch_debugger_request(Session& session, const GdbRequest& req, ReportState state) { @@ -326,7 +343,7 @@ void GdbServer::dispatch_debugger_request(Session& session, ASSERT(t, session.is_diversion()) << "Replay interrupts should be handled at a higher level"; DEBUG_ASSERT(!t || t->thread_group()->tguid() == debuggee_tguid); - dbg->notify_stop(t ? get_threadid(t) : GdbThreadId(), 0); + notify_stop_internal(session, t ? get_threadid(t) : GdbThreadId(), 0); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); if (t) { last_query_tuid = last_continue_tuid = t->tuid(); @@ -602,7 +619,8 @@ void GdbServer::dispatch_debugger_request(Session& session, } case DREQ_GET_STOP_REASON: { dbg->reply_get_stop_reason(get_threadid(session, last_continue_tuid), - stop_siginfo.si_signo); + stop_siginfo.si_signo, + thread_info(session)); return; } case DREQ_SET_SW_BREAK: { @@ -876,7 +894,8 @@ static Task* is_in_exec(ReplayTimeline& timeline) { : nullptr; } -void GdbServer::maybe_notify_stop(const GdbRequest& req, +void GdbServer::maybe_notify_stop(const Session& session, + const GdbRequest& req, const BreakStatus& break_status) { bool do_stop = false; remote_ptr watch_addr; @@ -958,8 +977,8 @@ void GdbServer::maybe_notify_stop(const GdbRequest& req, if (do_stop && t->thread_group()->tguid() == debuggee_tguid) { /* Notify the debugger and process any new requests * that might have triggered before resuming. */ - dbg->notify_stop(get_threadid(t), stop_siginfo.si_signo, - watch); + notify_stop_internal(session, get_threadid(t), stop_siginfo.si_signo, + watch); last_query_tuid = last_continue_tuid = t->tuid(); } } @@ -1046,7 +1065,8 @@ 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. - dbg->notify_stop(get_threadid(*diversion_session, last_continue_tuid), 0); + notify_stop_internal(*diversion_session, + get_threadid(*diversion_session, last_continue_tuid), 0); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); last_query_tuid = last_continue_tuid; continue; @@ -1063,7 +1083,7 @@ GdbRequest GdbServer::divert(ReplaySession& replay) { if (result.status == DiversionSession::DIVERSION_EXITED) { diversion_refcount = 0; - maybe_notify_stop(req, result.break_status); + maybe_notify_stop(*diversion_session, req, result.break_status); if (timeline.is_running()) { // gdb assumes that the process is gone and all its // breakpoints have gone with it. It will set new breakpoints. @@ -1075,7 +1095,7 @@ GdbRequest GdbServer::divert(ReplaySession& replay) { DEBUG_ASSERT(result.status == DiversionSession::DIVERSION_CONTINUE); - maybe_notify_stop(req, result.break_status); + maybe_notify_stop(*diversion_session, req, result.break_status); } LOG(debug) << "... ending debugging diversion"; @@ -1178,7 +1198,7 @@ void GdbServer::try_lazy_reverse_singlesteps(GdbRequest& req) { break_status.task_context = TaskContext(t); break_status.singlestep_complete = true; LOG(debug) << " using lazy reverse-singlestep"; - maybe_notify_stop(req, break_status); + maybe_notify_stop(timeline.current_session(), req, break_status); while (true) { req = dbg->get_request(); @@ -1272,7 +1292,8 @@ GdbServer::ContinueOrStop GdbServer::debug_one_step( Task* t = timeline.current_session().current_task(); if (t->thread_group()->tguid() == debuggee_tguid) { interrupt_pending = false; - dbg->notify_stop(get_threadid(t), in_debuggee_end_state ? SIGKILL : 0); + notify_stop_internal(timeline.current_session(), + get_threadid(t), in_debuggee_end_state ? SIGKILL : 0); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); return CONTINUE_DEBUGGING; } @@ -1283,7 +1304,8 @@ GdbServer::ContinueOrStop GdbServer::debug_one_step( if (t->thread_group()->tguid() == debuggee_tguid) { exit_sigkill_pending = false; if (req.cont().run_direction == RUN_FORWARD) { - dbg->notify_stop(get_threadid(t), SIGKILL); + notify_stop_internal(timeline.current_session(), + get_threadid(t), SIGKILL); memset(&stop_siginfo, 0, sizeof(stop_siginfo)); return CONTINUE_DEBUGGING; } @@ -1356,7 +1378,8 @@ GdbServer::ContinueOrStop GdbServer::debug_one_step( } } if (!req.suppress_debugger_stop) { - maybe_notify_stop(req, result.break_status); + maybe_notify_stop(timeline.current_session(), + req, result.break_status); } if (req.cont().run_direction == RUN_FORWARD && is_last_thread_exit(result.break_status) && diff --git a/src/GdbServer.h b/src/GdbServer.h index 00f33754c4a..83cd89a1961 100644 --- a/src/GdbServer.h +++ b/src/GdbServer.h @@ -169,9 +169,14 @@ class GdbServer { * If |break_status| indicates a stop that we should report to gdb, * report it. |req| is the resume request that generated the stop. */ - void maybe_notify_stop(const GdbRequest& req, + void maybe_notify_stop(const Session& session, + const GdbRequest& req, const BreakStatus& break_status); + void notify_stop_internal(const Session& session, + GdbThreadId which, int sig, + const char *reason = nullptr); + /** * Return the checkpoint stored as |checkpoint_id| or nullptr if there * isn't one. diff --git a/src/GdbServerConnection.cc b/src/GdbServerConnection.cc index cc744009d53..3e19f6ee5f6 100644 --- a/src/GdbServerConnection.cc +++ b/src/GdbServerConnection.cc @@ -63,7 +63,11 @@ GdbServerConnection::GdbServerConnection(pid_t tgid, const Features& features) cpu_features_(0), no_ack(false), features_(features), - connection_alive_(true) { + connection_alive_(true), + multiprocess_supported_(false), + hwbreak_supported_(false), + swbreak_supported_(false), + list_threads_in_stop_reply_(false) { #ifndef REVERSE_EXECUTION features_.reverse_execution = false; #endif @@ -914,7 +918,6 @@ bool GdbServerConnection::set_var(char* payload) { no_ack = true; return false; } - if (!strncmp(name, "PassSignals", sizeof("PassSignals"))) { pass_signals.clear(); while (*args != '\0') { @@ -937,6 +940,11 @@ bool GdbServerConnection::set_var(char* payload) { write_packet("OK"); return false; } + if (!strcmp(name, "ListThreadsInStopReply")) { + write_packet("OK"); + list_threads_in_stop_reply_ = true; + return false; + } if (!strcmp(name, "ThreadSuffixSupported")) { write_packet("OK"); @@ -1657,24 +1665,53 @@ static int to_gdb_signum(int sig) { } void GdbServerConnection::send_stop_reply_packet(GdbThreadId thread, int sig, - const char *reason) { + const vector& threads, + const char *reason) { if (sig < 0) { write_packet("E01"); return; } - char buf[PATH_MAX]; - if (multiprocess_supported_) { - snprintf(buf, sizeof(buf) - 1, "T%02xthread:p%02x.%02x;%s", - to_gdb_signum(sig), thread.pid, thread.tid, reason); - } else { - snprintf(buf, sizeof(buf) - 1, "T%02xthread:%02x;%s", - to_gdb_signum(sig), thread.tid, reason); + stringstream sstr; + sstr << "T" << std::setfill('0') << std::setw(2) << std::hex + << to_gdb_signum(sig) << std::setw(0); + sstr << "thread:" << format_thread_id(thread) << ";"; + if (reason) { + sstr << reason; + } + if (list_threads_in_stop_reply_) { + sstr << "threads:"; + bool first = true; + for (const auto& thread : threads) { + if (thread.id.pid != tgid) { + continue; + } + if (!first) { + sstr << ","; + } + first = false; + sstr << thread.id.tid; + } + sstr << ";thread-pcs:"; + first = true; + for (const auto& thread : threads) { + if (thread.id.pid != tgid) { + continue; + } + if (!first) { + sstr << ","; + } + first = false; + sstr << thread.pc; + } + sstr << ";"; } - write_packet(buf); + + write_packet(sstr.str().c_str()); } void GdbServerConnection::notify_stop(GdbThreadId thread, int sig, - const char *reason) { + const vector& threads, + const char *reason) { DEBUG_ASSERT(req.is_resume_request() || req.type == DREQ_INTERRUPT); // don't pass this signal to gdb if it is specified not to @@ -1696,7 +1733,7 @@ void GdbServerConnection::notify_stop(GdbThreadId thread, int sig, if (!reason) { reason = ""; } - send_stop_reply_packet(thread, sig, reason); + send_stop_reply_packet(thread, sig, threads, reason); // This isn't documented in the gdb remote protocol, but if we // don't do this, gdb will sometimes continue to send requests @@ -1726,17 +1763,20 @@ void GdbServerConnection::notify_restart_failed() { consume_request(); } -void GdbServerConnection::reply_get_current_thread(GdbThreadId thread) { - DEBUG_ASSERT(DREQ_GET_CURRENT_THREAD == req.type); - - char buf[1024]; +string GdbServerConnection::format_thread_id(GdbThreadId thread) { + char buf[32]; if (multiprocess_supported_) { - snprintf(buf, sizeof(buf), "QCp%02x.%02x", thread.pid, thread.tid); + snprintf(buf, sizeof(buf), "p%x.%x", thread.pid, thread.tid); } else { - snprintf(buf, sizeof(buf), "QC%02x", thread.tid); + snprintf(buf, sizeof(buf), "%x", thread.tid); } - write_packet(buf); + return buf; +} + +void GdbServerConnection::reply_get_current_thread(GdbThreadId thread) { + DEBUG_ASSERT(DREQ_GET_CURRENT_THREAD == req.type); + write_packet(("QC" + format_thread_id(thread)).c_str()); consume_request(); } @@ -1913,10 +1953,11 @@ 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(GdbThreadId which, int sig, + const std::vector& threads) { DEBUG_ASSERT(DREQ_GET_STOP_REASON == req.type); - send_stop_reply_packet(which, sig, ""); + send_stop_reply_packet(which, sig, threads, nullptr); consume_request(); } diff --git a/src/GdbServerConnection.h b/src/GdbServerConnection.h index 39c23108cc1..46a33fe0a72 100644 --- a/src/GdbServerConnection.h +++ b/src/GdbServerConnection.h @@ -441,12 +441,19 @@ class GdbServerConnection { */ void notify_exit_signal(int sig); + struct ThreadInfo { + GdbThreadId id; + uintptr_t pc; + }; + /** * Notify the host that a resume request has "finished", i.e., the * 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, const char *reason=nullptr); + void notify_stop(GdbThreadId which, int sig, + const std::vector& threads, + const char *reason); /** Notify the debugger that a restart request failed. */ void notify_restart_failed(); @@ -533,7 +540,8 @@ class GdbServerConnection { /** * Reply to the DREQ_GET_STOP_REASON request. */ - void reply_get_stop_reason(GdbThreadId which, int sig); + void reply_get_stop_reason(GdbThreadId which, int sig, + const std::vector& threads); /** * |threads| contains the list of live threads, of which there are @@ -655,8 +663,9 @@ class GdbServerConnection { */ bool is_connection_alive(); - bool hwbreak_supported() { return hwbreak_supported_; } - bool swbreak_supported() { return swbreak_supported_; } + bool hwbreak_supported() const { return hwbreak_supported_; } + bool swbreak_supported() const { return swbreak_supported_; } + bool multiprocess_supported() const { return multiprocess_supported_; } bool is_pass_signal(int sig); @@ -727,8 +736,10 @@ class GdbServerConnection { bool process_packet(); void consume_request(); void send_stop_reply_packet(GdbThreadId 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); // Current request to be processed. GdbRequest req; @@ -757,6 +768,7 @@ class GdbServerConnection { bool multiprocess_supported_; // client supports multiprocess extension bool hwbreak_supported_; // client supports hwbreak extension bool swbreak_supported_; // client supports swbreak extension + bool list_threads_in_stop_reply_; // client requested threads: and thread-pcs: in stop replies }; } // namespace rr