aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
diff options
context:
space:
mode:
authorRobert O'Callahan <rocallahan@google.com>2025-01-22 20:37:17 +1300
committerGitHub <noreply@github.com>2025-01-22 08:37:17 +0100
commitb7b9ccf44988edf49886743ae5c3cf4184db211f (patch)
tree23a42c4f7287081feb4b958f53c1a7f9f49e9d08 /lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
parent830bd0e8f263c6efcfd37f38cc621b0476582b83 (diff)
downloadllvm-b7b9ccf44988edf49886743ae5c3cf4184db211f.zip
llvm-b7b9ccf44988edf49886743ae5c3cf4184db211f.tar.gz
llvm-b7b9ccf44988edf49886743ae5c3cf4184db211f.tar.bz2
[lldb] Implement basic support for reverse-continue (#112079)
This commit adds support for a `SBProcess::ContinueInDirection()` API. A user-accessible command for this will follow in a later commit. This feature depends on a gdbserver implementation (e.g. `rr`) providing support for the `bc` and `bs` packets. `lldb-server` does not support those packets, and there is no plan to change that. For testing purposes, this commit adds a Python implementation of *very limited* record-and-reverse-execute functionality, implemented as a proxy between lldb and lldb-server in `lldbreverse.py`. This should not (and in practice cannot) be used for anything except testing. The tests here are quite minimal but we test that simple breakpoints and watchpoints work as expected during reverse execution, and that conditional breakpoints and watchpoints work when the condition calls a function that must be executed in the forward direction.
Diffstat (limited to 'lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp')
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp98
1 files changed, 85 insertions, 13 deletions
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 538c868..fa511af 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -169,6 +169,8 @@ public:
}
};
+std::chrono::seconds ResumeTimeout() { return std::chrono::seconds(5); }
+
} // namespace
static PluginProperties &GetGlobalPluginProperties() {
@@ -1180,10 +1182,16 @@ Status ProcessGDBRemote::WillResume() {
return Status();
}
-Status ProcessGDBRemote::DoResume() {
+bool ProcessGDBRemote::SupportsReverseDirection() {
+ return m_gdb_comm.GetReverseStepSupported() ||
+ m_gdb_comm.GetReverseContinueSupported();
+}
+
+Status ProcessGDBRemote::DoResume(RunDirection direction) {
Status error;
Log *log = GetLog(GDBRLog::Process);
- LLDB_LOGF(log, "ProcessGDBRemote::Resume()");
+ LLDB_LOGF(log, "ProcessGDBRemote::Resume(%s)",
+ direction == RunDirection::eRunForward ? "" : "reverse");
ListenerSP listener_sp(
Listener::MakeListener("gdb-remote.resume-packet-sent"));
@@ -1197,12 +1205,24 @@ Status ProcessGDBRemote::DoResume() {
StreamString continue_packet;
bool continue_packet_error = false;
- if (m_gdb_comm.HasAnyVContSupport()) {
+ // Number of threads continuing with "c", i.e. continuing without a signal
+ // to deliver.
+ const size_t num_continue_c_tids = m_continue_c_tids.size();
+ // Number of threads continuing with "C", i.e. continuing with a signal to
+ // deliver.
+ const size_t num_continue_C_tids = m_continue_C_tids.size();
+ // Number of threads continuing with "s", i.e. single-stepping.
+ const size_t num_continue_s_tids = m_continue_s_tids.size();
+ // Number of threads continuing with "S", i.e. single-stepping with a signal
+ // to deliver.
+ const size_t num_continue_S_tids = m_continue_S_tids.size();
+ if (direction == RunDirection::eRunForward &&
+ m_gdb_comm.HasAnyVContSupport()) {
std::string pid_prefix;
if (m_gdb_comm.GetMultiprocessSupported())
pid_prefix = llvm::formatv("p{0:x-}.", GetID());
- if (m_continue_c_tids.size() == num_threads ||
+ if (num_continue_c_tids == num_threads ||
(m_continue_c_tids.empty() && m_continue_C_tids.empty() &&
m_continue_s_tids.empty() && m_continue_S_tids.empty())) {
// All threads are continuing
@@ -1265,14 +1285,10 @@ Status ProcessGDBRemote::DoResume() {
} else
continue_packet_error = true;
- if (continue_packet_error) {
+ if (direction == RunDirection::eRunForward && continue_packet_error) {
// Either no vCont support, or we tried to use part of the vCont packet
// that wasn't supported by the remote GDB server. We need to try and
- // make a simple packet that can do our continue
- const size_t num_continue_c_tids = m_continue_c_tids.size();
- const size_t num_continue_C_tids = m_continue_C_tids.size();
- const size_t num_continue_s_tids = m_continue_s_tids.size();
- const size_t num_continue_S_tids = m_continue_S_tids.size();
+ // make a simple packet that can do our continue.
if (num_continue_c_tids > 0) {
if (num_continue_c_tids == num_threads) {
// All threads are resuming...
@@ -1363,9 +1379,59 @@ Status ProcessGDBRemote::DoResume() {
}
}
+ if (direction == RunDirection::eRunReverse) {
+ if (num_continue_s_tids > 0 || num_continue_S_tids > 0) {
+ if (!m_gdb_comm.GetReverseStepSupported()) {
+ LLDB_LOGF(log, "ProcessGDBRemote::DoResume: target does not "
+ "support reverse-stepping");
+ return Status::FromErrorString(
+ "target does not support reverse-stepping");
+ }
+
+ if (num_continue_S_tids > 0) {
+ LLDB_LOGF(
+ log,
+ "ProcessGDBRemote::DoResume: Signals not supported in reverse");
+ return Status::FromErrorString(
+ "can't deliver signals while running in reverse");
+ }
+
+ if (num_continue_s_tids > 1) {
+ LLDB_LOGF(log, "ProcessGDBRemote::DoResume: can't step multiple "
+ "threads in reverse");
+ return Status::FromErrorString(
+ "can't step multiple threads while reverse-stepping");
+ }
+
+ m_gdb_comm.SetCurrentThreadForRun(m_continue_s_tids.front());
+ continue_packet.PutCString("bs");
+ } else {
+ if (!m_gdb_comm.GetReverseContinueSupported()) {
+ LLDB_LOGF(log, "ProcessGDBRemote::DoResume: target does not "
+ "support reverse-continue");
+ return Status::FromErrorString(
+ "target does not support reverse-continue");
+ }
+
+ if (num_continue_C_tids > 0) {
+ LLDB_LOGF(
+ log,
+ "ProcessGDBRemote::DoResume: Signals not supported in reverse");
+ return Status::FromErrorString(
+ "can't deliver signals while running in reverse");
+ }
+
+ // All threads continue whether requested or not ---
+ // we can't change how threads ran in the past.
+ continue_packet.PutCString("bc");
+ }
+
+ continue_packet_error = false;
+ }
+
if (continue_packet_error) {
- error =
- Status::FromErrorString("can't make continue packet for this resume");
+ return Status::FromErrorString(
+ "can't make continue packet for this resume");
} else {
EventSP event_sp;
if (!m_async_thread.IsJoinable()) {
@@ -1380,7 +1446,7 @@ Status ProcessGDBRemote::DoResume() {
std::make_shared<EventDataBytes>(continue_packet.GetString());
m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue, data_sp);
- if (!listener_sp->GetEvent(event_sp, std::chrono::seconds(5))) {
+ if (!listener_sp->GetEvent(event_sp, ResumeTimeout())) {
error = Status::FromErrorString("Resume timed out.");
LLDB_LOGF(log, "ProcessGDBRemote::DoResume: Resume timed out.");
} else if (event_sp->BroadcasterIs(&m_async_broadcaster)) {
@@ -1863,6 +1929,10 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithException(
*thread_sp, description.c_str()));
handled = true;
+ } else if (reason == "history boundary") {
+ thread_sp->SetStopInfo(StopInfo::CreateStopReasonHistoryBoundary(
+ *thread_sp, description.c_str()));
+ handled = true;
} else if (reason == "exec") {
did_exec = true;
thread_sp->SetStopInfo(
@@ -2318,6 +2388,8 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
description = std::string(ostr.GetString());
} else if (key.compare("swbreak") == 0 || key.compare("hwbreak") == 0) {
reason = "breakpoint";
+ } else if (key.compare("replaylog") == 0) {
+ reason = "history boundary";
} else if (key.compare("library") == 0) {
auto error = LoadModules();
if (error) {