[Lldb-commits] [lldb] Handle stepping through "lazy library" stubs (PR #225238)

via lldb-commits lldb-commits at lists.llvm.org
Tue Sep 22 11:05:11 PDT 2026


https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/225238

>From aeffc242da28a1ec1736e9fdb9ebf641aa45b5ef Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 15 Sep 2026 15:23:34 -0700
Subject: [PATCH 1/5] Handle the new dyld lazy_linking stubs on Darwin

---
 lldb/include/lldb/Target/ThreadPlan.h         |   1 +
 .../lldb/Target/ThreadPlanRunToBreakpoint.h   |  71 +++++++++
 lldb/include/lldb/Utility/StreamString.h      |   8 +
 .../MacOSX-DYLD/DynamicLoaderDarwin.cpp       |  18 ++-
 lldb/source/Target/CMakeLists.txt             |   1 +
 .../Target/ThreadPlanRunToBreakpoint.cpp      | 146 ++++++++++++++++++
 lldb/source/Utility/StreamString.cpp          |   6 +
 lldb/test/API/macosx/lazy_library/Makefile    |  15 ++
 .../lazy_library/TestStepThroughLazyLoad.py   |  30 ++++
 lldb/test/API/macosx/lazy_library/library.c   |  14 ++
 lldb/test/API/macosx/lazy_library/main.c      |  13 ++
 11 files changed, 321 insertions(+), 2 deletions(-)
 create mode 100644 lldb/include/lldb/Target/ThreadPlanRunToBreakpoint.h
 create mode 100644 lldb/source/Target/ThreadPlanRunToBreakpoint.cpp
 create mode 100644 lldb/test/API/macosx/lazy_library/Makefile
 create mode 100644 lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py
 create mode 100644 lldb/test/API/macosx/lazy_library/library.c
 create mode 100644 lldb/test/API/macosx/lazy_library/main.c

diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h
index a7bac8cc5ecf6c..a8cfc29c7b61b4 100644
--- a/lldb/include/lldb/Target/ThreadPlan.h
+++ b/lldb/include/lldb/Target/ThreadPlan.h
@@ -313,6 +313,7 @@ class ThreadPlan : public std::enable_shared_from_this<ThreadPlan>,
     eKindStepThrough,
     eKindStepUntil,
     eKindSingleThreadTimeout,
+    eKindRunToBreakpoint
   };
 
   virtual ~ThreadPlan();
