[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