[Lldb-commits] [lldb] [lldb] Add step back single instruction for targets supporting reverse execution (PR #191183)
Maarten Steevens via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 10 11:16:47 PDT 2026
https://github.com/MaartenS11 updated https://github.com/llvm/llvm-project/pull/191183
>From 05450f4e063810c79e27830cf30d3c16f9031210 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 10:08:17 +0200
Subject: [PATCH 1/9] Early attempt at step back with a threadplan
---
.../Process/gdb-remote/ProcessGDBRemote.cpp | 53 +++++++++++++++++++
.../Process/gdb-remote/ProcessGDBRemote.h | 2 +
2 files changed, 55 insertions(+)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index e264161c881f1..8e8885908b2aa 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3903,6 +3903,37 @@ void ProcessGDBRemote::StopAsyncThread() {
__FUNCTION__);
}
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+
+class ThreadPlanStepBackInstruction : public ThreadPlanStepInstruction {
+public:
+ ThreadPlanStepBackInstruction(Thread &thread,
+ bool step_over,
+ bool stop_other_threads,
+ Vote report_stop_vote,
+ Vote report_run_vote)
+ : ThreadPlanStepInstruction(thread, step_over, stop_other_threads, report_stop_vote, report_run_vote) {}
+
+ lldb::RunDirection GetDirection() const override {
+ return lldb::RunDirection::eRunReverse;
+ }
+};
+
+Status ProcessGDBRemote::StepBack() {
+ LLDB_LOGF(GetLog(GDBRLog::Process), "Thread count = %d", m_thread_ids.size());
+ ThreadSP thread = GetThreadList().FindThreadByID(m_thread_ids[0]);
+
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepBackInstruction(
+ *thread, false, true, eVoteNoOpinion, eVoteNoOpinion));
+ thread->QueueThreadPlan(thread_plan_sp, false);
+
+ thread_plan_sp->SetIsControllingPlan(true);
+ thread_plan_sp->SetOkayToDiscard(false);
+
+ GetThreadList().SetSelectedThreadByID(m_thread_ids[0]);
+ return Resume();
+}
+
thread_result_t ProcessGDBRemote::AsyncThread() {
Log *log = GetLog(GDBRLog::Process);
LLDB_LOGF(log, "ProcessGDBRemote::%s(pid = %" PRIu64 ") thread starting...",
@@ -5953,6 +5984,25 @@ class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed {
}
};
+class CommandObjectProcessGDBRemotePacketStepBack : public CommandObjectParsed {
+ private:
+public:
+ CommandObjectProcessGDBRemotePacketStepBack(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet step-back",
+ "Step back one instruction",
+ nullptr) {}
+
+ ~CommandObjectProcessGDBRemotePacketStepBack() override = default;
+
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ ProcessGDBRemote *process =
+ (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
+ if (process) {
+ process->StepBack();
+ }
+ }
+};
+
class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw {
private:
public:
@@ -6012,6 +6062,9 @@ class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword {
LoadSubCommand(
"send", CommandObjectSP(
new CommandObjectProcessGDBRemotePacketSend(interpreter)));
+ LoadSubCommand(
+ "step-back", CommandObjectSP(
+ new CommandObjectProcessGDBRemotePacketStepBack(interpreter)));
LoadSubCommand(
"monitor",
CommandObjectSP(
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 434c4f29201e5..5dec9b1061e77 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -255,6 +255,8 @@ class ProcessGDBRemote : public Process,
llvm::Expected<bool> SaveCore(llvm::StringRef outfile) override;
+ Status StepBack();
+
protected:
friend class ThreadGDBRemote;
friend class GDBRemoteCommunicationClient;
>From 8e188a483fa5c2d82be4a4b3246fb348edd6fc3b Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 10:24:45 +0200
Subject: [PATCH 2/9] Use the currently selected thread instead of thread 0
---
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 8e8885908b2aa..ee009679df3e2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3920,8 +3920,7 @@ class ThreadPlanStepBackInstruction : public ThreadPlanStepInstruction {
};
Status ProcessGDBRemote::StepBack() {
- LLDB_LOGF(GetLog(GDBRLog::Process), "Thread count = %d", m_thread_ids.size());
- ThreadSP thread = GetThreadList().FindThreadByID(m_thread_ids[0]);
+ ThreadSP thread = GetThreadList().GetSelectedThread();
ThreadPlanSP thread_plan_sp(new ThreadPlanStepBackInstruction(
*thread, false, true, eVoteNoOpinion, eVoteNoOpinion));
@@ -3930,7 +3929,6 @@ Status ProcessGDBRemote::StepBack() {
thread_plan_sp->SetIsControllingPlan(true);
thread_plan_sp->SetOkayToDiscard(false);
- GetThreadList().SetSelectedThreadByID(m_thread_ids[0]);
return Resume();
}
>From 00d4ed398a62489a4858027558b249943dca9e17 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 10:39:08 +0200
Subject: [PATCH 3/9] Move some code into Thread.cpp so other platforms can
also implement step back
---
lldb/include/lldb/Target/Thread.h | 7 ++++
.../Process/gdb-remote/ProcessGDBRemote.cpp | 31 +--------------
.../Process/gdb-remote/ProcessGDBRemote.h | 2 -
lldb/source/Target/Thread.cpp | 38 +++++++++++++++++++
4 files changed, 46 insertions(+), 32 deletions(-)
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 4353725ca47f6..0f86223b493f2 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -629,6 +629,13 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// An error that describes anything that went wrong
virtual Status StepOut(uint32_t frame_idx = 0);
+ /// Default implementation for stepping back one instruction.
+ ///
+ /// This function is designed to be used by commands where the
+ /// process is publicly stopped.
+ ///
+ virtual Status StepBack();
+
/// Retrieves the per-thread data area.
/// Most OSs maintain a per-thread pointer (e.g. the FS register on
/// x64), which we return the value of here.
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index ee009679df3e2..7a8482a88fa18 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3903,35 +3903,6 @@ void ProcessGDBRemote::StopAsyncThread() {
__FUNCTION__);
}
-#include "lldb/Target/ThreadPlanStepInstruction.h"
-
-class ThreadPlanStepBackInstruction : public ThreadPlanStepInstruction {
-public:
- ThreadPlanStepBackInstruction(Thread &thread,
- bool step_over,
- bool stop_other_threads,
- Vote report_stop_vote,
- Vote report_run_vote)
- : ThreadPlanStepInstruction(thread, step_over, stop_other_threads, report_stop_vote, report_run_vote) {}
-
- lldb::RunDirection GetDirection() const override {
- return lldb::RunDirection::eRunReverse;
- }
-};
-
-Status ProcessGDBRemote::StepBack() {
- ThreadSP thread = GetThreadList().GetSelectedThread();
-
- ThreadPlanSP thread_plan_sp(new ThreadPlanStepBackInstruction(
- *thread, false, true, eVoteNoOpinion, eVoteNoOpinion));
- thread->QueueThreadPlan(thread_plan_sp, false);
-
- thread_plan_sp->SetIsControllingPlan(true);
- thread_plan_sp->SetOkayToDiscard(false);
-
- return Resume();
-}
-
thread_result_t ProcessGDBRemote::AsyncThread() {
Log *log = GetLog(GDBRLog::Process);
LLDB_LOGF(log, "ProcessGDBRemote::%s(pid = %" PRIu64 ") thread starting...",
@@ -5996,7 +5967,7 @@ class CommandObjectProcessGDBRemotePacketStepBack : public CommandObjectParsed {
ProcessGDBRemote *process =
(ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
if (process) {
- process->StepBack();
+ process->GetThreadList().GetSelectedThread()->StepBack();
}
}
};
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 5dec9b1061e77..434c4f29201e5 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -255,8 +255,6 @@ class ProcessGDBRemote : public Process,
llvm::Expected<bool> SaveCore(llvm::StringRef outfile) override;
- Status StepBack();
-
protected:
friend class ThreadGDBRemote;
friend class GDBRemoteCommunicationClient;
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index c199fd236f5cd..209488ba35885 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -2331,6 +2331,44 @@ Status Thread::StepOut(uint32_t frame_idx) {
return error;
}
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+
+class ThreadPlanStepBackInstruction : public ThreadPlanStepInstruction {
+public:
+ ThreadPlanStepBackInstruction(Thread &thread,
+ bool step_over,
+ bool stop_other_threads,
+ Vote report_stop_vote,
+ Vote report_run_vote)
+ : ThreadPlanStepInstruction(thread, step_over, stop_other_threads, report_stop_vote, report_run_vote) {}
+
+ lldb::RunDirection GetDirection() const override {
+ return lldb::RunDirection::eRunReverse;
+ }
+};
+
+Status Thread::StepBack() {
+ Process *process = GetProcess().get();
+ if (!StateIsStoppedState(process->GetState(), true)) {
+ return Status::FromErrorString("process not stopped");
+ }
+
+ if (!process->SupportsReverseDirection()) {
+ return Status::FromErrorString("process does not support reverse execution");
+ }
+
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepBackInstruction(
+ *this, false, true, eVoteNoOpinion, eVoteNoOpinion));
+ QueueThreadPlan(thread_plan_sp, false);
+
+ thread_plan_sp->SetIsControllingPlan(true);
+ thread_plan_sp->SetOkayToDiscard(false);
+
+ // Why do we need to set the current thread by ID here???
+ process->GetThreadList().SetSelectedThreadByID(GetID());
+ return process->Resume();
+}
+
ValueObjectSP Thread::GetCurrentException() {
if (auto frame_sp = GetStackFrameAtIndex(0))
if (auto recognized_frame = frame_sp->GetRecognizedFrame())
>From 20deabf644f8067f7d71d45c6c096910826cbd21 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 11:16:29 +0200
Subject: [PATCH 4/9] Add thread step-back-inst + stepbi sbi alias + remove old
testing command
---
lldb/source/Commands/CommandObjectThread.cpp | 24 +++++++++++++++++++
.../source/Interpreter/CommandInterpreter.cpp | 6 +++++
.../Process/gdb-remote/ProcessGDBRemote.cpp | 22 -----------------
3 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 9f4de72fd1bf3..b03bc8343d958 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -815,6 +815,26 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
OptionGroupOptions m_all_options;
};
+// CommandObjectThreadStepBackInstruction
+
+class CommandObjectThreadStepBackInstruction : public CommandObjectParsed {
+ private:
+public:
+ CommandObjectThreadStepBackInstruction(CommandInterpreter &interpreter)
+ : CommandObjectParsed(interpreter, "process plugin packet step-back",
+ "Step back one instruction.",
+ nullptr) {}
+
+ ~CommandObjectThreadStepBackInstruction() override = default;
+
+ void DoExecute(Args &command, CommandReturnObject &result) override {
+ Process *process = m_exe_ctx.GetProcessPtr();
+ if (process) {
+ process->GetThreadList().GetSelectedThread()->StepBack();
+ }
+ }
+};
+
// CommandObjectThreadContinue
class CommandObjectThreadContinue : public CommandObjectParsed {
@@ -2774,6 +2794,10 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread(
"Defaults to current thread unless specified.",
nullptr, eStepTypeTrace)));
+ LoadSubCommand("step-back-inst",
+ CommandObjectSP(new CommandObjectThreadStepBackInstruction(
+ interpreter)));
+
LoadSubCommand("step-inst-over",
CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
interpreter, "thread step-inst-over",
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index abbc2992b19e8..9b13032b0dcdb 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -335,6 +335,12 @@ void CommandInterpreter::Initialize() {
AddAlias("si", cmd_obj_sp);
}
+ cmd_obj_sp = GetCommandSPExact("thread step-back-inst");
+ if (cmd_obj_sp) {
+ AddAlias("stepbi", cmd_obj_sp);
+ AddAlias("sbi", cmd_obj_sp);
+ }
+
cmd_obj_sp = GetCommandSPExact("thread step-inst-over");
if (cmd_obj_sp) {
AddAlias("nexti", cmd_obj_sp);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 7a8482a88fa18..e264161c881f1 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -5953,25 +5953,6 @@ class CommandObjectProcessGDBRemotePacketSend : public CommandObjectParsed {
}
};
-class CommandObjectProcessGDBRemotePacketStepBack : public CommandObjectParsed {
- private:
-public:
- CommandObjectProcessGDBRemotePacketStepBack(CommandInterpreter &interpreter)
- : CommandObjectParsed(interpreter, "process plugin packet step-back",
- "Step back one instruction",
- nullptr) {}
-
- ~CommandObjectProcessGDBRemotePacketStepBack() override = default;
-
- void DoExecute(Args &command, CommandReturnObject &result) override {
- ProcessGDBRemote *process =
- (ProcessGDBRemote *)m_interpreter.GetExecutionContext().GetProcessPtr();
- if (process) {
- process->GetThreadList().GetSelectedThread()->StepBack();
- }
- }
-};
-
class CommandObjectProcessGDBRemotePacketMonitor : public CommandObjectRaw {
private:
public:
@@ -6031,9 +6012,6 @@ class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword {
LoadSubCommand(
"send", CommandObjectSP(
new CommandObjectProcessGDBRemotePacketSend(interpreter)));
- LoadSubCommand(
- "step-back", CommandObjectSP(
- new CommandObjectProcessGDBRemotePacketStepBack(interpreter)));
LoadSubCommand(
"monitor",
CommandObjectSP(
>From 97b06d973f1a44861600277c3fcda91bce3f4c74 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 14:23:40 +0200
Subject: [PATCH 5/9] Refactor code to integerate with
QueueThreadPlanForStepSingleInstruction which now has a direction
---
lldb/include/lldb/Target/Thread.h | 4 +-
.../lldb/Target/ThreadPlanStepInstruction.h | 9 ++-
lldb/include/lldb/lldb-private-enumerations.h | 1 +
lldb/source/API/SBThread.cpp | 8 ++-
lldb/source/API/SBThreadPlan.cpp | 2 +-
lldb/source/Commands/CommandObjectThread.cpp | 43 +++++--------
.../MacOSX-DYLD/DynamicLoaderDarwin.cpp | 4 +-
.../Windows-DYLD/DynamicLoaderWindowsDYLD.cpp | 2 +-
lldb/source/Target/StopInfo.cpp | 6 +-
lldb/source/Target/Thread.cpp | 63 ++++++++-----------
.../Target/ThreadPlanStepInstruction.cpp | 14 +++--
11 files changed, 72 insertions(+), 84 deletions(-)
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index 0f86223b493f2..f8c0398a90d31 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -730,8 +730,8 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// A shared pointer to the newly queued thread plan, or nullptr if the
/// plan could not be queued.
virtual lldb::ThreadPlanSP QueueThreadPlanForStepSingleInstruction(
- bool step_over, bool abort_other_plans, bool stop_other_threads,
- Status &status);
+ bool step_over, lldb::RunDirection direction, bool abort_other_plans,
+ bool stop_other_threads, Status &status);
/// Queues the plan used to step through an address range, stepping over
/// function calls.
diff --git a/lldb/include/lldb/Target/ThreadPlanStepInstruction.h b/lldb/include/lldb/Target/ThreadPlanStepInstruction.h
index 52a5a2efc0a47..f8a3f56cb422a 100644
--- a/lldb/include/lldb/Target/ThreadPlanStepInstruction.h
+++ b/lldb/include/lldb/Target/ThreadPlanStepInstruction.h
@@ -17,7 +17,8 @@ namespace lldb_private {
class ThreadPlanStepInstruction : public ThreadPlan {
public:
- ThreadPlanStepInstruction(Thread &thread, bool step_over, bool stop_others,
+ ThreadPlanStepInstruction(Thread &thread, bool step_over,
+ lldb::RunDirection direction, bool stop_others,
Vote report_stop_vote, Vote report_run_vote);
~ThreadPlanStepInstruction() override;
@@ -30,6 +31,7 @@ class ThreadPlanStepInstruction : public ThreadPlan {
bool WillStop() override;
bool MischiefManaged() override;
bool IsPlanStale() override;
+ lldb::RunDirection GetDirection() const override;
protected:
bool DoPlanExplainsStop(Event *event_ptr) override;
@@ -38,12 +40,13 @@ class ThreadPlanStepInstruction : public ThreadPlan {
private:
friend lldb::ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction(
- bool step_over, bool abort_other_plans, bool stop_other_threads,
- Status &status);
+ bool step_over, lldb::RunDirection direction, bool abort_other_plans,
+ bool stop_other_threads, Status &status);
lldb::addr_t m_instruction_addr;
bool m_stop_other_threads;
bool m_step_over;
+ lldb::RunDirection m_direction;
// These two are used only for the step over case.
bool m_start_has_symbol;
StackID m_stack_id;
diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h
index a6965657f5bc9..db26f53f4056b 100644
--- a/lldb/include/lldb/lldb-private-enumerations.h
+++ b/lldb/include/lldb/lldb-private-enumerations.h
@@ -21,6 +21,7 @@ namespace lldb_private {
enum StepType {
eStepTypeNone,
eStepTypeTrace, ///< Single step one instruction.
+ eStepTypeTraceBack, ///< Single step back one instruction.
eStepTypeTraceOver, ///< Single step one instruction, stepping over.
eStepTypeInto, ///< Single step into a specified context.
eStepTypeOver, ///< Single step over a specified context.
diff --git a/lldb/source/API/SBThread.cpp b/lldb/source/API/SBThread.cpp
index 8f672ae539780..d1eece1eae064 100644
--- a/lldb/source/API/SBThread.cpp
+++ b/lldb/source/API/SBThread.cpp
@@ -507,7 +507,8 @@ void SBThread::StepOver(lldb::RunMode stop_other_threads, SBError &error) {
new_plan_status, avoid_no_debug);
} else {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- true, abort_other_plans, stop_other_threads, new_plan_status);
+ true, eRunForward, abort_other_plans, stop_other_threads,
+ new_plan_status);
}
}
error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
@@ -573,7 +574,8 @@ void SBThread::StepInto(const char *target_name, uint32_t end_line,
step_out_avoids_code_without_debug_info);
} else {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- false, abort_other_plans, stop_other_threads, new_plan_status);
+ false, eRunForward, abort_other_plans, stop_other_threads,
+ new_plan_status);
}
if (new_plan_status.Success())
@@ -694,7 +696,7 @@ void SBThread::StepInstruction(bool step_over, SBError &error) {
Thread *thread = exe_ctx->GetThreadPtr();
Status new_plan_status;
ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction(
- step_over, false, true, new_plan_status));
+ step_over, eRunForward, false, true, new_plan_status));
if (new_plan_status.Success())
error = ResumeNewPlan(std::move(*exe_ctx), new_plan_sp.get());
diff --git a/lldb/source/API/SBThreadPlan.cpp b/lldb/source/API/SBThreadPlan.cpp
index c8ca6c81a3efb..21d834e0674dd 100644
--- a/lldb/source/API/SBThreadPlan.cpp
+++ b/lldb/source/API/SBThreadPlan.cpp
@@ -335,7 +335,7 @@ SBThreadPlan::QueueThreadPlanForStepSingleInstruction(bool step_over,
Status plan_status;
SBThreadPlan plan(
thread_plan_sp->GetThread().QueueThreadPlanForStepSingleInstruction(
- step_over, false, false, plan_status));
+ step_over, eRunForward, false, false, plan_status));
if (plan_status.Fail())
error.SetErrorString(plan_status.AsCString());
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index b03bc8343d958..ec211eb8d21fc 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -719,7 +719,8 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
}
} else
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- false, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ false, eRunForward, abort_other_plans, bool_stop_other_threads,
+ new_plan_status);
} else if (m_step_type == eStepTypeOver) {
StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
@@ -732,13 +733,20 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
m_options.m_step_out_avoid_no_debug);
else
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- true, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ true, eRunForward, abort_other_plans, bool_stop_other_threads,
+ new_plan_status);
} else if (m_step_type == eStepTypeTrace) {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- false, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ false, eRunForward, abort_other_plans, bool_stop_other_threads,
+ new_plan_status);
+ } else if (m_step_type == eStepTypeTraceBack) {
+ new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
+ false, eRunReverse, abort_other_plans, bool_stop_other_threads,
+ new_plan_status);
} else if (m_step_type == eStepTypeTraceOver) {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- true, abort_other_plans, bool_stop_other_threads, new_plan_status);
+ true, eRunForward, abort_other_plans, bool_stop_other_threads,
+ new_plan_status);
} else if (m_step_type == eStepTypeOut) {
new_plan_sp = thread->QueueThreadPlanForStepOut(
abort_other_plans, nullptr, false, bool_stop_other_threads, eVoteYes,
@@ -815,26 +823,6 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
OptionGroupOptions m_all_options;
};
-// CommandObjectThreadStepBackInstruction
-
-class CommandObjectThreadStepBackInstruction : public CommandObjectParsed {
- private:
-public:
- CommandObjectThreadStepBackInstruction(CommandInterpreter &interpreter)
- : CommandObjectParsed(interpreter, "process plugin packet step-back",
- "Step back one instruction.",
- nullptr) {}
-
- ~CommandObjectThreadStepBackInstruction() override = default;
-
- void DoExecute(Args &command, CommandReturnObject &result) override {
- Process *process = m_exe_ctx.GetProcessPtr();
- if (process) {
- process->GetThreadList().GetSelectedThread()->StepBack();
- }
- }
-};
-
// CommandObjectThreadContinue
class CommandObjectThreadContinue : public CommandObjectParsed {
@@ -2795,8 +2783,11 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread(
nullptr, eStepTypeTrace)));
LoadSubCommand("step-back-inst",
- CommandObjectSP(new CommandObjectThreadStepBackInstruction(
- interpreter)));
+ CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
+ interpreter, "thread step-back-inst",
+ "Instruction level back step. "
+ "Defaults to current thread unless specified.",
+ nullptr, eStepTypeTraceBack)));
LoadSubCommand("step-inst-over",
CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
index afa0ef28a381d..fa11896d749e8 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -1080,8 +1080,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
if (!thread_plan_sp && is_branch_island) {
thread_plan_sp = std::make_shared<ThreadPlanStepInstruction>(
thread,
- /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion,
- eVoteNoOpinion);
+ /* step_over= */ false, eRunForward, /* stop_others */ false,
+ eVoteNoOpinion, eVoteNoOpinion);
LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.",
current_name);
}
diff --git a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
index 79fb4d46eff41..bd735ef884b8f 100644
--- a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.cpp
@@ -228,5 +228,5 @@ DynamicLoaderWindowsDYLD::GetStepThroughTrampolinePlan(Thread &thread,
assert(first_insn->DoesBranch() && !second_insn->DoesBranch());
return ThreadPlanSP(new ThreadPlanStepInstruction(
- thread, false, false, eVoteNoOpinion, eVoteNoOpinion));
+ thread, false, eRunForward, false, eVoteNoOpinion, eVoteNoOpinion));
}
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 5110ed16edc91..89c85081733c6 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -788,11 +788,11 @@ class StopInfoWatchpoint : public StopInfo {
// them and they won't behave correctly.
class ThreadPlanStepOverWatchpoint : public ThreadPlanStepInstruction {
public:
- ThreadPlanStepOverWatchpoint(Thread &thread,
+ ThreadPlanStepOverWatchpoint(Thread &thread,
StopInfoWatchpointSP stop_info_sp,
WatchpointSP watch_sp)
- : ThreadPlanStepInstruction(thread, false, true, eVoteNoOpinion,
- eVoteNoOpinion),
+ : ThreadPlanStepInstruction(thread, false, eRunForward, true,
+ eVoteNoOpinion, eVoteNoOpinion),
m_stop_info_sp(stop_info_sp), m_watch_sp(watch_sp) {
assert(watch_sp);
}
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 209488ba35885..067de6673511c 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -372,14 +372,14 @@ lldb::StopInfoSP Thread::GetStopInfo() {
// from completed plan stack - m_stop_info_sp (trace stop reason is OK now) -
// ask GetPrivateStopInfo to set stop info
- bool have_valid_stop_info = m_stop_info_sp &&
- m_stop_info_sp ->IsValid() &&
- m_stop_info_stop_id == stop_id;
- bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded();
+ bool have_valid_stop_info = m_stop_info_sp && m_stop_info_sp->IsValid() &&
+ m_stop_info_stop_id == stop_id;
+ bool have_valid_completed_plan =
+ completed_plan_sp && completed_plan_sp->PlanSucceeded();
bool plan_failed = completed_plan_sp && !completed_plan_sp->PlanSucceeded();
bool plan_overrides_trace =
- have_valid_stop_info && have_valid_completed_plan
- && (m_stop_info_sp->GetStopReason() == eStopReasonTrace);
+ have_valid_stop_info && have_valid_completed_plan &&
+ (m_stop_info_sp->GetStopReason() == eStopReasonTrace);
if (have_valid_stop_info && !plan_overrides_trace && !plan_failed) {
return m_stop_info_sp;
@@ -415,8 +415,8 @@ lldb::StopInfoSP Thread::GetPrivateStopInfo(bool calculate) {
// 4) If this thread wasn't allowed to run the last time round.
if (m_stop_info_sp) {
if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() ||
- GetCurrentPlan()->IsVirtualStep()
- || GetTemporaryResumeState() == eStateSuspended)
+ GetCurrentPlan()->IsVirtualStep() ||
+ GetTemporaryResumeState() == eStateSuspended)
SetStopInfo(m_stop_info_sp);
else
m_stop_info_sp.reset();
@@ -1204,7 +1204,7 @@ bool Thread::CompletedPlanOverridesBreakpoint() const {
return GetPlans().AnyCompletedPlans();
}
-ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const{
+ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const {
return GetPlans().GetPreviousPlan(current_plan);
}
@@ -1294,10 +1294,11 @@ ThreadPlanSP Thread::QueueBasePlan(bool abort_other_plans) {
}
ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction(
- bool step_over, bool abort_other_plans, bool stop_other_threads,
- Status &status) {
+ bool step_over, lldb::RunDirection direction, bool abort_other_plans,
+ bool stop_other_threads, Status &status) {
ThreadPlanSP thread_plan_sp(new ThreadPlanStepInstruction(
- *this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion));
+ *this, step_over, direction, stop_other_threads, eVoteNoOpinion,
+ eVoteNoOpinion));
status = QueueThreadPlan(thread_plan_sp, abort_other_plans);
return thread_plan_sp;
}
@@ -2259,7 +2260,7 @@ Status Thread::StepIn(bool source_step,
step_out_avoids_code_without_debug_info);
} else {
new_plan_sp = QueueThreadPlanForStepSingleInstruction(
- false, abort_other_plans, run_mode, error);
+ false, eRunForward, abort_other_plans, run_mode, error);
}
new_plan_sp->SetIsControllingPlan(true);
@@ -2292,7 +2293,7 @@ Status Thread::StepOver(bool source_step,
step_out_avoids_code_without_debug_info);
} else {
new_plan_sp = QueueThreadPlanForStepSingleInstruction(
- true, abort_other_plans, run_mode, error);
+ true, eRunForward, abort_other_plans, run_mode, error);
}
new_plan_sp->SetIsControllingPlan(true);
@@ -2331,22 +2332,6 @@ Status Thread::StepOut(uint32_t frame_idx) {
return error;
}
-#include "lldb/Target/ThreadPlanStepInstruction.h"
-
-class ThreadPlanStepBackInstruction : public ThreadPlanStepInstruction {
-public:
- ThreadPlanStepBackInstruction(Thread &thread,
- bool step_over,
- bool stop_other_threads,
- Vote report_stop_vote,
- Vote report_run_vote)
- : ThreadPlanStepInstruction(thread, step_over, stop_other_threads, report_stop_vote, report_run_vote) {}
-
- lldb::RunDirection GetDirection() const override {
- return lldb::RunDirection::eRunReverse;
- }
-};
-
Status Thread::StepBack() {
Process *process = GetProcess().get();
if (!StateIsStoppedState(process->GetState(), true)) {
@@ -2354,11 +2339,13 @@ Status Thread::StepBack() {
}
if (!process->SupportsReverseDirection()) {
- return Status::FromErrorString("process does not support reverse execution");
+ return Status::FromErrorString(
+ "process does not support reverse execution");
}
-
- ThreadPlanSP thread_plan_sp(new ThreadPlanStepBackInstruction(
- *this, false, true, eVoteNoOpinion, eVoteNoOpinion));
+
+ Status error;
+ ThreadPlanSP thread_plan_sp(QueueThreadPlanForStepSingleInstruction(
+ false, eRunReverse, false, true, error));
QueueThreadPlan(thread_plan_sp, false);
thread_plan_sp->SetIsControllingPlan(true);
@@ -2424,7 +2411,9 @@ lldb::ValueObjectSP Thread::GetSiginfoValue() {
return ValueObjectConstResult::Create(&target,
Status::FromError(data.takeError()));
- DataExtractor data_extractor{data.get()->getBufferStart(), data.get()->getBufferSize(),
- process_sp->GetByteOrder(), arch.GetAddressByteSize()};
- return ValueObjectConstResult::Create(&target, type, ConstString("__lldb_siginfo"), data_extractor);
+ DataExtractor data_extractor{
+ data.get()->getBufferStart(), data.get()->getBufferSize(),
+ process_sp->GetByteOrder(), arch.GetAddressByteSize()};
+ return ValueObjectConstResult::Create(
+ &target, type, ConstString("__lldb_siginfo"), data_extractor);
}
diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp
index e9db3171a5bef..ace663f59841a 100644
--- a/lldb/source/Target/ThreadPlanStepInstruction.cpp
+++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -20,16 +20,14 @@ using namespace lldb_private;
// ThreadPlanStepInstruction: Step over the current instruction
-ThreadPlanStepInstruction::ThreadPlanStepInstruction(Thread &thread,
- bool step_over,
- bool stop_other_threads,
- Vote report_stop_vote,
- Vote report_run_vote)
+ThreadPlanStepInstruction::ThreadPlanStepInstruction(
+ Thread &thread, bool step_over, lldb::RunDirection direction,
+ bool stop_other_threads, Vote report_stop_vote, Vote report_run_vote)
: ThreadPlan(ThreadPlan::eKindStepInstruction,
"Step over single instruction", thread, report_stop_vote,
report_run_vote),
m_instruction_addr(0), m_stop_other_threads(stop_other_threads),
- m_step_over(step_over) {
+ m_step_over(step_over), m_direction(direction) {
m_takes_iteration_count = true;
SetUpState();
}
@@ -246,3 +244,7 @@ bool ThreadPlanStepInstruction::MischiefManaged() {
return false;
}
}
+
+lldb::RunDirection ThreadPlanStepInstruction::GetDirection() const {
+ return m_direction;
+}
>From d723878cafb0b21af2568cc5f70ea6d712195bea Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 15:30:05 +0200
Subject: [PATCH 6/9] Add assertion to ensure we don't allow step over +
backwards execution
---
lldb/source/Target/ThreadPlanStepInstruction.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp
index ace663f59841a..6a7cd9c2920e3 100644
--- a/lldb/source/Target/ThreadPlanStepInstruction.cpp
+++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -28,6 +28,8 @@ ThreadPlanStepInstruction::ThreadPlanStepInstruction(
report_run_vote),
m_instruction_addr(0), m_stop_other_threads(stop_other_threads),
m_step_over(step_over), m_direction(direction) {
+ // We don't support step_over when the direction equals eRunReverse.
+ assert(!step_over || direction == eRunForward);
m_takes_iteration_count = true;
SetUpState();
}
>From 8ed9c67ed7fe0d68fc83f001a6c7330b430337b5 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 17:35:04 +0200
Subject: [PATCH 7/9] Stop stepping back if LLDB hits a history boundary
---
lldb/source/Target/ThreadPlanStepInstruction.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp
index 6a7cd9c2920e3..79e264c179023 100644
--- a/lldb/source/Target/ThreadPlanStepInstruction.cpp
+++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -213,6 +213,13 @@ bool ThreadPlanStepInstruction::ShouldStop(Event *event_ptr) {
}
} else {
lldb::addr_t pc_addr = thread.GetRegisterContext()->GetPC(0);
+
+ if (m_direction == eRunReverse &&
+ eStopReasonHistoryBoundary == thread.GetStopReason()) {
+ SetPlanComplete();
+ return true;
+ }
+
if (pc_addr != m_instruction_addr) {
if (--m_iteration_count <= 0) {
SetPlanComplete();
>From 114db85e8c01d12fabbf078a7caebfd9aacce50d Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Thu, 9 Apr 2026 17:53:46 +0200
Subject: [PATCH 8/9] Document the additional direction parameter for
QueueThreadPlanForStepSingleInstruction
---
lldb/include/lldb/Target/Thread.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index f8c0398a90d31..dc3d9683e9ff6 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -715,6 +715,9 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// \param[in] step_over
/// \b true if we step over calls to functions, false if we step in.
///
+ /// \param[in] direction
+ /// The direction in which a step will be taken.
+ ///
/// \param[in] abort_other_plans
/// \b true if we discard the currently queued plans and replace them with
/// this one.
>From 7afb82f315f9aa5abaf423fecc8e81e1affbb887 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Fri, 10 Apr 2026 20:03:53 +0200
Subject: [PATCH 9/9] First set of changes based on feedback
- StepBack -> StepBackInstruction
- Remove accidental formatting changes from Thread.cpp
- Use ValidatePlan instead of an assert in the constructor
---
lldb/include/lldb/Target/Thread.h | 2 +-
lldb/source/Target/Thread.cpp | 28 +++++++++----------
.../Target/ThreadPlanStepInstruction.cpp | 8 ++----
3 files changed, 17 insertions(+), 21 deletions(-)
diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h
index dc3d9683e9ff6..82b01553c5ffc 100644
--- a/lldb/include/lldb/Target/Thread.h
+++ b/lldb/include/lldb/Target/Thread.h
@@ -634,7 +634,7 @@ class Thread : public std::enable_shared_from_this<Thread>,
/// This function is designed to be used by commands where the
/// process is publicly stopped.
///
- virtual Status StepBack();
+ virtual Status StepBackInstruction();
/// Retrieves the per-thread data area.
/// Most OSs maintain a per-thread pointer (e.g. the FS register on
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 067de6673511c..c4a7749c72184 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -372,14 +372,14 @@ lldb::StopInfoSP Thread::GetStopInfo() {
// from completed plan stack - m_stop_info_sp (trace stop reason is OK now) -
// ask GetPrivateStopInfo to set stop info
- bool have_valid_stop_info = m_stop_info_sp && m_stop_info_sp->IsValid() &&
- m_stop_info_stop_id == stop_id;
- bool have_valid_completed_plan =
- completed_plan_sp && completed_plan_sp->PlanSucceeded();
+ bool have_valid_stop_info = m_stop_info_sp &&
+ m_stop_info_sp ->IsValid() &&
+ m_stop_info_stop_id == stop_id;
+ bool have_valid_completed_plan = completed_plan_sp && completed_plan_sp->PlanSucceeded();
bool plan_failed = completed_plan_sp && !completed_plan_sp->PlanSucceeded();
bool plan_overrides_trace =
- have_valid_stop_info && have_valid_completed_plan &&
- (m_stop_info_sp->GetStopReason() == eStopReasonTrace);
+ have_valid_stop_info && have_valid_completed_plan
+ && (m_stop_info_sp->GetStopReason() == eStopReasonTrace);
if (have_valid_stop_info && !plan_overrides_trace && !plan_failed) {
return m_stop_info_sp;
@@ -415,8 +415,8 @@ lldb::StopInfoSP Thread::GetPrivateStopInfo(bool calculate) {
// 4) If this thread wasn't allowed to run the last time round.
if (m_stop_info_sp) {
if (m_stop_info_sp->IsValid() || IsStillAtLastBreakpointHit() ||
- GetCurrentPlan()->IsVirtualStep() ||
- GetTemporaryResumeState() == eStateSuspended)
+ GetCurrentPlan()->IsVirtualStep()
+ || GetTemporaryResumeState() == eStateSuspended)
SetStopInfo(m_stop_info_sp);
else
m_stop_info_sp.reset();
@@ -1204,7 +1204,7 @@ bool Thread::CompletedPlanOverridesBreakpoint() const {
return GetPlans().AnyCompletedPlans();
}
-ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const {
+ThreadPlan *Thread::GetPreviousPlan(ThreadPlan *current_plan) const{
return GetPlans().GetPreviousPlan(current_plan);
}
@@ -2332,7 +2332,7 @@ Status Thread::StepOut(uint32_t frame_idx) {
return error;
}
-Status Thread::StepBack() {
+Status Thread::StepBackInstruction() {
Process *process = GetProcess().get();
if (!StateIsStoppedState(process->GetState(), true)) {
return Status::FromErrorString("process not stopped");
@@ -2411,9 +2411,7 @@ lldb::ValueObjectSP Thread::GetSiginfoValue() {
return ValueObjectConstResult::Create(&target,
Status::FromError(data.takeError()));
- DataExtractor data_extractor{
- data.get()->getBufferStart(), data.get()->getBufferSize(),
- process_sp->GetByteOrder(), arch.GetAddressByteSize()};
- return ValueObjectConstResult::Create(
- &target, type, ConstString("__lldb_siginfo"), data_extractor);
+ DataExtractor data_extractor{data.get()->getBufferStart(), data.get()->getBufferSize(),
+ process_sp->GetByteOrder(), arch.GetAddressByteSize()};
+ return ValueObjectConstResult::Create(&target, type, ConstString("__lldb_siginfo"), data_extractor);
}
diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp
index 79e264c179023..6d4bdda5e0dc3 100644
--- a/lldb/source/Target/ThreadPlanStepInstruction.cpp
+++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -28,8 +28,6 @@ ThreadPlanStepInstruction::ThreadPlanStepInstruction(
report_run_vote),
m_instruction_addr(0), m_stop_other_threads(stop_other_threads),
m_step_over(step_over), m_direction(direction) {
- // We don't support step_over when the direction equals eRunReverse.
- assert(!step_over || direction == eRunForward);
m_takes_iteration_count = true;
SetUpState();
}
@@ -81,9 +79,9 @@ void ThreadPlanStepInstruction::GetDescription(Stream *s,
}
bool ThreadPlanStepInstruction::ValidatePlan(Stream *error) {
- // Since we read the instruction we're stepping over from the thread, this
- // plan will always work.
- return true;
+ // The plan is always valid when going forward, when going backwards only step
+ // into is supported.
+ return m_direction == eRunForward || !m_step_over;
}
bool ThreadPlanStepInstruction::DoPlanExplainsStop(Event *event_ptr) {
More information about the lldb-commits
mailing list