diff --git a/lldb/include/lldb/Target/ThreadPlanRunToBreakpoint.h b/lldb/include/lldb/Target/ThreadPlanRunToBreakpoint.h
new file mode 100644
index 00000000000000..0ea96dea380498
--- /dev/null
+++ b/lldb/include/lldb/Target/ThreadPlanRunToBreakpoint.h
@@ -0,0 +1,71 @@
+//===-- ThreadPlanRunToBreakpoint.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_TARGET_THREADPLANRUNTOBREAKPOINT_H
+#define LLDB_TARGET_THREADPLANRUNTOBREAKPOINT_H
+
+#include <vector>
+
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+// This plan runs till it hits one of the breakpoints passed in.
+// It takes ownership of the breakpoint, and will delete it when
+// the plan is complete.
+// ThreadPlanRunToAddress is more effient if you know the target address up
+// front.  Use this one when the function you want to stop at might be realized
+// in the course of the thread plan operation, so you need a breakpoint that
+// can update itself.
+
+class ThreadPlanRunToBreakpoint : public ThreadPlan {
+public:
+  ThreadPlanRunToBreakpoint(Thread &thread, lldb::BreakpointSP breakpoint_sp,
+                            bool stop_others);
+
+  ThreadPlanRunToBreakpoint(Thread &thread,
+                            const std::vector<lldb::BreakpointSP> &breakpoints,
+                            bool stop_others);
+
+  ~ThreadPlanRunToBreakpoint() override;
+
+  void GetDescription(Stream *s, lldb::DescriptionLevel level) override;
+
+  bool ValidatePlan(Stream *error) override;
+
+  bool ShouldStop(Event *event_ptr) override;
+
+  bool StopOthers() override;
+
+  void SetStopOthers(bool new_value) override;
+
+  lldb::StateType GetPlanRunState() override;
+
+  bool WillStop() override;
+
+  bool MischiefManaged() override;
+
+protected:
+  bool DoPlanExplainsStop(Event *event_ptr) override;
+
+  bool AtOurBreakpoint();
+
+private:
+  bool m_stop_others;
+  std::vector<lldb::BreakpointSP>
+      m_breakpoints; // This is the list of breakpoints we are going to run to.
+
+  ThreadPlanRunToBreakpoint(const ThreadPlanRunToBreakpoint &) = delete;
+  const ThreadPlanRunToBreakpoint &
+  operator=(const ThreadPlanRunToBreakpoint &) = delete;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_THREADPLANRUNTOBREAKPOINT_H
diff --git a/lldb/include/lldb/Utility/StreamString.h b/lldb/include/lldb/Utility/StreamString.h
index 5fcda832d4cf82..cadb4ae5630006 100644
--- a/lldb/include/lldb/Utility/StreamString.h
+++ b/lldb/include/lldb/Utility/StreamString.h
@@ -12,6 +12,7 @@
 #include "lldb/Utility/Stream.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatProviders.h"
 
 #include <string>
 
@@ -55,4 +56,11 @@ class StreamString : public Stream {
 
 } // namespace lldb_private
 
+namespace llvm {
+template <> struct format_provider<lldb_private::StreamString> {
+  static void format(const lldb_private::StreamString &label,
+                     raw_ostream &OS, StringRef Style);
+};
+} // end namespace llvm
+
 #endif // LLDB_UTILITY_STREAMSTRING_H
diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
index 052774fe0141da..67d5aa7efe6969 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -26,6 +26,7 @@
 #include "lldb/Target/Thread.h"
 #include "lldb/Target/ThreadPlanCallFunction.h"
 #include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanRunToBreakpoint.h"
 #include "lldb/Target/ThreadPlanStepInstruction.h"
 #include "lldb/Utility/DataBuffer.h"
 #include "lldb/Utility/DataBufferHeap.h"
@@ -980,8 +981,22 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
 
     ConstString current_name =
         current_symbol->GetMangled().GetName(Mangled::ePreferMangled);
-    if (current_symbol->IsTrampoline()) {
+    llvm::StringRef target_symbol_name = current_name;
+    static const llvm::StringRef g_lazy_stub_name = "$lazyLoadStub";
+    // If this is a "lazy library" stub, we don't have a guarantee that the
+    // target library is loaded at the point where we hit the stub.  So we
+    // have to use a symbol name breakpoint rather than an address one.
+    if (current_name && target_symbol_name.consume_back(g_lazy_stub_name)) {
+      auto bkpt_sp = target_sp->CreateBreakpoint(nullptr, nullptr,
+          target_symbol_name.str().c_str(),eFunctionNameTypeFull,
+          eLanguageTypeUnknown, /*offset=*/ 0, /*is_insn_count=*/ false,
+          /*skip_prologue=*/ eLazyBoolNo, /*internal=*/ true,
+          /* hardware=*/ false);
+      return std::make_shared<ThreadPlanRunToBreakpoint>(thread, bkpt_sp,
+          stop_others);
+    }
 
+    if (current_symbol->IsTrampoline()) {
       if (current_name) {
         const ModuleList &images = target_sp->GetImages();
 
@@ -998,7 +1013,6 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
                       load_addr);
           }
         }
-
         SymbolContextList reexported_symbols;
         images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported,
                                           reexported_symbols);
