[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
Mon Apr 13 07:28:18 PDT 2026
https://github.com/MaartenS11 updated https://github.com/llvm/llvm-project/pull/191183
>From cce7462eb62b5f19f276caa92609d16eb39915e4 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/13] 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 1ae0aeeba55f9..42e6e596bde1a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3870,6 +3870,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...",
@@ -5920,6 +5951,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:
@@ -5979,6 +6029,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 290577ccb889875adba601219ec41591d1858c46 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/13] 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 42e6e596bde1a..7d867651ab58d 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3887,8 +3887,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));
@@ -3897,7 +3896,6 @@ Status ProcessGDBRemote::StepBack() {
thread_plan_sp->SetIsControllingPlan(true);
thread_plan_sp->SetOkayToDiscard(false);
- GetThreadList().SetSelectedThreadByID(m_thread_ids[0]);
return Resume();
}
>From 82eb99fa7f2673e7e61cdeabdc8fd3214171fd7f 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/13] 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 7d867651ab58d..68cc210c2b85a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3870,35 +3870,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...",
@@ -5963,7 +5934,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 9b8037d19bc38beb54126afa0a830a93d9b91a6d 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/13] 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 1ddc62348d744..155d66a3c8e88 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 6774794f64580..6ba64d74d1af5 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 68cc210c2b85a..1ae0aeeba55f9 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -5920,25 +5920,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:
@@ -5998,9 +5979,6 @@ class CommandObjectProcessGDBRemotePacket : public CommandObjectMultiword {
LoadSubCommand(
"send", CommandObjectSP(
new CommandObjectProcessGDBRemotePacketSend(interpreter)));
- LoadSubCommand(
- "step-back", CommandObjectSP(
- new CommandObjectProcessGDBRemotePacketStepBack(interpreter)));
LoadSubCommand(
"monitor",
CommandObjectSP(
>From 282105e0c4986b8b103c84c44bca446c01947e61 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/13] 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 155d66a3c8e88..327a2940e5a0d 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 b6437728989940c31f00cc3d5f5a8f29107b476f 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/13] 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 a50d9e9842a3906ce25e8b1c69e541762a010b60 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/13] 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 79eed381e78b0243845665d9bfaa0e1797945719 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/13] 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 c1ca8fc0b7ac4b80c03aae988a3da56c9df447d2 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/13] 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 284d5a25750d9ec871b001692878283f09f78fbc 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/13] 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 | 53 ++++++++-------
lldb/source/Interpreter/CMakeLists.txt | 1 +
.../source/Interpreter/CommandInterpreter.cpp | 8 +--
.../Interpreter/OptionGroupDirection.cpp | 67 +++++++++++++++++++
.../lldb/source/Interpreter/BUILD.gn | 1 +
7 files changed, 139 insertions(+), 31 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 327a2940e5a0d..86b97daa899aa 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,8 @@ 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 +576,24 @@ 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,
- LLDB_OPT_SET_1);
+ 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,12 +752,8 @@ 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,
- new_plan_status);
+ 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(
true, eRunForward, abort_other_plans, bool_stop_other_threads,
@@ -819,6 +830,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 +2771,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 +2810,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 6ba64d74d1af5..7988464f5fb2a 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..2da6f641ecff8
--- /dev/null
+++ b/lldb/source/Interpreter/OptionGroupDirection.cpp
@@ -0,0 +1,67 @@
+//===-- 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."},
+};
+
+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",
>From e006c6bf00648b6bd3815dbb2b3dd505cd4092c9 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Sun, 12 Apr 2026 14:37:45 +0200
Subject: [PATCH 11/13] Use base direction if direction option is not present
---
lldb/include/lldb/Interpreter/OptionGroupDirection.h | 6 ++++--
lldb/source/Commands/CommandObjectThread.cpp | 7 +++++--
lldb/source/Interpreter/OptionGroupDirection.cpp | 2 +-
3 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/lldb/include/lldb/Interpreter/OptionGroupDirection.h b/lldb/include/lldb/Interpreter/OptionGroupDirection.h
index 7e30f432e7c48..7df55618015b1 100644
--- a/lldb/include/lldb/Interpreter/OptionGroupDirection.h
+++ b/lldb/include/lldb/Interpreter/OptionGroupDirection.h
@@ -11,6 +11,8 @@
#include "lldb/Interpreter/Options.h"
+#include <optional>
+
namespace lldb_private {
// OptionGroupDirection
@@ -28,10 +30,10 @@ class OptionGroupDirection : public OptionGroup {
void OptionParsingStarting(ExecutionContext *execution_context) override;
- lldb::RunDirection &GetDirection() { return m_direction; }
+ std::optional<lldb::RunDirection> &GetDirection() { return m_direction; }
protected:
- lldb::RunDirection m_direction;
+ std::optional<lldb::RunDirection> m_direction;
};
} // namespace lldb_private
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 86b97daa899aa..e733b0d8b4ae0 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -679,6 +679,9 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
ThreadPlanSP new_plan_sp;
Status new_plan_status;
+ RunDirection direction = m_direction_options.GetDirection().value_or(
+ process->GetBaseDirection());
+
if (m_step_type == eStepTypeInto) {
StackFrame *frame = thread->GetStackFrameAtIndex(0).get();
assert(frame != nullptr);
@@ -752,8 +755,8 @@ class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed {
new_plan_status);
} else if (m_step_type == eStepTypeTrace) {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
- false, m_direction_options.GetDirection(), abort_other_plans,
- bool_stop_other_threads, new_plan_status);
+ false, direction, abort_other_plans, bool_stop_other_threads,
+ new_plan_status);
} else if (m_step_type == eStepTypeTraceOver) {
new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction(
true, eRunForward, abort_other_plans, bool_stop_other_threads,
diff --git a/lldb/source/Interpreter/OptionGroupDirection.cpp b/lldb/source/Interpreter/OptionGroupDirection.cpp
index 2da6f641ecff8..dd77387dfe64e 100644
--- a/lldb/source/Interpreter/OptionGroupDirection.cpp
+++ b/lldb/source/Interpreter/OptionGroupDirection.cpp
@@ -59,7 +59,7 @@ OptionGroupDirection::SetOptionValue(uint32_t option_idx,
void OptionGroupDirection::OptionParsingStarting(
ExecutionContext *execution_context) {
- m_direction = eRunForward;
+ m_direction.reset();
}
llvm::ArrayRef<OptionDefinition> OptionGroupDirection::GetDefinitions() {
>From ace2694de516846142205226f77a31778266bdf0 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Sun, 12 Apr 2026 15:36:08 +0200
Subject: [PATCH 12/13] Use OptionGroupDirection for process continue
---
lldb/source/Commands/CommandObjectProcess.cpp | 31 ++++++++++---------
lldb/source/Commands/Options.td | 8 -----
2 files changed, 17 insertions(+), 22 deletions(-)
diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp
index 183163f21ac90..8b2f9afa4b731 100644
--- a/lldb/source/Commands/CommandObjectProcess.cpp
+++ b/lldb/source/Commands/CommandObjectProcess.cpp
@@ -23,6 +23,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/SaveCoreOptions.h"
@@ -440,12 +441,19 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
"Continue execution of all threads in the current process.",
"process continue",
eCommandRequiresProcess | eCommandTryTargetAPILock |
- eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
+ eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {
+ m_all_options.Append(&m_options);
+ m_all_options.Append(&m_direction_options, LLDB_OPT_SET_1,
+ LLDB_OPT_SET_1 | LLDB_OPT_SET_3);
+ m_all_options.Append(&m_direction_options, LLDB_OPT_SET_2,
+ LLDB_OPT_SET_2 | LLDB_OPT_SET_4);
+ m_all_options.Finalize();
+ }
~CommandObjectProcessContinue() override = default;
protected:
- class CommandOptions : public Options {
+ class CommandOptions : public OptionGroup {
public:
CommandOptions() {
// Keep default values of all options in one place: OptionParsingStarting
@@ -458,7 +466,8 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
ExecutionContext *exe_ctx) override {
Status error;
- const int short_option = m_getopt_table[option_idx].val;
+ const int short_option =
+ g_process_continue_options[option_idx].short_option;
switch (short_option) {
case 'i':
if (option_arg.getAsInteger(0, m_ignore))
@@ -470,12 +479,6 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
m_run_to_bkpt_args.AppendArgument(option_arg);
m_any_bkpts_specified = true;
break;
- case 'F':
- m_base_direction = lldb::RunDirection::eRunForward;
- break;
- case 'R':
- m_base_direction = lldb::RunDirection::eRunReverse;
- break;
default:
llvm_unreachable("Unimplemented option");
}
@@ -486,7 +489,6 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
m_ignore = 0;
m_run_to_bkpt_args.Clear();
m_any_bkpts_specified = false;
- m_base_direction = std::nullopt;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -496,7 +498,6 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
uint32_t m_ignore = 0;
Args m_run_to_bkpt_args;
bool m_any_bkpts_specified = false;
- std::optional<lldb::RunDirection> m_base_direction;
};
void DoExecute(Args &command, CommandReturnObject &result) override {
@@ -667,8 +668,8 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
}
}
- if (m_options.m_base_direction.has_value())
- process->SetBaseDirection(*m_options.m_base_direction);
+ if (m_direction_options.GetDirection())
+ process->SetBaseDirection(*m_direction_options.GetDirection());
const uint32_t iohandler_id = process->GetIOHandlerID();
@@ -742,9 +743,11 @@ class CommandObjectProcessContinue : public CommandObjectParsed {
}
}
- Options *GetOptions() override { return &m_options; }
+ Options *GetOptions() override { return &m_all_options; }
CommandOptions m_options;
+ OptionGroupDirection m_direction_options;
+ OptionGroupOptions m_all_options;
};
// CommandObjectProcessDetach
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 54e9f022880c4..11888942836d9 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1354,14 +1354,6 @@ let Command = "process continue" in {
Desc<"Specify a breakpoint to continue to, temporarily ignoring other "
"breakpoints. Can be specified more than once. The continue "
"action will be done synchronously if this option is specified.">;
- def thread_continue_forward
- : Option<"forward", "F">,
- Groups<[1, 3]>,
- Desc<"Set the direction to forward before continuing.">;
- def thread_continue_reverse
- : Option<"reverse", "R">,
- Groups<[2, 4]>,
- Desc<"Set the direction to reverse before continuing.">;
}
let Command = "process detach" in {
>From 7093af23d1826ccd7a0ffbff62c55422a71f0c58 Mon Sep 17 00:00:00 2001
From: MaartenS11 <maarten.steevens at gmail.com>
Date: Mon, 13 Apr 2026 16:27:11 +0200
Subject: [PATCH 13/13] Display direction in thread plan description when going
backwards
---
lldb/source/Target/ThreadPlanStepInstruction.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp
index f8e61040e6429..7904353079f96 100644
--- a/lldb/source/Target/ThreadPlanStepInstruction.cpp
+++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -74,6 +74,9 @@ void ThreadPlanStepInstruction::GetDescription(Stream *s,
else
s->Printf(" stepping into calls");
+ if (m_direction == eRunReverse)
+ s->Printf(" (direction = reverse)");
+
PrintFailureIfAny();
}
}
More information about the lldb-commits
mailing list