aboutsummaryrefslogtreecommitdiff
path: root/lldb/source
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
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')
-rw-r--r--lldb/source/API/SBProcess.cpp12
-rw-r--r--lldb/source/API/SBThread.cpp2
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp3
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp3
-rw-r--r--lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp8
-rw-r--r--lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h2
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp9
-rw-r--r--lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h2
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp20
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h6
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp1
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp98
-rw-r--r--lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h4
-rw-r--r--lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp9
-rw-r--r--lldb/source/Plugins/Process/scripted/ScriptedProcess.h2
-rw-r--r--lldb/source/Target/Process.cpp24
-rw-r--r--lldb/source/Target/StopInfo.cpp28
-rw-r--r--lldb/source/Target/Thread.cpp9
-rw-r--r--lldb/source/Target/ThreadList.cpp32
-rw-r--r--lldb/source/Target/ThreadPlanBase.cpp4
20 files changed, 246 insertions, 32 deletions
diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp
index 9773144..23ea449 100644
--- a/lldb/source/API/SBProcess.cpp
+++ b/lldb/source/API/SBProcess.cpp
@@ -583,6 +583,18 @@ SBError SBProcess::Continue() {
return sb_error;
}
+SBError SBProcess::ContinueInDirection(RunDirection direction) {
+ if (ProcessSP process_sp = GetSP()) {
+ if (direction == RunDirection::eRunReverse &&
+ !process_sp->SupportsReverseDirection())
+ return Status::FromErrorStringWithFormatv(
+ "error: {0} does not support reverse execution of processes",
+ GetPluginName());
+ process_sp->SetBaseDirection(direction);
+ }
+ return Continue();
+}
+
SBError SBProcess::Destroy() {
LLDB_INSTRUMENT_VA(this);
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index cc84807..d9469fc 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -172,6 +172,7 @@ size_t SBThread::GetStopReasonDataCount() {
case eStopReasonInstrumentation:
case eStopReasonProcessorTrace:
case eStopReasonVForkDone:
+ case eStopReasonHistoryBoundary:
// There is no data for these stop reasons.
return 0;
@@ -233,6 +234,7 @@ uint64_t SBThread::GetStopReasonDataAtIndex(uint32_t idx) {
case eStopReasonInstrumentation:
case eStopReasonProcessorTrace:
case eStopReasonVForkDone:
+ case eStopReasonHistoryBoundary:
// There is no data for these stop reasons.
return 0;
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 764dcfd..284955a 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -2557,7 +2557,8 @@ bool CommandInterpreter::DidProcessStopAbnormally() const {
const StopReason reason = stop_info->GetStopReason();
if (reason == eStopReasonException ||
reason == eStopReasonInstrumentation ||
- reason == eStopReasonProcessorTrace || reason == eStopReasonInterrupt)
+ reason == eStopReasonProcessorTrace || reason == eStopReasonInterrupt ||
+ reason == eStopReasonHistoryBoundary)
return true;
if (reason == eStopReasonSignal) {
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
index de047ee..b0aa664 100644
--- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -82,6 +82,9 @@ void LogThreadStopInfo(Log &log, const ThreadStopInfo &stop_info,
case eStopReasonProcessorTrace:
log.Printf("%s: %s processor trace", __FUNCTION__, header);
return;
+ case eStopReasonHistoryBoundary:
+ log.Printf("%s: %s history boundary", __FUNCTION__, header);
+ return;
default:
log.Printf("%s: %s invalid stop reason %" PRIu32, __FUNCTION__, header,
static_cast<uint32_t>(stop_info.reason));
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
index 9b2907c..ef57e7b 100644
--- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
+++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
@@ -402,9 +402,15 @@ lldb_private::DynamicLoader *ProcessKDP::GetDynamicLoader() {
Status ProcessKDP::WillResume() { return Status(); }
-Status ProcessKDP::DoResume() {
+Status ProcessKDP::DoResume(RunDirection direction) {
Status error;
Log *log = GetLog(KDPLog::Process);
+
+ if (direction == RunDirection::eRunReverse)
+ return Status::FromErrorStringWithFormatv(
+ "error: {0} does not support reverse execution of processes",
+ GetPluginName());
+
// Only start the async thread if we try to do any process control
if (!m_async_thread.IsJoinable())
StartAsyncThread();
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
index e5ec591..1b71d83 100644
--- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
+++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
@@ -90,7 +90,7 @@ public:
// Process Control
lldb_private::Status WillResume() override;
- lldb_private::Status DoResume() override;
+ lldb_private::Status DoResume(lldb::RunDirection direction) override;
lldb_private::Status DoHalt(bool &caused_stop) override;
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 1bdacec..7ff32ee 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -236,11 +236,18 @@ ProcessWindows::DoAttachToProcessWithID(lldb::pid_t pid,
return error;
}
-Status ProcessWindows::DoResume() {
+Status ProcessWindows::DoResume(RunDirection direction) {
Log *log = GetLog(WindowsLog::Process);
llvm::sys::ScopedLock lock(m_mutex);
Status error;
+ if (direction == RunDirection::eRunReverse) {
+ error.FromErrorStringWithFormatv(
+ "error: {0} does not support reverse execution of processes",
+ GetPluginName());
+ return error;
+ }
+
StateType private_state = GetPrivateState();
if (private_state == eStateStopped || private_state == eStateCrashed) {
LLDB_LOG(log, "process {0} is in state {1}. Resuming...",
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
index e97cfb7..97284b7 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
@@ -52,7 +52,7 @@ public:
Status DoAttachToProcessWithID(
lldb::pid_t pid,
const lldb_private::ProcessAttachInfo &attach_info) override;
- Status DoResume() override;
+ Status DoResume(lldb::RunDirection direction) override;
Status DoDestroy() override;
Status DoHalt(bool &caused_stop) override;
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index b3f1c6f..adc311c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -199,6 +199,18 @@ uint64_t GDBRemoteCommunicationClient::GetRemoteMaxPacketSize() {
return m_max_packet_size;
}
+bool GDBRemoteCommunicationClient::GetReverseContinueSupported() {
+ if (m_supports_reverse_continue == eLazyBoolCalculate)
+ GetRemoteQSupported();
+ return m_supports_reverse_continue == eLazyBoolYes;
+}
+
+bool GDBRemoteCommunicationClient::GetReverseStepSupported() {
+ if (m_supports_reverse_step == eLazyBoolCalculate)
+ GetRemoteQSupported();
+ return m_supports_reverse_step == eLazyBoolYes;
+}
+
bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() {
if (m_supports_not_sending_acks == eLazyBoolCalculate) {
m_send_acks = true;
@@ -295,6 +307,8 @@ void GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
m_supports_qXfer_siginfo_read = eLazyBoolCalculate;
m_supports_augmented_libraries_svr4_read = eLazyBoolCalculate;
m_uses_native_signals = eLazyBoolCalculate;
+ m_supports_reverse_continue = eLazyBoolCalculate;
+ m_supports_reverse_step = eLazyBoolCalculate;
m_supports_qProcessInfoPID = true;
m_supports_qfProcessInfo = true;
m_supports_qUserName = true;
@@ -348,6 +362,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_memory_tagging = eLazyBoolNo;
m_supports_qSaveCore = eLazyBoolNo;
m_uses_native_signals = eLazyBoolNo;
+ m_supports_reverse_continue = eLazyBoolNo;
+ m_supports_reverse_step = eLazyBoolNo;
m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
// not, we assume no limit
@@ -401,6 +417,10 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
m_supports_qSaveCore = eLazyBoolYes;
else if (x == "native-signals+")
m_uses_native_signals = eLazyBoolYes;
+ else if (x == "ReverseContinue+")
+ m_supports_reverse_continue = eLazyBoolYes;
+ else if (x == "ReverseStep+")
+ m_supports_reverse_step = eLazyBoolYes;
// Look for a list of compressions in the features list e.g.
// qXfer:features:read+;PacketSize=20000;qEcho+;SupportedCompressions=zlib-
// deflate,lzma
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 898d176..116b47c 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -331,6 +331,10 @@ public:
bool GetMultiprocessSupported();
+ bool GetReverseContinueSupported();
+
+ bool GetReverseStepSupported();
+
LazyBool SupportsAllocDeallocMemory() // const
{
// Uncomment this to have lldb pretend the debug server doesn't respond to
@@ -561,6 +565,8 @@ protected:
LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
LazyBool m_uses_native_signals = eLazyBoolCalculate;
+ LazyBool m_supports_reverse_continue = eLazyBoolCalculate;
+ LazyBool m_supports_reverse_step = eLazyBoolCalculate;
bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
m_supports_qUserName : 1, m_supports_qGroupName : 1,
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 8cdeaac..89d2730 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -716,6 +716,7 @@ static const char *GetStopReasonString(StopReason stop_reason) {
return "vforkdone";
case eStopReasonInterrupt:
return "async interrupt";
+ case eStopReasonHistoryBoundary:
case eStopReasonInstrumentation:
case eStopReasonInvalid:
case eStopReasonPlanComplete:
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) {
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 2492795..1cbd1e8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -111,7 +111,9 @@ public:
// Process Control
Status WillResume() override;
- Status DoResume() override;
+ bool SupportsReverseDirection() override;
+
+ Status DoResume(lldb::RunDirection direction) override;
Status DoHalt(bool &caused_stop) override;
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index d2111ce..3360bd9 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -182,10 +182,15 @@ void ScriptedProcess::DidResume() {
m_pid = GetInterface().GetProcessID();
}
-Status ScriptedProcess::DoResume() {
+Status ScriptedProcess::DoResume(RunDirection direction) {
LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s resuming process", __FUNCTION__);
- return GetInterface().Resume();
+ if (direction == RunDirection::eRunForward)
+ return GetInterface().Resume();
+ // FIXME: Pipe reverse continue through Scripted Processes
+ return Status::FromErrorStringWithFormatv(
+ "error: {0} does not support reverse execution of processes",
+ GetPluginName());
}
Status ScriptedProcess::DoAttach(const ProcessAttachInfo &attach_info) {
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
index 0335364..8ebe4ca 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
@@ -52,7 +52,7 @@ public:
void DidResume() override;
- Status DoResume() override;
+ Status DoResume(lldb::RunDirection direction) override;
Status DoAttachToProcessWithID(lldb::pid_t pid,
const ProcessAttachInfo &attach_info) override;
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index c47e728..142dd88 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -437,7 +437,8 @@ Process::Process(lldb::TargetSP target_sp, ListenerSP listener_sp,
m_mod_id(), m_process_unique_id(0), m_thread_index_id(0),
m_thread_id_to_index_id_map(), m_exit_status(-1),
m_thread_list_real(*this), m_thread_list(*this), m_thread_plans(*this),
- m_extended_thread_list(*this), m_extended_thread_stop_id(0),
+ m_extended_thread_list(*this),
+ m_base_direction(RunDirection::eRunForward), m_extended_thread_stop_id(0),
m_queue_list(this), m_queue_list_stop_id(0),
m_unix_signals_sp(unix_signals_sp), m_abi_sp(), m_process_input_reader(),
m_stdio_communication("process.stdio"), m_stdio_communication_mutex(),
@@ -845,6 +846,7 @@ bool Process::HandleProcessStateChangedEvent(
switch (thread_stop_reason) {
case eStopReasonInvalid:
case eStopReasonNone:
+ case eStopReasonHistoryBoundary:
break;
case eStopReasonSignal: {
@@ -3240,6 +3242,13 @@ Status Process::ConnectRemote(llvm::StringRef remote_url) {
return error;
}
+void Process::SetBaseDirection(RunDirection direction) {
+ if (m_base_direction == direction)
+ return;
+ m_thread_list.DiscardThreadPlans();
+ m_base_direction = direction;
+}
+
Status Process::PrivateResume() {
Log *log(GetLog(LLDBLog::Process | LLDBLog::Step));
LLDB_LOGF(log,
@@ -3266,18 +3275,25 @@ Status Process::PrivateResume() {
// (suspended/running/stepping). Threads should also check their resume
// signal in lldb::Thread::GetResumeSignal() to see if they are supposed to
// start back up with a signal.
- if (m_thread_list.WillResume()) {
+ RunDirection direction;
+ if (m_thread_list.WillResume(direction)) {
+ LLDB_LOGF(log, "Process::PrivateResume WillResume direction=%d",
+ direction);
// Last thing, do the PreResumeActions.
if (!RunPreResumeActions()) {
error = Status::FromErrorString(
"Process::PrivateResume PreResumeActions failed, not resuming.");
+ LLDB_LOGF(
+ log,
+ "Process::PrivateResume PreResumeActions failed, not resuming.");
} else {
m_mod_id.BumpResumeID();
- error = DoResume();
+ error = DoResume(direction);
if (error.Success()) {
DidResume();
m_thread_list.DidResume();
- LLDB_LOGF(log, "Process thinks the process has resumed.");
+ LLDB_LOGF(log,
+ "Process::PrivateResume thinks the process has resumed.");
} else {
LLDB_LOGF(log, "Process::PrivateResume() DoResume failed.");
return error;
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 356917a..355d3a9 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1269,6 +1269,29 @@ public:
}
};
+// StopInfoHistoryBoundary
+
+class StopInfoHistoryBoundary : public StopInfo {
+public:
+ StopInfoHistoryBoundary(Thread &thread, const char *description)
+ : StopInfo(thread, LLDB_INVALID_UID) {
+ if (description)
+ SetDescription(description);
+ }
+
+ ~StopInfoHistoryBoundary() override = default;
+
+ StopReason GetStopReason() const override {
+ return eStopReasonHistoryBoundary;
+ }
+
+ const char *GetDescription() override {
+ if (m_description.empty())
+ return "history boundary";
+ return m_description.c_str();
+ }
+};
+
// StopInfoThreadPlan
class StopInfoThreadPlan : public StopInfo {
@@ -1496,6 +1519,11 @@ StopInfoSP StopInfo::CreateStopReasonProcessorTrace(Thread &thread,
return StopInfoSP(new StopInfoProcessorTrace(thread, description));
}
+StopInfoSP StopInfo::CreateStopReasonHistoryBoundary(Thread &thread,
+ const char *description) {
+ return StopInfoSP(new StopInfoHistoryBoundary(thread, description));
+}
+
StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
return StopInfoSP(new StopInfoExec(thread));
}
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index b526131..2c4d925 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -617,7 +617,7 @@ void Thread::WillStop() {
current_plan->WillStop();
}
-bool Thread::SetupForResume() {
+bool Thread::SetupToStepOverBreakpointIfNeeded(RunDirection direction) {
if (GetResumeState() != eStateSuspended) {
// First check whether this thread is going to "actually" resume at all.
// For instance, if we're stepping from one level to the next of an
@@ -632,10 +632,11 @@ bool Thread::SetupForResume() {
// what the current plan is.
lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext());
- if (reg_ctx_sp) {
+ ProcessSP process_sp(GetProcess());
+ if (reg_ctx_sp && process_sp && direction == eRunForward) {
const addr_t thread_pc = reg_ctx_sp->GetPC();
BreakpointSiteSP bp_site_sp =
- GetProcess()->GetBreakpointSiteList().FindByAddress(thread_pc);
+ process_sp->GetBreakpointSiteList().FindByAddress(thread_pc);
if (bp_site_sp) {
// Note, don't assume there's a ThreadPlanStepOverBreakpoint, the
// target may not require anything special to step over a breakpoint.
@@ -1742,6 +1743,8 @@ std::string Thread::StopReasonAsString(lldb::StopReason reason) {
return "processor trace";
case eStopReasonInterrupt:
return "async interrupt";
+ case eStopReasonHistoryBoundary:
+ return "history boundary";
}
return "StopReason = " + std::to_string(reason);
diff --git a/lldb/source/Target/ThreadList.cpp b/lldb/source/Target/ThreadList.cpp
index 6cbef33..99e2c12 100644
--- a/lldb/source/Target/ThreadList.cpp
+++ b/lldb/source/Target/ThreadList.cpp
@@ -508,7 +508,7 @@ void ThreadList::DiscardThreadPlans() {
(*pos)->DiscardThreadPlans(true);
}
-bool ThreadList::WillResume() {
+bool ThreadList::WillResume(RunDirection &direction) {
// Run through the threads and perform their momentary actions. But we only
// do this for threads that are running, user suspended threads stay where
// they are.
@@ -566,6 +566,12 @@ bool ThreadList::WillResume() {
}
}
+ if (thread_to_run != nullptr) {
+ direction = thread_to_run->GetCurrentPlan()->GetDirection();
+ } else {
+ direction = m_process.GetBaseDirection();
+ }
+
// Give all the threads that are likely to run a last chance to set up their
// state before we negotiate who is actually going to get a chance to run...
// Don't set to resume suspended threads, and if any thread wanted to stop
@@ -577,7 +583,12 @@ bool ThreadList::WillResume() {
// "StopOthers" plans which would then get to be part of the who-gets-to-run
// negotiation, but they're coming in after the fact, and the threads that
// are already set up should take priority.
- thread_to_run->SetupForResume();
+ if (thread_to_run->SetupToStepOverBreakpointIfNeeded(direction)) {
+ // We only need to step over breakpoints when running forward, and the
+ // step-over-breakpoint plan itself wants to run forward, so this
+ // keeps our desired direction.
+ assert(thread_to_run->GetCurrentPlan()->GetDirection() == direction);
+ }
} else {
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
@@ -585,7 +596,11 @@ bool ThreadList::WillResume() {
if (thread_sp->IsOperatingSystemPluginThread() &&
!thread_sp->GetBackingThread())
continue;
- if (thread_sp->SetupForResume()) {
+ if (thread_sp->SetupToStepOverBreakpointIfNeeded(direction)) {
+ // We only need to step over breakpoints when running forward, and the
+ // step-over-breakpoint plan itself wants to run forward, so this
+ // keeps our desired direction.
+ assert(thread_sp->GetCurrentPlan()->GetDirection() == direction);
// You can't say "stop others" and also want yourself to be suspended.
assert(thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
thread_to_run = thread_sp;
@@ -626,6 +641,17 @@ bool ThreadList::WillResume() {
if (!thread_sp->ShouldResume(run_state))
need_to_resume = false;
}
+ if (need_to_resume) {
+ // Ensure all threads are running in the right direction
+ for (pos = m_threads.begin(); pos != end; ++pos) {
+ ThreadSP thread_sp(*pos);
+ while (thread_sp->GetCurrentPlan()->GetDirection() != direction) {
+ // This can't discard the base plan because its direction is
+ // m_process.GetBaseDirection() i.e. `direction`.
+ thread_sp->DiscardPlan();
+ }
+ }
+ }
} else {
for (pos = m_threads.begin(); pos != end; ++pos) {
ThreadSP thread_sp(*pos);
diff --git a/lldb/source/Target/ThreadPlanBase.cpp b/lldb/source/Target/ThreadPlanBase.cpp
index dfd2157..09437b0 100644
--- a/lldb/source/Target/ThreadPlanBase.cpp
+++ b/lldb/source/Target/ThreadPlanBase.cpp
@@ -196,3 +196,7 @@ bool ThreadPlanBase::MischiefManaged() {
// The base plan is never done.
return false;
}
+
+RunDirection ThreadPlanBase::GetDirection() const {
+ return m_process.GetBaseDirection();
+}