diff --git a/lldb/source/Target/CMakeLists.txt b/lldb/source/Target/CMakeLists.txt
index 2874394c400aaa..0485175d65b9ca 100644
--- a/lldb/source/Target/CMakeLists.txt
+++ b/lldb/source/Target/CMakeLists.txt
@@ -68,6 +68,7 @@ add_lldb_library(lldbTarget
   ThreadPlanCallOnFunctionExit.cpp
   ThreadPlanCallUserExpression.cpp
   ThreadPlanRunToAddress.cpp
+  ThreadPlanRunToBreakpoint.cpp
   ThreadPlanSingleThreadTimeout.cpp
   ThreadPlanShouldStopHere.cpp
   ThreadPlanStepInRange.cpp
diff --git a/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp b/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp
new file mode 100644
index 00000000000000..33114ef0f481f8
--- /dev/null
+++ b/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp
@@ -0,0 +1,146 @@
+//===-- ThreadPlanRunToBreakpoint.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/Breakpoint/BreakpointLocation.h"
+#include "lldb/Target/ThreadPlanRunToBreakpoint.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Stream.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// ThreadPlanRunToBreakpoint: Use this plan to run to a breakpoint.
+
+ThreadPlanRunToBreakpoint::ThreadPlanRunToBreakpoint(Thread &thread,
+                                                     BreakpointSP bkpt_sp,
+                                                     bool stop_others)
+    : ThreadPlan(ThreadPlan::eKindRunToBreakpoint, "Run to breakpoint plan",
+                 thread, eVoteNoOpinion, eVoteNoOpinion),
+      m_stop_others(stop_others) {
+  if (bkpt_sp)
+    m_breakpoints.push_back(std::move(bkpt_sp));
+}
+
+ThreadPlanRunToBreakpoint::ThreadPlanRunToBreakpoint(
+    Thread &thread, const std::vector<BreakpointSP> &breakpoints,
+    bool stop_others)
+    : ThreadPlan(ThreadPlan::eKindRunToBreakpoint, "Run to breakpoint plan",
+                 thread, eVoteNoOpinion, eVoteNoOpinion),
+      m_stop_others(stop_others)  {
+  for (auto bkpt_sp : breakpoints) {
+    // Make the breakpoints thread specific:
+    if (bkpt_sp) {
+      bkpt_sp->SetThreadID(thread.GetID());
+      m_breakpoints.push_back(bkpt_sp);
+    }
+  }
+}
+
+ThreadPlanRunToBreakpoint::~ThreadPlanRunToBreakpoint() {
+  for (auto bkpt_sp : m_breakpoints)
+    GetTarget().RemoveBreakpointByID(bkpt_sp->GetID());
+  m_breakpoints.clear();
+}
+
+void ThreadPlanRunToBreakpoint::GetDescription(Stream *s,
+                                            lldb::DescriptionLevel level) {
+  size_t num_bkpts = m_breakpoints.size();
+  if (level == lldb::eDescriptionLevelBrief) {
+    if (num_bkpts == 0) {
+      s->PutCString("run to breakpoint with no breakpoints given.");
+      return;
+    } else if (num_bkpts == 1)
+      s->PutCString("run to breakpoint: ");
+    else
+      s->PutCString("run to breakpoints: ");
+
+    for (auto bkpt_sp : m_breakpoints) {
+      *s << llvm::formatv("{0} ", bkpt_sp->GetID());
+    }
+  } else {
+    if (num_bkpts == 0) {
+      s->PutCString("run to breakpoint with no breakpoints given.");
+      return;
+    } else if (num_bkpts == 1)
+      s->PutCString("Run to breakpoint: ");
+    else {
+      s->PutCString("Run to breakpoints: ");
+    }
+
+    // FIXME: I'm not preserving breakpoint ID's
+    // in case I'm asked GetDescription after I've removed them.
+    for (auto bkpt_sp : m_breakpoints) {
+      s->PutCString("\n");
+      s->Indent();
+      bkpt_sp->Dump(s);
+    }
+  }
+}
+
+bool ThreadPlanRunToBreakpoint::ValidatePlan(Stream *error) {
+  // We don't require that the breakpoints we were given actually
+  // resolve to something when the plan was pushed.  The only error
+  // was not to have passed any breakpoints.
+  return m_breakpoints.size() != 0;
+}
+
+bool ThreadPlanRunToBreakpoint::DoPlanExplainsStop(Event *event_ptr) {
+  return AtOurBreakpoint();
+}
+
+bool ThreadPlanRunToBreakpoint::ShouldStop(Event *event_ptr) {
+  return AtOurBreakpoint();
+}
+
+bool ThreadPlanRunToBreakpoint::StopOthers() { return m_stop_others; }
+
+void ThreadPlanRunToBreakpoint::SetStopOthers(bool new_value) {
+  m_stop_others = new_value;
+}
+
+StateType ThreadPlanRunToBreakpoint::GetPlanRunState() { return eStateRunning; }
+
+bool ThreadPlanRunToBreakpoint::WillStop() { return true; }
+
+bool ThreadPlanRunToBreakpoint::MischiefManaged() {
+  Log *log = GetLog(LLDBLog::Step);
+
+  if (AtOurBreakpoint()) {
+    // Remove the breakpoints
+    for (auto bkpt_sp : m_breakpoints) {
+      GetTarget().RemoveBreakpointByID(bkpt_sp->GetID());
+    }
+    m_breakpoints.clear();
+    LLDB_LOGF(log, "Completed run to breakpoint plan.");
+    ThreadPlan::MischiefManaged();
+    return true;
+  } else
+    return false;
+}
+
+bool ThreadPlanRunToBreakpoint::AtOurBreakpoint() {
+  lldb::addr_t current_addr = GetThread().GetRegisterContext()->GetPC();
+  Address current_address;
+  current_address.SetLoadAddress(current_addr, &GetTarget());
+  bool found_it = false;
+  for (auto bkpt_sp : m_breakpoints) {
+    if (auto location_sp = bkpt_sp->FindLocationByAddress(current_address)) {
+      StreamString s;
+      location_sp->GetDescription(&s, eDescriptionLevelBrief);
+      LLDB_LOG(GetLog(LLDBLog::Step), "RunToBreakpoint hit breakpoint: {0}", s);
+      found_it = true;
+      break;
+    }
+  }
+  return found_it;
+}
diff --git a/lldb/source/Utility/StreamString.cpp b/lldb/source/Utility/StreamString.cpp
index 883790e0ce19cc..78f39acbc43a03 100644
--- a/lldb/source/Utility/StreamString.cpp
+++ b/lldb/source/Utility/StreamString.cpp
@@ -63,3 +63,9 @@ void StreamString::FillLastLineToColumn(uint32_t column, char fill_char) {
     m_packet.append(column - line_columns, fill_char);
   }
 }
