diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h index 4907572afe279..04e4639f90a17 100644 --- a/lldb/include/lldb/Target/Language.h +++ b/lldb/include/lldb/Target/Language.h @@ -26,6 +26,15 @@ namespace lldb_private { +class LanguageProperties : public Properties { +public: + LanguageProperties(); + + static llvm::StringRef GetSettingName(); + + bool GetEnableFilterForLineBreakpoints() const; +}; + class Language : public PluginInterface { public: class TypeScavenger { @@ -322,6 +331,8 @@ class Language : public PluginInterface { static LanguageSet GetLanguagesSupportingTypeSystemsForExpressions(); static LanguageSet GetLanguagesSupportingREPLs(); + static LanguageProperties &GetGlobalLanguageProperties(); + // Given a mangled function name, calculates some alternative manglings since // the compiler mangling may not line up with the symbol we are expecting. virtual std::vector @@ -337,6 +348,15 @@ class Language : public PluginInterface { virtual llvm::StringRef GetInstanceVariableName() { return {}; } + /// Returns true if this SymbolContext should be ignored when setting + /// breakpoints by line (number or regex). Helpful for languages that create + /// artificial functions without meaningful user code associated with them + /// (e.g. code that gets expanded in late compilation stages, like by + /// CoroSplitter). + virtual bool IgnoreForLineBreakpoints(const SymbolContext &) const { + return false; + } + protected: // Classes that inherit from Language can see and modify these diff --git a/lldb/source/Breakpoint/BreakpointResolver.cpp b/lldb/source/Breakpoint/BreakpointResolver.cpp index c1e77d3c70da1..6c97d0d76680a 100644 --- a/lldb/source/Breakpoint/BreakpointResolver.cpp +++ b/lldb/source/Breakpoint/BreakpointResolver.cpp @@ -23,6 +23,7 @@ #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Language.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" @@ -204,8 +205,15 @@ void BreakpointResolver::SetSCMatchesByLine( SearchFilter &filter, SymbolContextList &sc_list, bool skip_prologue, llvm::StringRef log_ident, uint32_t line, std::optional column) { llvm::SmallVector all_scs; - for (uint32_t i = 0; i < sc_list.GetSize(); ++i) - all_scs.push_back(sc_list[i]); + + for (const auto &sc : sc_list) { + if (Language::GetGlobalLanguageProperties() + .GetEnableFilterForLineBreakpoints()) + if (Language *lang = Language::FindPlugin(sc.GetLanguage()); + lang && lang->IgnoreForLineBreakpoints(sc)) + continue; + all_scs.push_back(sc); + } while (all_scs.size()) { uint32_t closest_line = UINT32_MAX; diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index aa05f9afae4dd..45188ea40463e 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -848,6 +848,9 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) m_collection_sp->AppendProperty( "symbols", "Symbol lookup and cache settings.", true, ModuleList::GetGlobalModuleListProperties().GetValueProperties()); + m_collection_sp->AppendProperty( + LanguageProperties::GetSettingName(), "Language settings.", true, + Language::GetGlobalLanguageProperties().GetValueProperties()); if (m_command_interpreter_up) { m_collection_sp->AppendProperty( "interpreter", diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp index caeee89810e05..4dfa9cf77de84 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.cpp @@ -1794,6 +1794,17 @@ SwiftLanguage::GetDemangledFunctionNameWithoutArguments(Mangled mangled) const { return mangled_name; } +bool SwiftLanguage::IgnoreForLineBreakpoints(const SymbolContext &sc) const { + // If we don't have a function, conservatively return false. + if (!sc.function) + return false; + StringRef name = sc.function->GetMangled().GetMangledName().GetStringRef(); + // In async functions, ignore "await resume" funclets, these only deallocate + // the async context and task_switch back to user code. + return SwiftLanguageRuntime::IsSwiftAsyncAwaitResumePartialFunctionSymbol( + name); +} + //------------------------------------------------------------------ // Static Functions //------------------------------------------------------------------ diff --git a/lldb/source/Plugins/Language/Swift/SwiftLanguage.h b/lldb/source/Plugins/Language/Swift/SwiftLanguage.h index 9231e5c1a1d14..7b276f445cf87 100644 --- a/lldb/source/Plugins/Language/Swift/SwiftLanguage.h +++ b/lldb/source/Plugins/Language/Swift/SwiftLanguage.h @@ -90,6 +90,8 @@ class SwiftLanguage : public Language { llvm::StringRef GetInstanceVariableName() override { return "self"; } + bool IgnoreForLineBreakpoints(const SymbolContext &sc) const override; + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ diff --git a/lldb/source/Target/Language.cpp b/lldb/source/Target/Language.cpp index 7c0ddd42813c5..4709dc6053585 100644 --- a/lldb/source/Target/Language.cpp +++ b/lldb/source/Target/Language.cpp @@ -13,6 +13,7 @@ #include "lldb/Target/Language.h" #include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Target/Target.h" @@ -27,6 +28,35 @@ using namespace lldb_private::formatters; typedef std::unique_ptr LanguageUP; typedef std::map LanguagesMap; +#define LLDB_PROPERTIES_language +#include "TargetProperties.inc" + +enum { +#define LLDB_PROPERTIES_language +#include "TargetPropertiesEnum.inc" +}; + +LanguageProperties &Language::GetGlobalLanguageProperties() { + static LanguageProperties g_settings; + return g_settings; +} + +llvm::StringRef LanguageProperties::GetSettingName() { + static constexpr llvm::StringLiteral g_setting_name("language"); + return g_setting_name; +} + +LanguageProperties::LanguageProperties() { + m_collection_sp = std::make_shared(GetSettingName()); + m_collection_sp->Initialize(g_language_properties); +} + +bool LanguageProperties::GetEnableFilterForLineBreakpoints() const { + const uint32_t idx = ePropertyEnableFilterForLineBreakpoints; + return GetPropertyAtIndexAs( + idx, g_language_properties[idx].default_uint_value != 0); +} + static LanguagesMap &GetLanguagesMap() { static LanguagesMap *g_map = nullptr; static llvm::once_flag g_initialize; diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index e55bd7406c02c..9208aea4ed20b 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -360,3 +360,9 @@ let Definition = "thread" in { DefaultUnsignedValue<600000>, Desc<"Maximum number of frames to backtrace.">; } + +let Definition = "language" in { + def EnableFilterForLineBreakpoints: Property<"enable-filter-for-line-breakpoints", "Boolean">, + DefaultTrue, + Desc<"If true, allow Language plugins to filter locations when setting breakpoints by line number or regex.">; +} diff --git a/lldb/test/API/lang/swift/async_breakpoints/Makefile b/lldb/test/API/lang/swift/async_breakpoints/Makefile new file mode 100644 index 0000000000000..2a69023633b34 --- /dev/null +++ b/lldb/test/API/lang/swift/async_breakpoints/Makefile @@ -0,0 +1,3 @@ +SWIFT_SOURCES := main.swift + +include Makefile.rules diff --git a/lldb/test/API/lang/swift/async_breakpoints/TestSwiftAsyncBreakpoints.py b/lldb/test/API/lang/swift/async_breakpoints/TestSwiftAsyncBreakpoints.py new file mode 100644 index 0000000000000..532abc96d5a9e --- /dev/null +++ b/lldb/test/API/lang/swift/async_breakpoints/TestSwiftAsyncBreakpoints.py @@ -0,0 +1,54 @@ +import lldb +from lldbsuite.test.decorators import * +import lldbsuite.test.lldbtest as lldbtest +import lldbsuite.test.lldbutil as lldbutil + + +class TestSwiftAsyncBreakpoints(lldbtest.TestBase): + @swiftTest + @skipIfWindows + @skipIfLinux + @skipIf(archs=no_match(["arm64", "arm64e", "x86_64"])) + def test(self): + """Test async breakpoints""" + self.build() + filespec = lldb.SBFileSpec("main.swift") + target, process, thread, breakpoint1 = lldbutil.run_to_source_breakpoint( + self, "Breakpoint1", filespec + ) + breakpoint2 = target.BreakpointCreateBySourceRegex("Breakpoint2", filespec) + breakpoint3 = target.BreakpointCreateBySourceRegex("Breakpoint3", filespec) + self.assertEquals(breakpoint1.GetNumLocations(), 2) + self.assertEquals(breakpoint2.GetNumLocations(), 1) + self.assertEquals(breakpoint3.GetNumLocations(), 2) + + location11 = breakpoint1.GetLocationAtIndex(0) + location12 = breakpoint1.GetLocationAtIndex(1) + self.assertEquals(location11.GetHitCount(), 1) + self.assertEquals(location12.GetHitCount(), 0) + + self.assertEquals(thread.GetStopDescription(128), "breakpoint 1.1") + process.Continue() + self.assertEquals(thread.GetStopDescription(128), "breakpoint 1.2") + + thread.StepOver() + self.assertEquals(thread.GetStopDescription(128), "breakpoint 2.1") + self.expect("expr timestamp1", substrs=["42"]) + + thread.StepOver() + self.assertIn("breakpoint 3.1", thread.GetStopDescription(128)) + self.expect("expr timestamp1", substrs=["42"]) + + process.Continue() + self.assertIn("breakpoint 3.2", thread.GetStopDescription(128)) + self.expect("expr timestamp1", substrs=["42"]) + + thread.StepOver() + self.expect("expr timestamp1", substrs=["42"]) + self.expect("expr timestamp2", substrs=["43"]) + + self.runCmd("settings set language.enable-filter-for-line-breakpoints false") + breakpoint1_no_filter = target.BreakpointCreateBySourceRegex( + "Breakpoint1", filespec + ) + self.assertEquals(breakpoint1_no_filter.GetNumLocations(), 3) diff --git a/lldb/test/API/lang/swift/async_breakpoints/main.swift b/lldb/test/API/lang/swift/async_breakpoints/main.swift new file mode 100644 index 0000000000000..d1c83877229a9 --- /dev/null +++ b/lldb/test/API/lang/swift/async_breakpoints/main.swift @@ -0,0 +1,15 @@ +func getTimestamp(i:Int) async -> Int { + return i +} + +func work() {} + +func foo() async { + work() + let timestamp1 = await getTimestamp(i:42) // Breakpoint1 + work() // Breakpoint2 + let timestamp2 = await getTimestamp(i:43) // Breakpoint3 + work() +} + +await foo()