[Lldb-commits] [lldb] Handle stepping through "lazy library" stubs (PR #225238)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Sep 22 12:57:33 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/6] 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/6] 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/6] 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/6] 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/6] 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")
-
-
>From 71c6840880c9e7804d5b97b9d7e904b883f56e2f Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 22 Sep 2026 12:56:06 -0700
Subject: [PATCH 6/6] The macos version check doesn't also require macos. This
test relies on a Darwin linker feature, so it can only run there.
---
lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
index 894999aec58e95..45c8283b998c83 100644
--- a/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
+++ b/lldb/test/API/macosx/lazy_library/TestStepThroughLazyLibrary.py
@@ -13,6 +13,7 @@
class TestStepThroughLazyLibrary(TestBase):
NO_DEBUG_INFO_TESTCASE = True
+ @skipUnlessDarwin
@skipIf(macos_version=["<", "27"])
def test_step_through_lazy_library(self):
self.build()
More information about the lldb-commits
mailing list