+
+void llvm::format_provider<StreamString>::format(const StreamString &stream,
+                                                llvm::raw_ostream &os,
+                                                llvm::StringRef options) {
+  format_provider<StringRef>::format(stream.GetString(), os, options);
+}
diff --git a/lldb/test/API/macosx/lazy_library/Makefile b/lldb/test/API/macosx/lazy_library/Makefile
new file mode 100644
index 00000000000000..66f32fbae238a1
--- /dev/null
+++ b/lldb/test/API/macosx/lazy_library/Makefile
@@ -0,0 +1,15 @@
+C_SOURCES := main.c
+LD_EXTRAS := -L. -Wl,-lazy_library -Wl,libmy_library.dylib -lmy_library
+
+.PHONY: build-lazylib
+all: build-lazylib a.out
+
+include Makefile.rules
+
+a.out : main.c
+
+build-lazylib: library.c
+	"$(MAKE)" -f $(MAKEFILE_RULES) \
+		DYLIB_C_SOURCES=library.c DYLIB_NAME=my_library DYLIB_ONLY=YES \
+		LD_EXTRAS=""
+
diff --git a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py
new file mode 100644
index 00000000000000..72549be2a1e6fb
--- /dev/null
+++ b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py
@@ -0,0 +1,30 @@
+"""
+Test that we can step through the stubs implementing the
+Darwin linker's lazy_loading feature.
+"""
+
+
+import lldb
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+
+class TestStepThroughLazyLoading(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_step_through_lazy_load(self):
+        self.build()
+        self.main_source_file = lldb.SBFileSpec("main.c")
+        self.lazy_test()
+
+    def lazy_test(self):
+        # This function starts a process, "a.out" by default, sets a source
+        # breakpoint, runs to it, and returns the thread, process & target.
+        # It optionally takes an SBLaunchOption argument if you want to pass
+        # arguments or environment variables.
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "Stop here", self.main_source_file
+        )
+
+        frame = thread.GetFrameAtIndex(0)
+
diff --git a/lldb/test/API/macosx/lazy_library/library.c b/lldb/test/API/macosx/lazy_library/library.c
new file mode 100644
index 00000000000000..aaa2783afe351c
--- /dev/null
+++ b/lldb/test/API/macosx/lazy_library/library.c
@@ -0,0 +1,14 @@
+struct Foo {
+  int bar;
+  int baz;
+};
+
+struct Foo g_foo = {10, 20};
+
+int return_bar() {
+  return g_foo.bar;
+}
+
+int return_baz() {
+  return g_foo.baz;
+}
diff --git a/lldb/test/API/macosx/lazy_library/main.c b/lldb/test/API/macosx/lazy_library/main.c
new file mode 100644
index 00000000000000..bc9b9fdeba0d8a
--- /dev/null
+++ b/lldb/test/API/macosx/lazy_library/main.c
@@ -0,0 +1,13 @@
+extern int return_bar();
+extern int return_baz();
+
+int
+main()
+{
+  int bar = return_bar(); // Stop here
+  bar += return_bar();
+  int baz = return_baz();
+  baz += return_baz();
+
+  return bar + baz;
+}

