[Lldb-commits] [lldb] [llvm] [lldb] Add step back single instruction for targets supporting reverse execution (PR #191183)
Maarten Steevens via lldb-commits
lldb-commits at lists.llvm.org
Sat Apr 11 07:59:57 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 01/10] 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 02/10] 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 03/10] 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 04/10] 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 05/10] 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 06/10] 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 07/10] 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 08/10] 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 5e6ac1c1bb968429c66c9127d027432bc81b74e3 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 09/10] 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 | 9 +++---
3 files changed, 19 insertions(+), 20 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..f8e61040e6429 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,8 +79,11 @@ 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.
+ if (m_direction == eRunReverse && m_step_over) {
+ error->PutCString("Step over not supported when reverse executing");
+ return false;
+ }
+
return true;
}
>From b085328babfeb571e1ab61006615140c5d970d31 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Sat, 11 Apr 2026 16:57:20 +0200
Subject: [PATCH 10/10] Add OptionGroupDirection and use it for
CommandObjectThreadStepWithTypeAndScope
TODO: Re-use it for process continue
---
.../lldb/Interpreter/OptionGroupDirection.h | 39 +++++++++++++
lldb/include/lldb/lldb-private-enumerations.h | 1 -
lldb/source/Commands/CommandObjectThread.cpp | 46 ++++++++--------
lldb/source/Interpreter/CMakeLists.txt | 1 +
.../source/Interpreter/CommandInterpreter.cpp | 8 +--
.../Interpreter/OptionGroupDirection.cpp | 55 +++++++++++++++++++
.../lldb/source/Interpreter/BUILD.gn | 1 +
7 files changed, 122 insertions(+), 29 deletions(-)
create mode 100644 lldb/include/lldb/Interpreter/OptionGroupDirection.h
create mode 100644 lldb/source/Interpreter/OptionGroupDirection.cpp
diff --git a/lldb/include/lldb/Interpreter/OptionGroupDirection.h b/lldb/include/lldb/Interpreter/OptionGroupDirection.h
new file mode 100644
index 0000000000000..7e30f432e7c48
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/OptionGroupDirection.h
@@ -0,0 +1,39 @@
+//===-- OptionGroupDirection.h --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_INTERPRETER_OPTIONGROUPDIRECTION_H
+#define LLDB_INTERPRETER_OPTIONGROUPDIRECTION_H
+
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+// OptionGroupDirection
+
+class OptionGroupDirection : public OptionGroup {
+public:
+ OptionGroupDirection();
+
+ ~OptionGroupDirection() override = default;
+
+ llvm::ArrayRef<OptionDefinition> GetDefinitions() override;
+
+ Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) override;
+
+ void OptionParsingStarting(ExecutionContext *execution_context) override;
+
+ lldb::RunDirection &GetDirection() { return m_direction; }
+
+protected:
+ lldb::RunDirection m_direction;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_OPTIONGROUPDIRECTION_H
diff --git a/lldb/include/lldb/lldb-private-enumerations.h b/lldb/include/lldb/lldb-private-enumerations.h
index db26f53f4056b..a6965657f5bc9 100644
--- a/lldb/include/lldb/lldb-private-enumerations.h
+++ b/lldb/include/lldb/lldb-private-enumerations.h
@@ -21,7 +21,6 @@ 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/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index ec211eb8d21fc..25cf87f34a9a7 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -20,6 +20,7 @@
#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupDirection.h"
#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
#include "lldb/Interpreter/Options.h"
#include "lldb/Symbol/CompileUnit.h"
@@ -565,7 +566,7 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
CommandObjectThreadStepWithTypeAndScope(CommandInterpreter &interpreter,
const char *name, const char *help,
const char *syntax,
- StepType step_type)
+ StepType step_type, bool allow_reverse)
: CommandObjectParsed(interpreter, name, help, syntax,
eCommandRequiresProcess | eCommandRequiresThread |
eCommandTryTargetAPILock |
@@ -574,11 +575,22 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
m_step_type(step_type), m_class_options("scripted step") {
AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatOptional);
- if (step_type == eStepTypeScripted) {
- m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+ if (allow_reverse) {
+ m_all_options.Append(&m_direction_options);
+
+ // Reverse is a separate option group, all other options should also exist in that group.
+ if (step_type == eStepTypeScripted) {
+ m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ }
+ m_all_options.Append(&m_options, LLDB_OPT_SET_1, LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
+ } else {
+ if (step_type == eStepTypeScripted) {
+ m_all_options.Append(&m_class_options, LLDB_OPT_SET_1 | LLDB_OPT_SET_2,
LLDB_OPT_SET_1);
+ }
+ m_all_options.Append(&m_options);
}
- m_all_options.Append(&m_options);
m_all_options.Finalize();
}
@@ -737,11 +749,7 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
new_plan_status);
} else if (m_step_type == eStepTypeTrace) {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- 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,
+ false, m_direction_options.GetDirection(), abort_other_plans, bool_stop_other_threads,
new_plan_status);
} else if (m_step_type == eStepTypeTraceOver) {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
@@ -819,6 +827,7 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
StepType m_step_type;
ThreadStepScopeOptionGroup m_options;
+ OptionGroupDirection m_direction_options;
OptionGroupPythonClassWithDict m_class_options;
OptionGroupOptions m_all_options;
};
@@ -2759,42 +2768,35 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread(
interpreter, "thread step-in",
"Source level single step, stepping into calls. Defaults "
"to current thread unless specified.",
- nullptr, eStepTypeInto)));
+ nullptr, eStepTypeInto, false)));
LoadSubCommand("step-out",
CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
interpreter, "thread step-out",
"Finish executing the current stack frame and stop after "
"returning. Defaults to current thread unless specified.",
- nullptr, eStepTypeOut)));
+ nullptr, eStepTypeOut, false)));
LoadSubCommand("step-over",
CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
interpreter, "thread step-over",
"Source level single step, stepping over calls. Defaults "
"to current thread unless specified.",
- nullptr, eStepTypeOver)));
+ nullptr, eStepTypeOver, false)));
LoadSubCommand("step-inst",
CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
interpreter, "thread step-inst",
"Instruction level single step, stepping into calls. "
"Defaults to current thread unless specified.",
- nullptr, eStepTypeTrace)));
-
- LoadSubCommand("step-back-inst",
- CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
- interpreter, "thread step-back-inst",
- "Instruction level back step. "
- "Defaults to current thread unless specified.",
- nullptr, eStepTypeTraceBack)));
+ nullptr, eStepTypeTrace, true)));
LoadSubCommand("step-inst-over",
CommandObjectSP(new CommandObjectThreadStepWithTypeAndScope(
interpreter, "thread step-inst-over",
"Instruction level single step, stepping over calls. "
"Defaults to current thread unless specified.",
- nullptr, eStepTypeTraceOver)));
+ nullptr, eStepTypeTraceOver, false)));
LoadSubCommand(
"step-scripted",
@@ -2805,7 +2807,7 @@ CommandObjectMultiwordThread::CommandObjectMultiwordThread(
"that will be used to populate an SBStructuredData Dictionary, which "
"will be passed to the constructor of the class implementing the "
"scripted step. See the Python Reference for more details.",
- nullptr, eStepTypeScripted)));
+ nullptr, eStepTypeScripted, false)));
LoadSubCommand("plan", CommandObjectSP(new CommandObjectMultiwordThreadPlan(
interpreter)));
diff --git a/lldb/source/Interpreter/CMakeLists.txt b/lldb/source/Interpreter/CMakeLists.txt
index 8af7373702c38..44b48c3f18456 100644
--- a/lldb/source/Interpreter/CMakeLists.txt
+++ b/lldb/source/Interpreter/CMakeLists.txt
@@ -18,6 +18,7 @@ add_lldb_library(lldbInterpreter NO_PLUGIN_DEPENDENCIES
OptionArgParser.cpp
OptionGroupArchitecture.cpp
OptionGroupBoolean.cpp
+ OptionGroupDirection.cpp
OptionGroupFile.cpp
OptionGroupFormat.cpp
OptionGroupMemoryTag.cpp
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 9b13032b0dcdb..3881afea72c12 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -333,12 +333,8 @@ void CommandInterpreter::Initialize() {
if (cmd_obj_sp) {
AddAlias("stepi", cmd_obj_sp);
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);
+ AddAlias("stepbi", cmd_obj_sp, "--reverse");
+ AddAlias("sbi", cmd_obj_sp, "--reverse");
}
cmd_obj_sp = GetCommandSPExact("thread step-inst-over");
diff --git a/lldb/source/Interpreter/OptionGroupDirection.cpp b/lldb/source/Interpreter/OptionGroupDirection.cpp
new file mode 100644
index 0000000000000..5b234fc64bbc3
--- /dev/null
+++ b/lldb/source/Interpreter/OptionGroupDirection.cpp
@@ -0,0 +1,55 @@
+//===-- OptionGroupDirection.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Interpreter/OptionGroupDirection.h"
+
+#include "lldb/Host/OptionParser.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static constexpr OptionDefinition g_direction_options[] = {
+ {LLDB_OPT_SET_1, false, "forward", 'F',
+ OptionParser::eNoArgument, nullptr, {}, eNoCompletion, eArgTypeNone,
+ "Forward execute the operation."},
+ {LLDB_OPT_SET_2, false, "reverse", 'R',
+ OptionParser::eNoArgument, nullptr, {}, eNoCompletion, eArgTypeNone,
+ "Reverse execute the operation."},
+};
+
+//{LLDB_OPT_SET_1 | LLDB_OPT_SET_3, false, "forward", 'F', OptionParser::eNoArgument, nullptr, {}, CompletionType::eNoCompletion, eArgTypeNone, "Set the direction to forward before continuing."},
+//{LLDB_OPT_SET_2 | LLDB_OPT_SET_4, false, "reverse", 'R', OptionParser::eNoArgument, nullptr, {}, CompletionType::eNoCompletion, eArgTypeNone, "Set the direction to reverse before continuing."},
+
+OptionGroupDirection::OptionGroupDirection()
+ : m_direction(eRunForward) {
+}
+
+Status OptionGroupDirection::SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
+ ExecutionContext *execution_context) {
+ Status error;
+ char short_option = g_direction_options[option_idx].short_option;
+ switch (short_option) {
+ case 'F':
+ m_direction = lldb::RunDirection::eRunForward;
+ break;
+ case 'R':
+ m_direction = lldb::RunDirection::eRunReverse;
+ break;
+ default:
+ llvm_unreachable("Unimplemented option");
+ }
+ return error;
+}
+
+void OptionGroupDirection::OptionParsingStarting(ExecutionContext *execution_context) {
+ m_direction = eRunForward;
+}
+
+llvm::ArrayRef<OptionDefinition> OptionGroupDirection::GetDefinitions() {
+ return g_direction_options;
+}
diff --git a/llvm/utils/gn/secondary/lldb/source/Interpreter/BUILD.gn b/llvm/utils/gn/secondary/lldb/source/Interpreter/BUILD.gn
index c6c6ef994c28d..a40066077246d 100644
--- a/llvm/utils/gn/secondary/lldb/source/Interpreter/BUILD.gn
+++ b/llvm/utils/gn/secondary/lldb/source/Interpreter/BUILD.gn
@@ -38,6 +38,7 @@ static_library("Interpreter") {
"OptionArgParser.cpp",
"OptionGroupArchitecture.cpp",
"OptionGroupBoolean.cpp",
+ "OptionGroupDirection.cpp"
"OptionGroupFile.cpp",
"OptionGroupFormat.cpp",
"OptionGroupMemoryTag.cpp",
More information about the lldb-commits
mailing list