>From f14a3fd9ca642de667cc279fa2b22830e3a1a263 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Mon, 21 Sep 2026 17:03:04 -0700
Subject: [PATCH 2/5] Use "lazy LIBRARY" consistently.

---
 ...pThroughLazyLoad.py => TestStepThroughLazyLibrary.py} | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)
 rename lldb/test/API/macosx/lazy_library/{TestStepThroughLazyLoad.py => TestStepThroughLazyLibrary.py} (79%)

diff --git a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
similarity index 79%
rename from lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py
rename to lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
index 72549be2a1e6fb..c42278735feebb 100644
--- a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLoad.py
+++ b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
@@ -1,18 +1,19 @@
 """
 Test that we can step through the stubs implementing the
-Darwin linker's lazy_loading feature.
+Darwin linker's lazy_library feature.
 """
 
 
 import lldb
+from lldbsuite.test.decorators import *
 import lldbsuite.test.lldbutil as lldbutil
 from lldbsuite.test.lldbtest import *
 
-
-class TestStepThroughLazyLoading(TestBase):
+class TestStepThroughLazyLibrary(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    def test_step_through_lazy_load(self):
+    @skipIf(macos_version=["<", "27"])
+    def test_step_through_lazy_library(self):
         self.build()
         self.main_source_file = lldb.SBFileSpec("main.c")
         self.lazy_test()

>From 33a79b591f565ee89a5751356494d009212a82bc Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Mon, 21 Sep 2026 17:10:58 -0700
Subject: [PATCH 3/5] Formatting

---
 lldb/include/lldb/Utility/StreamString.h            |  4 ++--
 .../MacOSX-DYLD/DynamicLoaderDarwin.cpp             | 13 +++++++------
 lldb/source/Target/ThreadPlanRunToBreakpoint.cpp    |  9 +++++----
 lldb/source/Utility/StreamString.cpp                |  4 ++--
 .../lazy_library/TestStepThroughLazyLibrary.py      |  2 +-
 lldb/test/API/macosx/lazy_library/library.c         |  8 ++------
 lldb/test/API/macosx/lazy_library/main.c            |  4 +---
 7 files changed, 20 insertions(+), 24 deletions(-)

diff --git a/lldb/include/lldb/Utility/StreamString.h b/lldb/include/lldb/Utility/StreamString.h
index cadb4ae5630006..fe80644a6ce190 100644
--- a/lldb/include/lldb/Utility/StreamString.h
+++ b/lldb/include/lldb/Utility/StreamString.h
@@ -58,8 +58,8 @@ class StreamString : public Stream {
 
 namespace llvm {
 template <> struct format_provider<lldb_private::StreamString> {
-  static void format(const lldb_private::StreamString &label,
-                     raw_ostream &OS, StringRef Style);
+  static void format(const lldb_private::StreamString &label, raw_ostream &OS,
+                     StringRef Style);
 };
 } // end namespace llvm
 
diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
index 67d5aa7efe6969..e9aef3d2496921 100644
--- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
+++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp
@@ -987,13 +987,14 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
     // target library is loaded at the point where we hit the stub.  So we
     // have to use a symbol name breakpoint rather than an address one.
     if (current_name && target_symbol_name.consume_back(g_lazy_stub_name)) {
-      auto bkpt_sp = target_sp->CreateBreakpoint(nullptr, nullptr,
-          target_symbol_name.str().c_str(),eFunctionNameTypeFull,
-          eLanguageTypeUnknown, /*offset=*/ 0, /*is_insn_count=*/ false,
-          /*skip_prologue=*/ eLazyBoolNo, /*internal=*/ true,
-          /* hardware=*/ false);
+      auto bkpt_sp = target_sp->CreateBreakpoint(
+          nullptr, nullptr, target_symbol_name.str().c_str(),
+          eFunctionNameTypeFull, eLanguageTypeUnknown, /*offset=*/0,
+          /*is_insn_count=*/false,
+          /*skip_prologue=*/eLazyBoolNo, /*internal=*/true,
+          /* hardware=*/false);
       return std::make_shared<ThreadPlanRunToBreakpoint>(thread, bkpt_sp,
-          stop_others);
+                                                         stop_others);
     }
 
     if (current_symbol->IsTrampoline()) {
diff --git a/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp b/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp
index 33114ef0f481f8..0350b16bcf98e3 100644
--- a/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp
+++ b/lldb/source/Target/ThreadPlanRunToBreakpoint.cpp
@@ -1,4 +1,5 @@
-//===-- ThreadPlanRunToBreakpoint.cpp ----------------------------------------===//
+//===-- ThreadPlanRunToBreakpoint.cpp
+//----------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +7,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Target/ThreadPlanRunToBreakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/RegisterContext.h"
 #include "lldb/Target/Target.h"
@@ -36,7 +37,7 @@ ThreadPlanRunToBreakpoint::ThreadPlanRunToBreakpoint(
     bool stop_others)
     : ThreadPlan(ThreadPlan::eKindRunToBreakpoint, "Run to breakpoint plan",
                  thread, eVoteNoOpinion, eVoteNoOpinion),
-      m_stop_others(stop_others)  {
+      m_stop_others(stop_others) {
   for (auto bkpt_sp : breakpoints) {
     // Make the breakpoints thread specific:
     if (bkpt_sp) {
@@ -53,7 +54,7 @@ ThreadPlanRunToBreakpoint::~ThreadPlanRunToBreakpoint() {
 }
 
 void ThreadPlanRunToBreakpoint::GetDescription(Stream *s,
-                                            lldb::DescriptionLevel level) {
+                                               lldb::DescriptionLevel level) {
   size_t num_bkpts = m_breakpoints.size();
   if (level == lldb::eDescriptionLevelBrief) {
     if (num_bkpts == 0) {
diff --git a/lldb/source/Utility/StreamString.cpp b/lldb/source/Utility/StreamString.cpp
index 78f39acbc43a03..8161f9a7592091 100644
--- a/lldb/source/Utility/StreamString.cpp
+++ b/lldb/source/Utility/StreamString.cpp
@@ -65,7 +65,7 @@ void StreamString::FillLastLineToColumn(uint32_t column, char fill_char) {
 }
 
 void llvm::format_provider<StreamString>::format(const StreamString &stream,
-                                                llvm::raw_ostream &os,
-                                                llvm::StringRef options) {
+                                                 llvm::raw_ostream &os,
+                                                 llvm::StringRef options) {
   format_provider<StringRef>::format(stream.GetString(), os, options);
 }
diff --git a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
index c42278735feebb..1e5acd4ab15353 100644
--- a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
+++ b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
@@ -9,6 +9,7 @@
 import lldbsuite.test.lldbutil as lldbutil
 from lldbsuite.test.lldbtest import *
 
+
 class TestStepThroughLazyLibrary(TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
@@ -28,4 +29,3 @@ def lazy_test(self):
         )
 
         frame = thread.GetFrameAtIndex(0)
-
diff --git a/lldb/test/API/macosx/lazy_library/library.c b/lldb/test/API/macosx/lazy_library/library.c
index aaa2783afe351c..4fadb00230a5a9 100644
--- a/lldb/test/API/macosx/lazy_library/library.c
+++ b/lldb/test/API/macosx/lazy_library/library.c
@@ -5,10 +5,6 @@ struct Foo {
 
 struct Foo g_foo = {10, 20};
 
-int return_bar() {
-  return g_foo.bar;
-}
+int return_bar() { return g_foo.bar; }
 
-int return_baz() {
-  return g_foo.baz;
-}
+int return_baz() { return g_foo.baz; }
diff --git a/lldb/test/API/macosx/lazy_library/main.c b/lldb/test/API/macosx/lazy_library/main.c
index bc9b9fdeba0d8a..8616bc5ca2eac3 100644
--- a/lldb/test/API/macosx/lazy_library/main.c
+++ b/lldb/test/API/macosx/lazy_library/main.c
@@ -1,9 +1,7 @@
 extern int return_bar();
 extern int return_baz();
 
-int
-main()
-{
+int main() {
   int bar = return_bar(); // Stop here
   bar += return_bar();
   int baz = return_baz();

>From 2c19efbd38ad7ac4e4323d982baee97ba61e471d Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 22 Sep 2026 10:46:52 -0700
Subject: [PATCH 4/5] Restore the bits of the test I somehow deleted...

---
 .../TestStepThroughLazyLibrary.py             | 31 +++++++++++++++++++
 lldb/test/API/macosx/lazy_library/main.c      |  2 +-
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
index 1e5acd4ab15353..355b4b19138ada 100644
--- a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
+++ b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
@@ -28,4 +28,35 @@ def lazy_test(self):
             self, "Stop here", self.main_source_file
         )
 
+        # Record our line here so we know if step out is still in this line.
         frame = thread.GetFrameAtIndex(0)
+        first_stop_line = frame.line_entry.line
+        thread.StepInto()
+
+        frame = thread.GetFrameAtIndex(0)
+        self.assertEqual(frame.name, "return_bar", "Stepped in first use")
+
+        thread.StepOut()
+        frame = thread.GetFrameAtIndex(0)
+        self.assertEqual(frame.name, "main", "Stepped out successfully")
+        if frame.line_entry.line == first_stop_line:
+            thread.StepOver()
+            frame = thread.GetFrameAtIndex(0)
+            self.assertNotEqual(frame.line_entry.line, first_stop_line, "Stepped past first stop line")
+    
+        thread.StepInto()
+        frame = thread.GetFrameAtIndex(0)
+        self.assertEqual(frame.name, "return_bar", "Stepped in second use")
+
+        run_to_bkpt = target.BreakpointCreateBySourceRegex("Run to here", self.main_source_file)
+        self.assertNotEqual(0, run_to_bkpt.num_locations, "Made run to here bkpt")
+
+        thread_list = lldbutil.continue_to_breakpoint(process, run_to_bkpt)
+        self.assertEqual(len(thread_list), 1, "Hit our breakpoint")
+        self.assertEqual(thread.id, thread_list[0].id, "Our thread hit it")
+
+        thread.StepInto()
+        frame = thread.GetFrameAtIndex(0)
+        self.assertEqual(frame.name, "return_baz", "Stepped into return_baz")
+        
+        
diff --git a/lldb/test/API/macosx/lazy_library/main.c b/lldb/test/API/macosx/lazy_library/main.c
index 8616bc5ca2eac3..e8bc5744f173eb 100644
--- a/lldb/test/API/macosx/lazy_library/main.c
+++ b/lldb/test/API/macosx/lazy_library/main.c
@@ -4,7 +4,7 @@ extern int return_baz();
 int main() {
   int bar = return_bar(); // Stop here
   bar += return_bar();
-  int baz = return_baz();
+  int baz = return_baz(); // Run to here
   baz += return_baz();
 
   return bar + baz;

>From bce5e71f8c9a4eef978cc11914a04164e6f03045 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 22 Sep 2026 11:04:45 -0700
Subject: [PATCH 5/5] Formatting

---
 .../lazy_library/TestStepThroughLazyLibrary.py       | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
index 355b4b19138ada..894999aec58e95 100644
--- a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
+++ b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
@@ -42,13 +42,17 @@ def lazy_test(self):
         if frame.line_entry.line == first_stop_line:
             thread.StepOver()
             frame = thread.GetFrameAtIndex(0)
-            self.assertNotEqual(frame.line_entry.line, first_stop_line, "Stepped past first stop line")
-    
+            self.assertNotEqual(
+                frame.line_entry.line, first_stop_line, "Stepped past first stop line"
+            )
+
         thread.StepInto()
         frame = thread.GetFrameAtIndex(0)
         self.assertEqual(frame.name, "return_bar", "Stepped in second use")
 
-        run_to_bkpt = target.BreakpointCreateBySourceRegex("Run to here", self.main_source_file)
+        run_to_bkpt = target.BreakpointCreateBySourceRegex(
+            "Run to here", self.main_source_file
+        )
         self.assertNotEqual(0, run_to_bkpt.num_locations, "Made run to here bkpt")
 
         thread_list = lldbutil.continue_to_breakpoint(process, run_to_bkpt)
@@ -58,5 +62,3 @@ def lazy_test(self):
         thread.StepInto()
         frame = thread.GetFrameAtIndex(0)
         self.assertEqual(frame.name, "return_baz", "Stepped into return_baz")
-        
-        



More information about the lldb-commits mailing list