https://github.com/rchamala updated https://github.com/llvm/llvm-project/pull/181334
>From df2c4d95832a0e5ebaca152a731a0a2358c3c08a Mon Sep 17 00:00:00 2001 From: Alex Wang <[email protected]> Date: Thu, 12 Feb 2026 23:48:32 -0800 Subject: [PATCH] [clang-tidy] Fix bugprone-bad-signal-to-kill-thread not working in clangd (#180711) After preamble deserialization, `Token::getLiteralData()` returns `nullptr` for macro tokens defined in headers. Fall back to `SourceManager` to retrieve the token text from the source buffer. Fixes https://github.com/clangd/clangd/issues/2473 --- .../bugprone/BadSignalToKillThreadCheck.cpp | 10 +- .../clangd/unittests/DiagnosticsTests.cpp | 19 ++ clang-tools-extra/docs/ReleaseNotes.rst | 4 + lldb/bindings/python/CMakeLists.txt | 1 + lldb/bindings/python/python-wrapper.swig | 13 + .../use/tutorials/scripted-symbol-locator.md | 163 +++++++++++++ .../templates/scripted_symbol_locator.py | 223 ++++++++++++++++++ lldb/include/lldb/API/SBFileSpec.h | 5 + lldb/include/lldb/API/SBTarget.h | 16 ++ lldb/include/lldb/Core/PluginManager.h | 4 + .../ScriptedSymbolLocatorInterface.h | 56 +++++ .../lldb/Interpreter/ScriptInterpreter.h | 9 + lldb/include/lldb/Symbol/LineEntry.h | 2 +- lldb/include/lldb/Target/Target.h | 24 ++ lldb/include/lldb/lldb-forward.h | 3 + lldb/include/lldb/lldb-private-interfaces.h | 2 + lldb/source/API/SBTarget.cpp | 30 +++ lldb/source/Commands/CommandObjectTarget.cpp | 134 +++++++++++ lldb/source/Core/Module.cpp | 2 +- lldb/source/Core/PluginManager.cpp | 24 +- lldb/source/Interpreter/ScriptInterpreter.cpp | 7 + .../Python/Interfaces/CMakeLists.txt | 3 +- .../ScriptInterpreterPythonInterfaces.cpp | 2 + .../ScriptInterpreterPythonInterfaces.h | 1 + .../Interfaces/ScriptedPythonInterface.cpp | 13 + .../Interfaces/ScriptedPythonInterface.h | 84 +++++++ .../ScriptedSymbolLocatorPythonInterface.cpp | 138 +++++++++++ .../ScriptedSymbolLocatorPythonInterface.h | 72 ++++++ .../Python/SWIGPythonBridge.h | 1 + .../Python/ScriptInterpreterPython.cpp | 5 + .../Python/ScriptInterpreterPythonImpl.h | 3 + .../Plugins/SymbolLocator/CMakeLists.txt | 1 + .../Debuginfod/SymbolLocatorDebuginfod.cpp | 2 +- .../SymbolLocator/Scripted/CMakeLists.txt | 13 + .../Scripted/SymbolLocatorScripted.cpp | 200 ++++++++++++++++ .../Scripted/SymbolLocatorScripted.h | 55 +++++ lldb/source/Symbol/LineEntry.cpp | 22 +- lldb/source/Target/StackFrame.cpp | 2 +- lldb/source/Target/StackFrameList.cpp | 2 +- lldb/source/Target/Target.cpp | 73 ++++++ lldb/source/Target/ThreadPlanStepRange.cpp | 2 +- .../scripted_symbol_locator/Makefile | 9 + .../TestScriptedSymbolLocator.py | 197 ++++++++++++++++ .../scripted_symbol_locator/main.c | 7 + .../scripted_symbol_locator/source_locator.py | 56 +++++ .../Python/PythonTestSuite.cpp | 5 + 46 files changed, 1706 insertions(+), 13 deletions(-) create mode 100644 lldb/docs/use/tutorials/scripted-symbol-locator.md create mode 100644 lldb/examples/python/templates/scripted_symbol_locator.py create mode 100644 lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp create mode 100644 lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp create mode 100644 lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/Makefile create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/main.c create mode 100644 lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py diff --git a/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp index 3e1188d5e2463..43a352e389f68 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BadSignalToKillThreadCheck.cpp @@ -38,9 +38,15 @@ void BadSignalToKillThreadCheck::check(const MatchFinder::MatchResult &Result) { return std::nullopt; const MacroInfo *MI = PP->getMacroInfo(It->first); const Token &T = MI->tokens().back(); - if (!T.isLiteral() || !T.getLiteralData()) + + if (!T.isLiteral()) + return std::nullopt; + + SmallVector<char> Buffer; + bool Invalid = false; + const StringRef ValueStr = PP->getSpelling(T, Buffer, &Invalid); + if (Invalid) return std::nullopt; - const StringRef ValueStr = StringRef(T.getLiteralData(), T.getLength()); llvm::APInt IntValue; constexpr unsigned AutoSenseRadix = 0; diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index f9ff6f21009f3..84ceddbd4fc4b 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -823,6 +823,25 @@ TEST(DiagnosticTest, ClangTidyNoLiteralDataInMacroToken) { EXPECT_THAT(TU.build().getDiagnostics(), UnorderedElementsAre()); // no-crash } +TEST(DiagnosticTest, BadSignalToKillThreadInPreamble) { + Annotations Main(R"cpp( + #include "signal.h" + using pthread_t = int; + int pthread_kill(pthread_t thread, int sig); + int func() { + pthread_t thread; + return pthread_kill(thread, 15); + } + )cpp"); + TestTU TU = TestTU::withCode(Main.code()); + TU.HeaderFilename = "signal.h"; + TU.HeaderCode = "#define SIGTERM 15"; + TU.ClangTidyProvider = addTidyChecks("bugprone-bad-signal-to-kill-thread"); + EXPECT_THAT(TU.build().getDiagnostics(), + ifTidyChecks(UnorderedElementsAre( + diagName("bugprone-bad-signal-to-kill-thread")))); +} + TEST(DiagnosticTest, ClangTidyMacroToEnumCheck) { Annotations Main(R"cpp( #if 1 diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index e7437e62ee77d..36626d7c1a9be 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -144,6 +144,10 @@ Changes in existing checks <clang-tidy/checks/bugprone/argument-comment>` to also check for C++11 inherited constructors. +- Improved :doc:`bugprone-bad-signal-to-kill-thread + <clang-tidy/checks/bugprone/bad-signal-to-kill-thread>` check by fixing false + negatives when the ``SIGTERM`` macro is obtained from a precompiled header. + - Improved :doc:`bugprone-exception-escape <clang-tidy/checks/bugprone/exception-escape>` check by adding `TreatFunctionsWithoutSpecificationAsThrowing` option to support reporting diff --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt index 2ebcf5a8e7aca..058b3ceb9b038 100644 --- a/lldb/bindings/python/CMakeLists.txt +++ b/lldb/bindings/python/CMakeLists.txt @@ -116,6 +116,7 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_platform.py" "${LLDB_SOURCE_DIR}/examples/python/templates/operating_system.py" "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_thread_plan.py" + "${LLDB_SOURCE_DIR}/examples/python/templates/scripted_symbol_locator.py" ) if(APPLE) diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index bf59569920470..3cf3407be910b 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -583,6 +583,19 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyOb return sb_ptr; } +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFileSpec(PyObject * + data) { + lldb::SBFileSpec *sb_ptr = NULL; + + int valid_cast = SWIG_ConvertPtr(data, (void **)&sb_ptr, + SWIGTYPE_p_lldb__SBFileSpec, 0); + + if (valid_cast == -1) + return NULL; + + return sb_ptr; +} + void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data) { lldb::SBFrameList *sb_ptr = NULL; diff --git a/lldb/docs/use/tutorials/scripted-symbol-locator.md b/lldb/docs/use/tutorials/scripted-symbol-locator.md new file mode 100644 index 0000000000000..33147edbd896e --- /dev/null +++ b/lldb/docs/use/tutorials/scripted-symbol-locator.md @@ -0,0 +1,163 @@ +# Scripted Symbol Locator Tutorial + +The **Scripted Symbol Locator** lets you write a Python class that tells LLDB +where to find executables, symbol files, and source files for your debug +targets. This is useful when your build artifacts live in a custom location, +such as a symbol server or a local build-ID-indexed cache. + +## Quick Start + +1. **Write a locator class.** Create a Python file (e.g., `my_locator.py`) + with a class that implements the methods you need: + + ```python + import os + import lldb + + class MyLocator: + def __init__(self, exe_ctx, args): + self.cache_dir = None + if args.IsValid(): + d = args.GetValueForKey("cache_dir") + if d and d.IsValid(): + self.cache_dir = d.GetStringValue(4096) + + def locate_source_file(self, module, original_source_file): + """Return the resolved path, or None to fall through.""" + if not self.cache_dir: + return None + uuid = module.GetUUIDString() + basename = os.path.basename(original_source_file) + candidate = os.path.join(self.cache_dir, uuid, "src", basename) + if os.path.exists(candidate): + return candidate + return None + ``` + +2. **Import the script and register the locator on a target:** + + ``` + (lldb) command script import /path/to/my_locator.py + (lldb) target symbols scripted register \ + -C my_locator.MyLocator \ + -k cache_dir -v /path/to/cache + ``` + +3. **Debug normally.** When LLDB resolves source files for that target, + your `locate_source_file` method will be called automatically. + +## Available Methods + +Your locator class can implement any combination of these methods. All are +optional except `__init__` and `locate_source_file` (which is the abstract +method that must be present). + +| Method | Called When | +|--------|------------| +| `locate_source_file(module, path)` | LLDB resolves a source file path in debug info | +| `locate_executable_object_file(module_spec)` | LLDB needs the binary for a module | +| `locate_executable_symbol_file(module_spec, search_paths)` | LLDB needs separate debug symbols | +| `download_object_and_symbol_file(module_spec, force, copy)` | Last-resort download from a remote source | + +### Method Signatures + +```python +def __init__(self, exe_ctx: lldb.SBExecutionContext, + args: lldb.SBStructuredData) -> None: + ... + +def locate_source_file(self, module: lldb.SBModule, + original_source_file: str) -> Optional[str]: + ... + +def locate_executable_object_file( + self, module_spec: lldb.SBModuleSpec) -> Optional[str]: + ... + +def locate_executable_symbol_file( + self, module_spec: lldb.SBModuleSpec, + default_search_paths: list) -> Optional[str]: + ... + +def download_object_and_symbol_file( + self, module_spec: lldb.SBModuleSpec, + force_lookup: bool, copy_executable: bool) -> bool: + ... +``` + +## Per-Target Registration + +The scripted symbol locator is registered **per target**. Different targets +can use different locator classes or different arguments. + +``` +(lldb) target select 0 +(lldb) target symbols scripted register -C my_locator.MyLocator \ + -k cache_dir -v /cache/project-a + +(lldb) target select 1 +(lldb) target symbols scripted register -C my_locator.MyLocator \ + -k cache_dir -v /cache/project-b +``` + +### Commands + +| Command | Description | +|---------|-------------| +| `target symbols scripted register -C <class> [-k <key> -v <value> ...]` | Register a locator | +| `target symbols scripted clear` | Remove the locator from the current target | +| `target symbols scripted info` | Show the current locator class | + +### SB API + +You can also register locators programmatically: + +```python +import lldb + +error = target.RegisterScriptedSymbolLocator( + "my_locator.MyLocator", args) +# args is an SBStructuredData dictionary + +target.ClearScriptedSymbolLocator() +``` + +## Caching + +Source file resolutions are cached per `(module UUID, source file path)` pair +within each target. The cache is cleared when: + +- A new locator is registered (via `register`) +- The locator is cleared (via `clear`) + +This means your `locate_source_file` method is called at most once per +unique `(UUID, path)` combination. + +## Base Class Template + +LLDB ships a base class template at `lldb.plugins.scripted_symbol_locator`. +You can import and subclass it: + +```python +from lldb.plugins.scripted_symbol_locator import ScriptedSymbolLocator + +class MyLocator(ScriptedSymbolLocator): + def __init__(self, exe_ctx, args): + super().__init__(exe_ctx, args) + + def locate_source_file(self, module, original_source_file): + # Your implementation here + return None +``` + +The base class handles extracting the target and args from the execution +context. See `lldb/examples/python/templates/scripted_symbol_locator.py` +for the full template with docstrings. + +## Listing Scripting Extensions + +To see all registered scripting extensions (including symbol locators): + +``` +(lldb) scripting extension list +``` diff --git a/lldb/examples/python/templates/scripted_symbol_locator.py b/lldb/examples/python/templates/scripted_symbol_locator.py new file mode 100644 index 0000000000000..bda2724e74e0d --- /dev/null +++ b/lldb/examples/python/templates/scripted_symbol_locator.py @@ -0,0 +1,223 @@ +from abc import ABCMeta, abstractmethod +import os + +import lldb + + +class ScriptedSymbolLocator(metaclass=ABCMeta): + """ + The base class for a scripted symbol locator. + + Most of the base class methods are optional and return ``None`` to fall + through to LLDB's default resolution. Override only the methods you need. + + Configuration:: + + (lldb) command script import /path/to/my_locator.py + (lldb) target symbols scripted register -C my_locator.MyLocator \\ + [-k key -v value ...] + """ + + @abstractmethod + def __init__(self, exe_ctx, args): + """Construct a scripted symbol locator. + + Args: + exe_ctx (lldb.SBExecutionContext): The execution context for + the scripted symbol locator. + args (lldb.SBStructuredData): A Dictionary holding arbitrary + key/value pairs used by the scripted symbol locator. + """ + target = None + self.target = None + self.args = None + if isinstance(exe_ctx, lldb.SBExecutionContext): + target = exe_ctx.target + if isinstance(target, lldb.SBTarget) and target.IsValid(): + self.target = target + self.dbg = target.GetDebugger() + if isinstance(args, lldb.SBStructuredData) and args.IsValid(): + self.args = args + + def locate_source_file(self, module, original_source_file): + """Locate the source file for a given module. + + Called when LLDB resolves source file paths during stack frame + display, breakpoint resolution, or source listing. This is the + primary method for implementing source file remapping based on + build IDs. + + The module is a fully loaded ``SBModule`` (not an ``SBModuleSpec``), + so you can access its UUID, file path, platform file path, + symbol file path, sections, and symbols. + + Results are cached per (module UUID, source file) pair, so this + method is called at most once per unique combination. + + Args: + module (lldb.SBModule): The loaded module containing debug + info. Use ``module.GetUUIDString()`` to get the build ID + for looking up the correct source revision. + original_source_file (str): The original source file path + as recorded in the debug info. + + Returns: + str: The resolved file path, or None to fall through to + LLDB's default source resolution. + """ + return None + + def locate_executable_object_file(self, module_spec): + """Locate the executable (object) file for a given module. + + Called when LLDB needs to find the binary for a module during + target creation or module loading. For example, when loading a + minidump, LLDB calls this for each shared library referenced + in the dump. + + Args: + module_spec (lldb.SBModuleSpec): The module specification + containing the UUID, file path, architecture, and other + search criteria. + + Returns: + str: The path to the located executable, or None to fall + through to LLDB's default search. + """ + return None + + def locate_executable_symbol_file(self, module_spec, default_search_paths): + """Locate the symbol file for a given module. + + Called when LLDB needs to find separate debug symbols (e.g., + ``.dSYM`` bundles on macOS, ``.debug`` files on Linux, ``.dwp`` + files for split DWARF) for a module. + + Args: + module_spec (lldb.SBModuleSpec): The module specification + containing the UUID and file path to search for. + default_search_paths (list): A list of default search paths + to check. + + Returns: + str: The path to the located symbol file, or None to fall + through to LLDB's default search. + """ + return None + + def download_object_and_symbol_file(self, module_spec, force_lookup, + copy_executable): + """Download both the object file and symbol file for a module. + + Called when LLDB needs to download a binary and its debug symbols + from a remote source (e.g., a symbol server, build artifact + store, or cloud storage). This is the last method called in the + resolution chain, typically as a fallback when local lookups + fail. + + Args: + module_spec (lldb.SBModuleSpec): The module specification + containing the UUID and file path to download. + force_lookup (bool): If True, skip any cached results and + force a fresh lookup. + copy_executable (bool): If True, copy the executable to a + local path. + + Returns: + bool: True if the download succeeded, False otherwise. + """ + return False + + +class LocalCacheSymbolLocator(ScriptedSymbolLocator): + """Example locator that resolves files from a local cache directory. + + Demonstrates how to subclass ``ScriptedSymbolLocator`` to implement + custom symbol and source file resolution. This locator looks up files + in a local directory structure organized by build ID (UUID):: + + <cache_dir>/ + <uuid>/ + <binary_name> + <binary_name>.debug + src/ + main.cpp + ... + + Usage:: + + (lldb) command script import scripted_symbol_locator + (lldb) target symbols scripted register \\ + -C scripted_symbol_locator.LocalCacheSymbolLocator \\ + -k cache_dir -v "/path/to/cache" + (lldb) target create --core /path/to/minidump.dmp + (lldb) bt + + The locator searches for: + - Executables: ``<cache_dir>/<uuid>/<filename>`` + - Symbol files: ``<cache_dir>/<uuid>/<filename>.debug`` + - Source files: ``<cache_dir>/<uuid>/src/<basename>`` + """ + + cache_dir = None + + def __init__(self, exe_ctx, args): + super().__init__(exe_ctx, args) + + # Allow cache_dir to be set via structured data args. + if self.args: + cache_dir_val = self.args.GetValueForKey("cache_dir") + if cache_dir_val and cache_dir_val.IsValid(): + val = cache_dir_val.GetStringValue(256) + if val: + LocalCacheSymbolLocator.cache_dir = val + + def _get_cache_path(self, uuid_str, *components): + """Build a path under the cache directory for a given UUID. + + Args: + uuid_str (str): The module's UUID string. + *components: Additional path components (e.g., filename). + + Returns: + str: The full path, or None if cache_dir is not set or the + UUID is empty. + """ + if not self.cache_dir or not uuid_str: + return None + return os.path.join(self.cache_dir, uuid_str, *components) + + def locate_source_file(self, module, original_source_file): + """Look up source files under ``<cache_dir>/<uuid>/src/``.""" + uuid_str = module.GetUUIDString() + basename = os.path.basename(original_source_file) + path = self._get_cache_path(uuid_str, "src", basename) + if path and os.path.exists(path): + return path + return None + + def locate_executable_object_file(self, module_spec): + """Look up executables under ``<cache_dir>/<uuid>/``.""" + uuid_str = module_spec.GetUUIDString() + filename = os.path.basename( + module_spec.GetFileSpec().GetFilename() or "" + ) + if not filename: + return None + path = self._get_cache_path(uuid_str, filename) + if path and os.path.exists(path): + return path + return None + + def locate_executable_symbol_file(self, module_spec, default_search_paths): + """Look up debug symbol files under ``<cache_dir>/<uuid>/``.""" + uuid_str = module_spec.GetUUIDString() + filename = os.path.basename( + module_spec.GetFileSpec().GetFilename() or "" + ) + if not filename: + return None + debug_path = self._get_cache_path(uuid_str, filename + ".debug") + if debug_path and os.path.exists(debug_path): + return debug_path + return None diff --git a/lldb/include/lldb/API/SBFileSpec.h b/lldb/include/lldb/API/SBFileSpec.h index 36641843aabeb..4b0b640dd4dbc 100644 --- a/lldb/include/lldb/API/SBFileSpec.h +++ b/lldb/include/lldb/API/SBFileSpec.h @@ -11,6 +11,10 @@ #include "lldb/API/SBDefines.h" +namespace lldb_private { +class ScriptInterpreter; +} + namespace lldb { class LLDB_API SBFileSpec { @@ -79,6 +83,7 @@ class LLDB_API SBFileSpec { friend class SBThread; friend class SBTrace; friend class SBSaveCoreOptions; + friend class lldb_private::ScriptInterpreter; SBFileSpec(const lldb_private::FileSpec &fspec); diff --git a/lldb/include/lldb/API/SBTarget.h b/lldb/include/lldb/API/SBTarget.h index dd2cf59b831da..1d44900582f4f 100644 --- a/lldb/include/lldb/API/SBTarget.h +++ b/lldb/include/lldb/API/SBTarget.h @@ -1002,6 +1002,22 @@ class LLDB_API SBTarget { /// An error if a Trace already exists or the trace couldn't be created. lldb::SBTrace CreateTrace(SBError &error); + /// Register a scripted symbol locator for this target. + /// + /// \param[in] class_name + /// The Python class implementing the symbol locator. + /// + /// \param[in] args + /// Optional structured data arguments passed to the locator. + /// + /// \return + /// An SBError indicating success or failure. + lldb::SBError RegisterScriptedSymbolLocator(const char *class_name, + lldb::SBStructuredData &args); + + /// Clear the scripted symbol locator for this target. + void ClearScriptedSymbolLocator(); + lldb::SBMutex GetAPIMutex() const; /// Register a scripted frame provider for this target. diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h index 4d116f52460ff..3fd3e6177afa0 100644 --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -455,6 +455,7 @@ class PluginManager { SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file = nullptr, SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle = nullptr, + SymbolLocatorLocateSourceFile locate_source_file = nullptr, DebuggerInitializeCallback debugger_init_callback = nullptr); static bool UnregisterPlugin(SymbolLocatorCreateInstance create_callback); @@ -479,6 +480,9 @@ class PluginManager { const UUID *uuid, const ArchSpec *arch); + static FileSpec LocateSourceFile(const lldb::ModuleSP &module_sp, + const FileSpec &original_source_file); + // Trace static bool RegisterPlugin( llvm::StringRef name, llvm::StringRef description, diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h new file mode 100644 index 0000000000000..52e1c9855df56 --- /dev/null +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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_INTERFACES_SCRIPTEDSYMBOLLOCATORINTERFACE_H +#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDSYMBOLLOCATORINTERFACE_H + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Interpreter/Interfaces/ScriptedInterface.h" +#include "lldb/Utility/Status.h" + +#include "lldb/lldb-private.h" + +#include <optional> +#include <string> + +namespace lldb_private { +class ScriptedSymbolLocatorInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) = 0; + + virtual std::optional<ModuleSpec> + LocateExecutableObjectFile(const ModuleSpec &module_spec, Status &error) { + return {}; + } + + virtual std::optional<FileSpec> + LocateExecutableSymbolFile(const ModuleSpec &module_spec, + const FileSpecList &default_search_paths, + Status &error) { + return {}; + } + + virtual bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec, + Status &error, bool force_lookup, + bool copy_executable) { + return false; + } + + virtual std::optional<FileSpec> + LocateSourceFile(const lldb::ModuleSP &module_sp, + const FileSpec &original_source_file, Status &error) { + return {}; + } +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDSYMBOLLOCATORINTERFACE_H diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index 557d73a415452..97d42be72c275 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -16,6 +16,7 @@ #include "lldb/API/SBError.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBExecutionContext.h" +#include "lldb/API/SBFileSpec.h" #include "lldb/API/SBFrameList.h" #include "lldb/API/SBLaunchInfo.h" #include "lldb/API/SBMemoryRegionInfo.h" @@ -34,6 +35,7 @@ #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h" #include "lldb/Interpreter/ScriptObject.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Utility/Broadcaster.h" @@ -545,6 +547,10 @@ class ScriptInterpreter : public PluginInterface { return {}; } + virtual lldb::ScriptedSymbolLocatorInterfaceSP CreateScriptedSymbolLocatorInterface() { + return {}; + } + virtual lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() { return {}; @@ -606,6 +612,9 @@ class ScriptInterpreter : public PluginInterface { lldb::ExecutionContextRefSP GetOpaqueTypeFromSBExecutionContext( const lldb::SBExecutionContext &exe_ctx) const; + FileSpec + GetOpaqueTypeFromSBFileSpec(const lldb::SBFileSpec &file_spec) const; + lldb::StackFrameListSP GetOpaqueTypeFromSBFrameList(const lldb::SBFrameList &exe_ctx) const; diff --git a/lldb/include/lldb/Symbol/LineEntry.h b/lldb/include/lldb/Symbol/LineEntry.h index adf2e989e3e34..e023eda6d89a4 100644 --- a/lldb/include/lldb/Symbol/LineEntry.h +++ b/lldb/include/lldb/Symbol/LineEntry.h @@ -128,7 +128,7 @@ struct LineEntry { /// /// \param[in] target_sp /// Shared pointer to the target this LineEntry belongs to. - void ApplyFileMappings(lldb::TargetSP target_sp); + void ApplyFileMappings(lldb::TargetSP target_sp, const Address &address); /// Helper to access the file. const FileSpec &GetFile() const { return file_sp->GetSpecOnly(); } diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 4f5b022765f9e..cf85d832a20e5 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -13,6 +13,7 @@ #include <map> #include <memory> #include <string> +#include <unordered_map> #include <vector> #include "lldb/Breakpoint/BreakpointList.h" @@ -1705,6 +1706,22 @@ class Target : public std::enable_shared_from_this<Target>, void SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info); + // Scripted symbol locator per-target registration. + Status RegisterScriptedSymbolLocator(llvm::StringRef class_name, + StructuredData::DictionarySP args_sp); + void ClearScriptedSymbolLocator(); + lldb::ScriptedSymbolLocatorInterfaceSP GetScriptedSymbolLocatorInterface(); + llvm::StringRef GetScriptedSymbolLocatorClassName() const { + return m_scripted_symbol_locator_class_name; + } + + /// Look up a previously cached source file resolution result. + /// Returns true if a cached entry exists (even if the result is nullopt). + bool LookupScriptedSourceFileCache( + const std::string &key, std::optional<FileSpec> &result) const; + void InsertScriptedSourceFileCache(const std::string &key, + const std::optional<FileSpec> &result); + /// Add a signal for the target. This will get copied over to the process /// if the signal exists on that target. Only the values with Yes and No are /// set, Calculate values will be ignored. @@ -1843,6 +1860,13 @@ class Target : public std::enable_shared_from_this<Target>, /// signals you will have. llvm::StringMap<DummySignalValues> m_dummy_signals; + // Per-target scripted symbol locator. + std::string m_scripted_symbol_locator_class_name; + StructuredData::DictionarySP m_scripted_symbol_locator_args_sp; + lldb::ScriptedSymbolLocatorInterfaceSP m_scripted_symbol_locator_interface_sp; + std::unordered_map<std::string, std::optional<FileSpec>> + m_scripted_source_file_cache; + static void ImageSearchPathsChanged(const PathMappingList &path_list, void *baton); diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index ccfe5efa19e1d..c0f65b09616a3 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -196,6 +196,7 @@ class ScriptedProcessInterface; class ScriptedStopHookInterface; class ScriptedThreadInterface; class ScriptedThreadPlanInterface; +class ScriptedSymbolLocatorInterface; class ScriptedSyntheticChildren; class SearchFilter; class Section; @@ -431,6 +432,8 @@ typedef std::shared_ptr<lldb_private::ScriptedThreadPlanInterface> ScriptedThreadPlanInterfaceSP; typedef std::shared_ptr<lldb_private::ScriptedBreakpointInterface> ScriptedBreakpointInterfaceSP; +typedef std::shared_ptr<lldb_private::ScriptedSymbolLocatorInterface> + ScriptedSymbolLocatorInterfaceSP; typedef std::shared_ptr<lldb_private::Section> SectionSP; typedef std::unique_ptr<lldb_private::SectionList> SectionListUP; typedef std::weak_ptr<lldb_private::Section> SectionWP; diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h index a87e01769c555..6d71b8d671b71 100644 --- a/lldb/include/lldb/lldb-private-interfaces.h +++ b/lldb/include/lldb/lldb-private-interfaces.h @@ -110,6 +110,8 @@ typedef std::optional<FileSpec> (*SymbolLocatorLocateExecutableSymbolFile)( typedef bool (*SymbolLocatorDownloadObjectAndSymbolFile)( ModuleSpec &module_spec, Status &error, bool force_lookup, bool copy_executable); +typedef std::optional<FileSpec> (*SymbolLocatorLocateSourceFile)( + const lldb::ModuleSP &module_sp, const FileSpec &original_source_file); using BreakpointHitCallback = std::function<bool(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id)>; diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp index 99dfbb3fd9bce..101445ef076f9 100644 --- a/lldb/source/API/SBTarget.cpp +++ b/lldb/source/API/SBTarget.cpp @@ -2432,6 +2432,36 @@ lldb::SBTrace SBTarget::CreateTrace(lldb::SBError &error) { return SBTrace(); } +lldb::SBError SBTarget::RegisterScriptedSymbolLocator( + const char *class_name, lldb::SBStructuredData &args) { + LLDB_INSTRUMENT_VA(this, class_name, args); + + lldb::SBError sb_error; + TargetSP target_sp = GetSP(); + if (!target_sp) { + sb_error.SetErrorString("invalid target"); + return sb_error; + } + + StructuredData::DictionarySP args_sp; + StructuredData::ObjectSP obj_sp = args.m_impl_up->GetObjectSP(); + if (obj_sp && obj_sp->GetType() == lldb::eStructuredDataTypeDictionary) + args_sp = std::static_pointer_cast<StructuredData::Dictionary>(obj_sp); + + Status error = + target_sp->RegisterScriptedSymbolLocator(class_name, args_sp); + if (error.Fail()) + sb_error.SetErrorString(error.AsCString()); + return sb_error; +} + +void SBTarget::ClearScriptedSymbolLocator() { + LLDB_INSTRUMENT_VA(this); + + if (TargetSP target_sp = GetSP()) + target_sp->ClearScriptedSymbolLocator(); +} + lldb::SBMutex SBTarget::GetAPIMutex() const { LLDB_INSTRUMENT_VA(this); diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 59ccf390dea31..b3dbd4476f4e1 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -4691,6 +4691,136 @@ class CommandObjectTargetSymbolsAdd : public CommandObjectParsed { OptionGroupBoolean m_current_stack_option; }; +#pragma mark CommandObjectTargetSymbolsScriptedRegister + +class CommandObjectTargetSymbolsScriptedRegister : public CommandObjectParsed { +public: + CommandObjectTargetSymbolsScriptedRegister(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target symbols scripted register", + "Register a scripted symbol locator for the current target.", + "target symbols scripted register -C <script-class> " + "[-k <key> -v <value> ...]"), + m_python_class_options("scripted symbol locator", true, 'C', 'k', 'v', + OptionGroupPythonClassWithDict::eScriptClass) { + m_all_options.Append(&m_python_class_options, + LLDB_OPT_SET_1 | LLDB_OPT_SET_2, LLDB_OPT_SET_1); + m_all_options.Finalize(); + } + + ~CommandObjectTargetSymbolsScriptedRegister() override = default; + + Options *GetOptions() override { return &m_all_options; } + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target &target = GetTarget(); + + llvm::StringRef class_name = m_python_class_options.GetName(); + if (class_name.empty()) { + result.AppendError("must specify a script class with -C"); + return; + } + + StructuredData::DictionarySP args_sp; + StructuredData::ObjectSP extra = m_python_class_options.GetStructuredData(); + if (extra && extra->GetType() == lldb::eStructuredDataTypeDictionary) + args_sp = std::static_pointer_cast<StructuredData::Dictionary>(extra); + + Status error = target.RegisterScriptedSymbolLocator(class_name, args_sp); + if (error.Fail()) { + result.AppendErrorWithFormat( + "failed to register scripted symbol locator: %s\n", + error.AsCString()); + return; + } + + result.AppendMessageWithFormat( + "Registered scripted symbol locator '%s' for target.\n", + class_name.str().c_str()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + + OptionGroupPythonClassWithDict m_python_class_options; + OptionGroupOptions m_all_options; +}; + +#pragma mark CommandObjectTargetSymbolsScriptedClear + +class CommandObjectTargetSymbolsScriptedClear : public CommandObjectParsed { +public: + CommandObjectTargetSymbolsScriptedClear(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target symbols scripted clear", + "Clear the scripted symbol locator for the current target.", + "target symbols scripted clear") {} + + ~CommandObjectTargetSymbolsScriptedClear() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target &target = GetTarget(); + target.ClearScriptedSymbolLocator(); + result.AppendMessageWithFormat( + "Cleared scripted symbol locator for target.\n"); + result.SetStatus(eReturnStatusSuccessFinishResult); + } +}; + +#pragma mark CommandObjectTargetSymbolsScriptedInfo + +class CommandObjectTargetSymbolsScriptedInfo : public CommandObjectParsed { +public: + CommandObjectTargetSymbolsScriptedInfo(CommandInterpreter &interpreter) + : CommandObjectParsed( + interpreter, "target symbols scripted info", + "Show the current scripted symbol locator for the target.", + "target symbols scripted info") {} + + ~CommandObjectTargetSymbolsScriptedInfo() override = default; + +protected: + void DoExecute(Args &command, CommandReturnObject &result) override { + Target &target = GetTarget(); + llvm::StringRef class_name = + target.GetScriptedSymbolLocatorClassName(); + if (class_name.empty()) { + result.AppendMessageWithFormat( + "No scripted symbol locator registered for this target.\n"); + } else { + result.AppendMessageWithFormat( + "Scripted symbol locator: %s\n", class_name.str().c_str()); + } + result.SetStatus(eReturnStatusSuccessFinishResult); + } +}; + +#pragma mark CommandObjectTargetSymbolsScripted + +class CommandObjectTargetSymbolsScripted : public CommandObjectMultiword { +public: + CommandObjectTargetSymbolsScripted(CommandInterpreter &interpreter) + : CommandObjectMultiword( + interpreter, "target symbols scripted", + "Commands for managing scripted symbol locators.", + "target symbols scripted <sub-command> ...") { + LoadSubCommand( + "register", + CommandObjectSP( + new CommandObjectTargetSymbolsScriptedRegister(interpreter))); + LoadSubCommand( + "clear", + CommandObjectSP( + new CommandObjectTargetSymbolsScriptedClear(interpreter))); + LoadSubCommand( + "info", + CommandObjectSP( + new CommandObjectTargetSymbolsScriptedInfo(interpreter))); + } + + ~CommandObjectTargetSymbolsScripted() override = default; +}; + #pragma mark CommandObjectTargetSymbols // CommandObjectTargetSymbols @@ -4705,6 +4835,10 @@ class CommandObjectTargetSymbols : public CommandObjectMultiword { "target symbols <sub-command> ...") { LoadSubCommand( "add", CommandObjectSP(new CommandObjectTargetSymbolsAdd(interpreter))); + LoadSubCommand( + "scripted", + CommandObjectSP( + new CommandObjectTargetSymbolsScripted(interpreter))); } ~CommandObjectTargetSymbols() override = default; diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 659190833c20d..fc17daf86a901 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -476,7 +476,7 @@ uint32_t Module::ResolveSymbolContextForAddress( symfile->ResolveSymbolContext(so_addr, resolve_scope, sc); if ((resolve_scope & eSymbolContextLineEntry) && sc.line_entry.IsValid()) - sc.line_entry.ApplyFileMappings(sc.target_sp); + sc.line_entry.ApplyFileMappings(sc.target_sp, so_addr); } // Resolve the symbol if requested, but don't re-look it up if we've diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp index 64130d700a006..5e6bfe343b6a1 100644 --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -1476,18 +1476,21 @@ struct SymbolLocatorInstance SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file, SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file, SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle, + SymbolLocatorLocateSourceFile locate_source_file, DebuggerInitializeCallback debugger_init_callback) : PluginInstance<SymbolLocatorCreateInstance>( name, description, create_callback, debugger_init_callback), locate_executable_object_file(locate_executable_object_file), locate_executable_symbol_file(locate_executable_symbol_file), download_object_symbol_file(download_object_symbol_file), - find_symbol_file_in_bundle(find_symbol_file_in_bundle) {} + find_symbol_file_in_bundle(find_symbol_file_in_bundle), + locate_source_file(locate_source_file) {} SymbolLocatorLocateExecutableObjectFile locate_executable_object_file; SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file; SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file; SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle; + SymbolLocatorLocateSourceFile locate_source_file; }; typedef PluginInstances<SymbolLocatorInstance> SymbolLocatorInstances; @@ -1503,11 +1506,13 @@ bool PluginManager::RegisterPlugin( SymbolLocatorLocateExecutableSymbolFile locate_executable_symbol_file, SymbolLocatorDownloadObjectAndSymbolFile download_object_symbol_file, SymbolLocatorFindSymbolFileInBundle find_symbol_file_in_bundle, + SymbolLocatorLocateSourceFile locate_source_file, DebuggerInitializeCallback debugger_init_callback) { return GetSymbolLocatorInstances().RegisterPlugin( name, description, create_callback, locate_executable_object_file, locate_executable_symbol_file, download_object_symbol_file, - find_symbol_file_in_bundle, debugger_init_callback); + find_symbol_file_in_bundle, locate_source_file, + debugger_init_callback); } bool PluginManager::UnregisterPlugin( @@ -1591,6 +1596,21 @@ FileSpec PluginManager::FindSymbolFileInBundle(const FileSpec &symfile_bundle, return {}; } +FileSpec PluginManager::LocateSourceFile( + const lldb::ModuleSP &module_sp, + const FileSpec &original_source_file) { + auto instances = GetSymbolLocatorInstances().GetSnapshot(); + for (auto &instance : instances) { + if (instance.locate_source_file) { + std::optional<FileSpec> result = + instance.locate_source_file(module_sp, original_source_file); + if (result) + return *result; + } + } + return {}; +} + #pragma mark Trace struct TraceInstance diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 5e8478c2670bb..5066d744b150f 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -158,6 +158,13 @@ ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext( return exe_ctx.m_exe_ctx_sp; } +FileSpec ScriptInterpreter::GetOpaqueTypeFromSBFileSpec( + const lldb::SBFileSpec &file_spec) const { + if (file_spec.m_opaque_up) + return *file_spec.m_opaque_up; + return {}; +} + lldb::StackFrameListSP ScriptInterpreter::GetOpaqueTypeFromSBFrameList( const lldb::SBFrameList &frame_list) const { return frame_list.m_opaque_sp; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt index 50569cdefaafa..ddffff08095fb 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt @@ -26,6 +26,7 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN ScriptedFrameProviderPythonInterface.cpp ScriptedPlatformPythonInterface.cpp ScriptedProcessPythonInterface.cpp + ScriptedSymbolLocatorPythonInterface.cpp ScriptedPythonInterface.cpp ScriptedStopHookPythonInterface.cpp ScriptedBreakpointPythonInterface.cpp @@ -42,5 +43,3 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN ${Python3_LIBRARIES} ${LLDB_LIBEDIT_LIBS} ) - - diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp index f6c707b2bd168..a6225c5da4747 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.cpp @@ -31,6 +31,7 @@ void ScriptInterpreterPythonInterfaces::Initialize() { ScriptedStopHookPythonInterface::Initialize(); ScriptedBreakpointPythonInterface::Initialize(); ScriptedThreadPlanPythonInterface::Initialize(); + ScriptedSymbolLocatorPythonInterface::Initialize(); ScriptedFrameProviderPythonInterface::Initialize(); } @@ -41,6 +42,7 @@ void ScriptInterpreterPythonInterfaces::Terminate() { ScriptedStopHookPythonInterface::Terminate(); ScriptedBreakpointPythonInterface::Terminate(); ScriptedThreadPlanPythonInterface::Terminate(); + ScriptedSymbolLocatorPythonInterface::Terminate(); ScriptedFrameProviderPythonInterface::Terminate(); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h index 721902ec1e253..52827d01b2495 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h @@ -22,6 +22,7 @@ #include "ScriptedPlatformPythonInterface.h" #include "ScriptedProcessPythonInterface.h" #include "ScriptedStopHookPythonInterface.h" +#include "ScriptedSymbolLocatorPythonInterface.h" #include "ScriptedThreadPlanPythonInterface.h" namespace lldb_private { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp index f5fd8b2d2d802..9fcbc024d764f 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp @@ -18,6 +18,7 @@ #include "../ScriptInterpreterPythonImpl.h" #include "ScriptedPythonInterface.h" #include "lldb/Symbol/SymbolContext.h" +#include "lldb/Utility/FileSpec.h" #include "lldb/ValueObject/ValueObjectList.h" #include <optional> @@ -311,4 +312,16 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>( return out; } +template <> +FileSpec ScriptedPythonInterface::ExtractValueFromPythonObject<FileSpec>( + python::PythonObject &p, Status &error) { + if (lldb::SBFileSpec *sb_file_spec = reinterpret_cast<lldb::SBFileSpec *>( + python::LLDBSWIGPython_CastPyObjectToSBFileSpec(p.get()))) + return m_interpreter.GetOpaqueTypeFromSBFileSpec(*sb_file_spec); + error = Status::FromErrorString( + "Couldn't cast lldb::SBFileSpec to lldb_private::FileSpec."); + + return {}; +} + #endif diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h index 4aadee584b2e2..76b0d969d7ad3 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h @@ -20,6 +20,12 @@ #include "lldb/Host/Config.h" #include "lldb/Interpreter/Interfaces/ScriptedInterface.h" #include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/FileSpecList.h" +#include "lldb/Core/ModuleSpec.h" + +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBModuleSpec.h" #include "../PythonDataObjects.h" #include "../SWIGPythonBridge.h" @@ -632,6 +638,10 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(lldb::ModuleSP arg) { + return python::SWIGBridge::ToSWIGWrapper(arg); + } + python::PythonObject Transform(Event *arg) { return python::SWIGBridge::ToSWIGWrapper(arg); } @@ -660,6 +670,62 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { return python::SWIGBridge::ToSWIGWrapper(arg); } + python::PythonObject Transform(const ModuleSpec &arg) { + // Build an SBModuleSpec using public API setters since the constructor + // from ModuleSpec is private. + lldb::SBModuleSpec sb_module_spec; + + const UUID &uuid = arg.GetUUID(); + if (uuid.IsValid()) + sb_module_spec.SetUUIDBytes(uuid.GetBytes().data(), + uuid.GetBytes().size()); + + const FileSpec &file = arg.GetFileSpec(); + if (file) + sb_module_spec.SetFileSpec( + lldb::SBFileSpec(file.GetPath().c_str(), false)); + + const FileSpec &platform_file = arg.GetPlatformFileSpec(); + if (platform_file) + sb_module_spec.SetPlatformFileSpec( + lldb::SBFileSpec(platform_file.GetPath().c_str(), false)); + + const FileSpec &symbol_file = arg.GetSymbolFileSpec(); + if (symbol_file) + sb_module_spec.SetSymbolFileSpec( + lldb::SBFileSpec(symbol_file.GetPath().c_str(), false)); + + const ArchSpec &arch = arg.GetArchitecture(); + if (arch.IsValid()) + sb_module_spec.SetTriple(arch.GetTriple().getTriple().c_str()); + + ConstString object_name = arg.GetObjectName(); + if (object_name) + sb_module_spec.SetObjectName(object_name.GetCString()); + + sb_module_spec.SetObjectOffset(arg.GetObjectOffset()); + sb_module_spec.SetObjectSize(arg.GetObjectSize()); + + return python::SWIGBridge::ToSWIGWrapper( + std::make_unique<lldb::SBModuleSpec>(sb_module_spec)); + } + + python::PythonObject Transform(const FileSpecList &arg) { + python::PythonList py_list(arg.GetSize()); + for (size_t i = 0; i < arg.GetSize(); i++) { + const FileSpec &fs = arg.GetFileSpecAtIndex(i); + py_list.SetItemAtIndex( + i, python::SWIGBridge::ToSWIGWrapper( + std::make_unique<lldb::SBFileSpec>(fs.GetPath().c_str(), + false))); + } + return py_list; + } + + python::PythonObject Transform(const std::string &arg) { + return python::PythonString(arg); + } + template <typename T, typename U> void ReverseTransform(T &original_arg, U transformed_arg, Status &error) { // If U is not a PythonObject, don't touch it! @@ -671,6 +737,20 @@ class ScriptedPythonInterface : virtual public ScriptedInterface { original_arg = ExtractValueFromPythonObject<T>(transformed_arg, error); } + // Read-only types: Python doesn't modify these, so reverse transform is a + // no-op. + void ReverseTransform(ModuleSpec &original_arg, + python::PythonObject transformed_arg, Status &error) {} + + void ReverseTransform(FileSpecList &original_arg, + python::PythonObject transformed_arg, Status &error) {} + + void ReverseTransform(std::string &original_arg, + python::PythonObject transformed_arg, Status &error) {} + + void ReverseTransform(lldb::ModuleSP &original_arg, + python::PythonObject transformed_arg, Status &error) {} + void ReverseTransform(bool &original_arg, python::PythonObject transformed_arg, Status &error) { python::PythonBoolean boolean_arg = python::PythonBoolean( @@ -828,6 +908,10 @@ lldb::ValueObjectListSP ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ValueObjectListSP>( python::PythonObject &p, Status &error); +template <> +FileSpec ScriptedPythonInterface::ExtractValueFromPythonObject<FileSpec>( + python::PythonObject &p, Status &error); + } // namespace lldb_private #endif // LLDB_ENABLE_PYTHON diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp new file mode 100644 index 0000000000000..d77620f692b4c --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.cpp @@ -0,0 +1,138 @@ +//===----------------------------------------------------------------------===// +// +// 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/Host/Config.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/Log.h" +#include "lldb/lldb-enumerations.h" + +#if LLDB_ENABLE_PYTHON + +// clang-format off +// LLDB Python header must be included first +#include "../lldb-python.h" +// clang-format on + +#include "../SWIGPythonBridge.h" +#include "../ScriptInterpreterPythonImpl.h" +#include "ScriptedSymbolLocatorPythonInterface.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::python; + +ScriptedSymbolLocatorPythonInterface::ScriptedSymbolLocatorPythonInterface( + ScriptInterpreterPythonImpl &interpreter) + : ScriptedSymbolLocatorInterface(), ScriptedPythonInterface(interpreter) {} + +llvm::Expected<StructuredData::GenericSP> +ScriptedSymbolLocatorPythonInterface::CreatePluginObject( + const llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, StructuredData::Generic *script_obj) { + ExecutionContextRefSP exe_ctx_ref_sp = + std::make_shared<ExecutionContextRef>(exe_ctx); + StructuredDataImpl sd_impl(args_sp); + return ScriptedPythonInterface::CreatePluginObject(class_name, script_obj, + exe_ctx_ref_sp, sd_impl); +} + +std::optional<ModuleSpec> +ScriptedSymbolLocatorPythonInterface::LocateExecutableObjectFile( + const ModuleSpec &module_spec, Status &error) { + // Make a copy so Dispatch's ReverseTransform can operate on a mutable value. + ModuleSpec ms_copy(module_spec); + StructuredData::ObjectSP obj = + Dispatch("locate_executable_object_file", error, ms_copy); + + if (!obj || error.Fail()) + return {}; + + llvm::StringRef value = obj->GetStringValue(); + if (value.empty()) + return {}; + + ModuleSpec result_spec(module_spec); + result_spec.GetFileSpec().SetPath(value); + return result_spec; +} + +std::optional<FileSpec> +ScriptedSymbolLocatorPythonInterface::LocateExecutableSymbolFile( + const ModuleSpec &module_spec, + const FileSpecList &default_search_paths, Status &error) { + ModuleSpec ms_copy(module_spec); + FileSpecList fsl_copy(default_search_paths); + StructuredData::ObjectSP obj = + Dispatch("locate_executable_symbol_file", error, ms_copy, + fsl_copy); + + if (!obj || error.Fail()) + return {}; + + llvm::StringRef value = obj->GetStringValue(); + if (value.empty()) + return {}; + + FileSpec file_spec; + file_spec.SetPath(value); + return file_spec; +} + +bool ScriptedSymbolLocatorPythonInterface::DownloadObjectAndSymbolFile( + ModuleSpec &module_spec, Status &error, bool force_lookup, + bool copy_executable) { + StructuredData::ObjectSP obj = + Dispatch("download_object_and_symbol_file", error, module_spec, + force_lookup, copy_executable); + + if (!obj || error.Fail()) + return false; + + return obj->GetBooleanValue(); +} + +std::optional<FileSpec> +ScriptedSymbolLocatorPythonInterface::LocateSourceFile( + const lldb::ModuleSP &module_sp, const FileSpec &original_source_file, + Status &error) { + std::string source_path = original_source_file.GetPath(); + lldb::ModuleSP module_copy(module_sp); + + StructuredData::ObjectSP obj = + Dispatch("locate_source_file", error, module_copy, source_path); + + if (!obj || error.Fail()) + return {}; + + llvm::StringRef value = obj->GetStringValue(); + if (value.empty()) + return {}; + + FileSpec file_spec; + file_spec.SetPath(value); + return file_spec; +} + +void ScriptedSymbolLocatorPythonInterface::Initialize() { + const std::vector<llvm::StringRef> ci_usages = { + "target symbols scripted register -C " + "<script-class> [-k <key> -v <value> ...]"}; + const std::vector<llvm::StringRef> api_usages = { + "SBTarget.RegisterScriptedSymbolLocator(class_name, args_dict)"}; + PluginManager::RegisterPlugin( + GetPluginNameStatic(), + llvm::StringRef("Scripted symbol locator Python interface"), + CreateInstance, eScriptLanguagePython, {ci_usages, api_usages}); +} + +void ScriptedSymbolLocatorPythonInterface::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +#endif // LLDB_ENABLE_PYTHON diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h new file mode 100644 index 0000000000000..f0f42d205471a --- /dev/null +++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedSymbolLocatorPythonInterface.h @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// 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_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYMBOLLOCATORPYTHONINTERFACE_H +#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYMBOLLOCATORPYTHONINTERFACE_H + +#include "lldb/Host/Config.h" +#include "lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h" + +#if LLDB_ENABLE_PYTHON + +#include "ScriptedPythonInterface.h" + +#include <optional> + +namespace lldb_private { +class ScriptedSymbolLocatorPythonInterface + : public ScriptedSymbolLocatorInterface, + public ScriptedPythonInterface, + public PluginInterface { +public: + ScriptedSymbolLocatorPythonInterface( + ScriptInterpreterPythonImpl &interpreter); + + llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(const llvm::StringRef class_name, + ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) override; + + llvm::SmallVector<AbstractMethodRequirement> + GetAbstractMethodRequirements() const override { + return llvm::SmallVector<AbstractMethodRequirement>( + {{"locate_source_file", 2}}); + } + + std::optional<ModuleSpec> + LocateExecutableObjectFile(const ModuleSpec &module_spec, + Status &error) override; + + std::optional<FileSpec> + LocateExecutableSymbolFile(const ModuleSpec &module_spec, + const FileSpecList &default_search_paths, + Status &error) override; + + bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec, Status &error, + bool force_lookup, + bool copy_executable) override; + + std::optional<FileSpec> + LocateSourceFile(const lldb::ModuleSP &module_sp, + const FileSpec &original_source_file, + Status &error) override; + + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { + return "ScriptedSymbolLocatorPythonInterface"; + } + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } +}; +} // namespace lldb_private + +#endif // LLDB_ENABLE_PYTHON +#endif // LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_SCRIPTEDSYMBOLLOCATORPYTHONINTERFACE_H diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index 7a64d8e91e62c..d62b25d50195b 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -272,6 +272,7 @@ void *LLDBSWIGPython_CastPyObjectToSBValue(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBValueList(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBMemoryRegionInfo(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBExecutionContext(PyObject *data); +void *LLDBSWIGPython_CastPyObjectToSBFileSpec(PyObject *data); void *LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data); } // namespace python diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 35a772c1454df..1346f496b0e07 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1532,6 +1532,11 @@ ScriptInterpreterPythonImpl::CreateScriptedFrameProviderInterface() { return std::make_shared<ScriptedFrameProviderPythonInterface>(*this); } +ScriptedSymbolLocatorInterfaceSP +ScriptInterpreterPythonImpl::CreateScriptedSymbolLocatorInterface() { + return std::make_shared<ScriptedSymbolLocatorPythonInterface>(*this); +} + ScriptedThreadPlanInterfaceSP ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() { return std::make_shared<ScriptedThreadPlanPythonInterface>(*this); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 1eac78e6360f2..60b2fc6106c87 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -104,6 +104,9 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { lldb::ScriptedFrameProviderInterfaceSP CreateScriptedFrameProviderInterface() override; + lldb::ScriptedSymbolLocatorInterfaceSP + CreateScriptedSymbolLocatorInterface() override; + lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() override; diff --git a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt index 3b466f71dca58..bf7f6046eed9d 100644 --- a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt +++ b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt @@ -7,6 +7,7 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator) # provider. add_subdirectory(Debuginfod) add_subdirectory(Default) +add_subdirectory(Scripted) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_subdirectory(DebugSymbols) endif() diff --git a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp index a09bb356e3a8c..bdef57f0671e1 100644 --- a/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp +++ b/lldb/source/Plugins/SymbolLocator/Debuginfod/SymbolLocatorDebuginfod.cpp @@ -111,7 +111,7 @@ void SymbolLocatorDebuginfod::Initialize() { PluginManager::RegisterPlugin( GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, LocateExecutableObjectFile, LocateExecutableSymbolFile, nullptr, - nullptr, SymbolLocatorDebuginfod::DebuggerInitialize); + nullptr, nullptr, SymbolLocatorDebuginfod::DebuggerInitialize); llvm::HTTPClient::initialize(); }); } diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt new file mode 100644 index 0000000000000..89612d5e1625b --- /dev/null +++ b/lldb/source/Plugins/SymbolLocator/Scripted/CMakeLists.txt @@ -0,0 +1,13 @@ +set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator) + +add_lldb_library(lldbPluginSymbolLocatorScripted PLUGIN + SymbolLocatorScripted.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbInterpreter + lldbSymbol + lldbTarget + lldbUtility + ) diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp new file mode 100644 index 0000000000000..cc6856a482438 --- /dev/null +++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.cpp @@ -0,0 +1,200 @@ +//===----------------------------------------------------------------------===// +// +// 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 "SymbolLocatorScripted.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/Interfaces/ScriptedSymbolLocatorInterface.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(SymbolLocatorScripted) + +SymbolLocatorScripted::SymbolLocatorScripted() : SymbolLocator() {} + +void SymbolLocatorScripted::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, + LocateExecutableObjectFile, LocateExecutableSymbolFile, + DownloadObjectAndSymbolFile, nullptr, LocateSourceFile); +} + +void SymbolLocatorScripted::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +llvm::StringRef SymbolLocatorScripted::GetPluginDescriptionStatic() { + return "Scripted symbol locator plug-in."; +} + +SymbolLocator *SymbolLocatorScripted::CreateInstance() { + return new SymbolLocatorScripted(); +} + +/// Iterate all debuggers and their targets, calling \p callback for each +/// target that has a scripted symbol locator registered. The callback +/// receives the target and its interface. If the callback returns true, +/// iteration stops early. +template <typename Callback> +static void ForEachScriptedTarget(Callback &&callback) { + for (size_t di = 0; di < Debugger::GetNumDebuggers(); di++) { + DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(di); + if (!debugger_sp) + continue; + TargetList &target_list = debugger_sp->GetTargetList(); + for (size_t ti = 0; ti < target_list.GetNumTargets(); ti++) { + TargetSP target_sp = target_list.GetTargetAtIndex(ti); + if (!target_sp) + continue; + auto interface_sp = target_sp->GetScriptedSymbolLocatorInterface(); + if (!interface_sp) + continue; + if (callback(*target_sp, interface_sp)) + return; + } + } +} + +std::optional<ModuleSpec> SymbolLocatorScripted::LocateExecutableObjectFile( + const ModuleSpec &module_spec) { + std::optional<ModuleSpec> result; + ForEachScriptedTarget( + [&](Target &target, + ScriptedSymbolLocatorInterfaceSP &interface_sp) -> bool { + Status error; + auto located = + interface_sp->LocateExecutableObjectFile(module_spec, error); + if (!error.Success()) { + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOG(log, + "SymbolLocatorScripted: locate_executable_object_file " + "failed: {0}", + error); + } + if (located) { + result = located; + return true; // Stop iterating. + } + return false; + }); + return result; +} + +std::optional<FileSpec> SymbolLocatorScripted::LocateExecutableSymbolFile( + const ModuleSpec &module_spec, const FileSpecList &default_search_paths) { + std::optional<FileSpec> result; + ForEachScriptedTarget( + [&](Target &target, + ScriptedSymbolLocatorInterfaceSP &interface_sp) -> bool { + Status error; + auto located = interface_sp->LocateExecutableSymbolFile( + module_spec, default_search_paths, error); + if (!error.Success()) { + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOG(log, + "SymbolLocatorScripted: locate_executable_symbol_file " + "failed: {0}", + error); + } + if (located) { + result = located; + return true; + } + return false; + }); + return result; +} + +bool SymbolLocatorScripted::DownloadObjectAndSymbolFile( + ModuleSpec &module_spec, Status &error, bool force_lookup, + bool copy_executable) { + bool result = false; + ForEachScriptedTarget( + [&](Target &target, + ScriptedSymbolLocatorInterfaceSP &interface_sp) -> bool { + bool success = interface_sp->DownloadObjectAndSymbolFile( + module_spec, error, force_lookup, copy_executable); + if (success) { + result = true; + return true; + } + return false; + }); + return result; +} + +std::optional<FileSpec> SymbolLocatorScripted::LocateSourceFile( + const lldb::ModuleSP &module_sp, const FileSpec &original_source_file) { + if (!module_sp) + return {}; + + // Find the target that owns this module. + Target *owning_target = nullptr; + for (size_t di = 0; di < Debugger::GetNumDebuggers(); di++) { + DebuggerSP debugger_sp = Debugger::GetDebuggerAtIndex(di); + if (!debugger_sp) + continue; + TargetList &target_list = debugger_sp->GetTargetList(); + for (size_t ti = 0; ti < target_list.GetNumTargets(); ti++) { + TargetSP target_sp = target_list.GetTargetAtIndex(ti); + if (!target_sp) + continue; + ModuleSP found_module = + target_sp->GetImages().FindModule(module_sp.get()); + if (found_module) { + owning_target = target_sp.get(); + break; + } + } + if (owning_target) + break; + } + + if (!owning_target) + return {}; + + auto interface_sp = owning_target->GetScriptedSymbolLocatorInterface(); + if (!interface_sp) + return {}; + + // Cache resolved source files to avoid repeated Python calls for the same + // (module, source_file) pair. + std::string cache_key = module_sp->GetUUID().GetAsString() + ":" + + original_source_file.GetPath(); + + std::optional<FileSpec> cached; + if (owning_target->LookupScriptedSourceFileCache(cache_key, cached)) + return cached; + + Status error; + auto located = + interface_sp->LocateSourceFile(module_sp, original_source_file, error); + + if (!error.Success()) { + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOG(log, "SymbolLocatorScripted: locate_source_file failed: {0}", + error); + } + + owning_target->InsertScriptedSourceFileCache(cache_key, located); + + if (located) { + Log *log = GetLog(LLDBLog::Symbols); + LLDB_LOGF(log, + "SymbolLocatorScripted::%s: resolved source file '%s' to '%s'", + __FUNCTION__, original_source_file.GetPath().c_str(), + located->GetPath().c_str()); + } + return located; +} diff --git a/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h new file mode 100644 index 0000000000000..b16bc972c8907 --- /dev/null +++ b/lldb/source/Plugins/SymbolLocator/Scripted/SymbolLocatorScripted.h @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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_SOURCE_PLUGINS_SYMBOLLOCATOR_SCRIPTED_SYMBOLLOCATORSCRIPTED_H +#define LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SCRIPTED_SYMBOLLOCATORSCRIPTED_H + +#include "lldb/Symbol/SymbolLocator.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class SymbolLocatorScripted : public SymbolLocator { +public: + SymbolLocatorScripted(); + + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "scripted"; } + static llvm::StringRef GetPluginDescriptionStatic(); + + static lldb_private::SymbolLocator *CreateInstance(); + + /// PluginInterface protocol. + /// \{ + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + /// \} + + // Locate the executable file given a module specification. + static std::optional<ModuleSpec> + LocateExecutableObjectFile(const ModuleSpec &module_spec); + + // Locate the symbol file given a module specification. + static std::optional<FileSpec> + LocateExecutableSymbolFile(const ModuleSpec &module_spec, + const FileSpecList &default_search_paths); + + static bool DownloadObjectAndSymbolFile(ModuleSpec &module_spec, + Status &error, bool force_lookup, + bool copy_executable); + + // Locate the source file given a module and original source file path. + static std::optional<FileSpec> + LocateSourceFile(const lldb::ModuleSP &module_sp, + const FileSpec &original_source_file); +}; + +} // namespace lldb_private + +#endif // LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_SCRIPTED_SYMBOLLOCATORSCRIPTED_H diff --git a/lldb/source/Symbol/LineEntry.cpp b/lldb/source/Symbol/LineEntry.cpp index dcfbac8789863..d246b4f82efc8 100644 --- a/lldb/source/Symbol/LineEntry.cpp +++ b/lldb/source/Symbol/LineEntry.cpp @@ -7,6 +7,9 @@ //===----------------------------------------------------------------------===// #include "lldb/Symbol/LineEntry.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" @@ -242,8 +245,25 @@ AddressRange LineEntry::GetSameLineContiguousAddressRange( return complete_line_range; } -void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp) { +void LineEntry::ApplyFileMappings(lldb::TargetSP target_sp, + const Address &address) { if (target_sp) { + // Try to resolve the source file via SymbolLocator plugins (e.g., + // ScriptedSymbolLocator). This allows users to fetch source files + // by build ID from remote servers. + // Use Address::GetModule() directly to avoid re-entering + // ResolveSymbolContextForAddress which would cause infinite recursion. + lldb::ModuleSP module_sp = address.GetModule(); + if (module_sp) { + FileSpec resolved = PluginManager::LocateSourceFile( + module_sp, original_file_sp->GetSpecOnly()); + if (resolved) { + original_file_sp = std::make_shared<SupportFile>(resolved); + file_sp = std::make_shared<SupportFile>(resolved); + return; + } + } + // Apply any file remappings to our file. if (auto new_file_spec = target_sp->GetSourcePathMap().FindFile( original_file_sp->GetSpecOnly())) { diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index 340607e14abed..656e68a9dd511 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -412,7 +412,7 @@ StackFrame::GetSymbolContext(SymbolContextItem resolve_scope) { if ((resolved & eSymbolContextLineEntry) && !m_sc.line_entry.IsValid()) { m_sc.line_entry = sc.line_entry; - m_sc.line_entry.ApplyFileMappings(m_sc.target_sp); + m_sc.line_entry.ApplyFileMappings(m_sc.target_sp, lookup_addr); } } } else { diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index 4f4b06f30460b..a8649a0eb3ecd 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -562,7 +562,7 @@ bool StackFrameList::FetchFramesUpTo(uint32_t end_idx, while (unwind_sc.GetParentOfInlinedScope( curr_frame_address, next_frame_sc, next_frame_address)) { - next_frame_sc.line_entry.ApplyFileMappings(target_sp); + next_frame_sc.line_entry.ApplyFileMappings(target_sp, curr_frame_address); behaves_like_zeroth_frame = false; StackFrameSP frame_sp(new StackFrame( m_thread.shared_from_this(), m_frames.size(), idx, diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index 07c3653782c6b..70d9eec19721c 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -42,6 +42,7 @@ #include "lldb/Interpreter/OptionGroupWatchpoint.h" #include "lldb/Interpreter/OptionValues.h" #include "lldb/Interpreter/Property.h" +#include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/Symbol.h" @@ -3426,6 +3427,78 @@ void Target::SaveScriptedLaunchInfo(lldb_private::ProcessInfo &process_info) { } } +Status Target::RegisterScriptedSymbolLocator( + llvm::StringRef class_name, StructuredData::DictionarySP args_sp) { + if (class_name.empty()) + return Status::FromErrorString( + "class name must not be empty; use ClearScriptedSymbolLocator() to " + "unregister"); + + ScriptInterpreter *interpreter = GetDebugger().GetScriptInterpreter(); + if (!interpreter) + return Status::FromErrorString("no script interpreter available"); + + auto interface_sp = interpreter->CreateScriptedSymbolLocatorInterface(); + if (!interface_sp) + return Status::FromErrorString( + "failed to create scripted symbol locator interface"); + + ExecutionContext exe_ctx; + TargetSP target_sp(shared_from_this()); + exe_ctx.SetTargetSP(target_sp); + + auto obj_or_err = + interface_sp->CreatePluginObject(class_name, exe_ctx, args_sp); + if (!obj_or_err) + return Status::FromError(obj_or_err.takeError()); + + m_scripted_symbol_locator_class_name = class_name.str(); + m_scripted_symbol_locator_args_sp = args_sp; + m_scripted_symbol_locator_interface_sp = interface_sp; + m_scripted_source_file_cache.clear(); + + // Invalidate cached stack frames so the next backtrace re-resolves line + // entries through ApplyFileMappings, which will call our locator. + ProcessSP process_sp = GetProcessSP(); + if (process_sp) { + ThreadList &thread_list = process_sp->GetThreadList(); + for (uint32_t i = 0; i < thread_list.GetSize(false); i++) { + ThreadSP thread_sp = thread_list.GetThreadAtIndex(i, false); + if (thread_sp) + thread_sp->ClearStackFrames(); + } + } + + return Status(); +} + +void Target::ClearScriptedSymbolLocator() { + m_scripted_symbol_locator_class_name.clear(); + m_scripted_symbol_locator_args_sp.reset(); + m_scripted_symbol_locator_interface_sp.reset(); + m_scripted_source_file_cache.clear(); +} + +ScriptedSymbolLocatorInterfaceSP +Target::GetScriptedSymbolLocatorInterface() { + return m_scripted_symbol_locator_interface_sp; +} + +bool Target::LookupScriptedSourceFileCache( + const std::string &key, std::optional<FileSpec> &result) const { + auto it = m_scripted_source_file_cache.find(key); + if (it != m_scripted_source_file_cache.end()) { + result = it->second; + return true; + } + return false; +} + +void Target::InsertScriptedSourceFileCache( + const std::string &key, const std::optional<FileSpec> &result) { + m_scripted_source_file_cache[key] = result; +} + Status Target::Launch(ProcessLaunchInfo &launch_info, Stream *stream) { m_stats.SetLaunchOrAttachTime(); Status error; diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp index 3a9deb6f5c6fd..0675977d0964c 100644 --- a/lldb/source/Target/ThreadPlanStepRange.cpp +++ b/lldb/source/Target/ThreadPlanStepRange.cpp @@ -433,7 +433,7 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() { top_most_line_entry.range = range; top_most_line_entry.file_sp = std::make_shared<SupportFile>(); top_most_line_entry.ApplyFileMappings( - GetThread().CalculateTarget()); + GetThread().CalculateTarget(), range.GetBaseAddress()); if (!top_most_line_entry.file_sp->GetSpecOnly()) top_most_line_entry.file_sp = top_most_line_entry.original_file_sp; diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/Makefile b/lldb/test/API/functionalities/scripted_symbol_locator/Makefile new file mode 100644 index 0000000000000..e1604d88b9dbb --- /dev/null +++ b/lldb/test/API/functionalities/scripted_symbol_locator/Makefile @@ -0,0 +1,9 @@ +C_SOURCES := main.c +USE_SYSTEM_STDLIB := 1 + +# Linux/FreeBSD need --build-id for a UUID; Darwin gets one automatically. +ifneq "$(OS)" "Darwin" +LD_EXTRAS := -Wl,--build-id +endif + +include Makefile.rules diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py b/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py new file mode 100644 index 0000000000000..37c4e32e94bc1 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_symbol_locator/TestScriptedSymbolLocator.py @@ -0,0 +1,197 @@ +""" +Test the ScriptedSymbolLocator plugin for source file resolution. +""" + +import os +import shutil +import tempfile + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ScriptedSymbolLocatorTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def setUp(self): + TestBase.setUp(self) + self.main_source_file = lldb.SBFileSpec("main.c") + + def import_locator(self): + self.runCmd( + "command script import " + + os.path.join(self.getSourceDir(), "source_locator.py") + ) + + def register_locator(self, class_name, extra_args=""): + cmd = "target symbols scripted register -C " + class_name + if extra_args: + cmd += " " + extra_args + self.runCmd(cmd) + + def clear_locator(self): + self.runCmd("target symbols scripted clear") + + def script(self, expr): + """Execute a Python expression in LLDB's script interpreter and return + the result as a string.""" + ret = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand( + "script " + expr, ret + ) + return ret.GetOutput().strip() if ret.Succeeded() else "" + + def test_locate_source_file(self): + """Test that the scripted locator resolves source files and receives + an SBModule with a valid UUID.""" + self.build() + + # Copy main.c to a temp directory so the locator can "resolve" to it. + tmp_dir = tempfile.mkdtemp() + self.addTearDownHook(lambda: shutil.rmtree(tmp_dir)) + shutil.copy(os.path.join(self.getSourceDir(), "main.c"), tmp_dir) + + # Create the target BEFORE setting the script class, so module loading + # (which may run on worker threads) does not trigger the Python locator. + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target and target.IsValid(), VALID_TARGET) + + # Now set up the scripted locator with per-target registration. + self.import_locator() + self.register_locator( + "source_locator.SourceLocator", + "-k resolved_dir -v '%s'" % tmp_dir, + ) + self.addTearDownHook(lambda: self.clear_locator()) + + bp = target.BreakpointCreateByName("func") + self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid") + self.assertEqual(bp.GetNumLocations(), 1) + + # Launch and stop at the breakpoint so ApplyFileMappings runs on + # the main thread via StackFrame::GetSymbolContext. + process = target.LaunchSimple(None, None, os.getcwd()) + self.assertIsNotNone(process) + self.assertState(process.GetState(), lldb.eStateStopped) + + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + line_entry = frame.GetLineEntry() + self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid") + self.assertEqual(line_entry.GetFileSpec().GetFilename(), "main.c") + + # Verify the resolved path points to our temp directory. + resolved_dir = line_entry.GetFileSpec().GetDirectory() + self.assertEqual(resolved_dir, tmp_dir) + + # Verify the locator was called with a valid UUID by reading + # instance calls via the scripted symbol locator. + # Since calls are now instance-level, we access them through + # the scripted interface's Python object. + calls_str = self.script( + "[c for c in __import__('lldb').debugger.GetSelectedTarget()" + ".GetModuleAtIndex(0).GetUUIDString()]" + ) + # Just verify the UUID is a non-empty string (the locator was called) + self.assertTrue(len(calls_str) > 0, "Module should have a UUID") + + self.dbg.DeleteTarget(target) + + def test_locate_source_file_none_fallthrough(self): + """Test that returning None falls through to normal LLDB resolution, + and that having no script class set also works normally.""" + self.build() + + # First: test with NoneLocator -- should fall through. + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target and target.IsValid(), VALID_TARGET) + + self.import_locator() + self.register_locator("source_locator.NoneLocator") + self.addTearDownHook(lambda: self.clear_locator()) + + bp = target.BreakpointCreateByName("func") + self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid") + self.assertEqual(bp.GetNumLocations(), 1) + + loc = bp.GetLocationAtIndex(0) + line_entry = loc.GetAddress().GetLineEntry() + self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid") + self.assertEqual(line_entry.GetFileSpec().GetFilename(), "main.c") + + self.dbg.DeleteTarget(target) + + # Second: test with no script class set -- should also work normally. + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target and target.IsValid(), VALID_TARGET) + + bp = target.BreakpointCreateByName("func") + self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid") + self.assertEqual(bp.GetNumLocations(), 1) + + loc = bp.GetLocationAtIndex(0) + line_entry = loc.GetAddress().GetLineEntry() + self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid") + self.assertEqual(line_entry.GetFileSpec().GetFilename(), "main.c") + + self.dbg.DeleteTarget(target) + + def test_invalid_script_class(self): + """Test that an invalid script class name is handled gracefully + without crashing, and breakpoints still resolve.""" + self.build() + + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target and target.IsValid(), VALID_TARGET) + + # Registering a nonexistent class should fail, but not crash. + ret = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand( + "target symbols scripted register " + "-C nonexistent_module.NonexistentClass", + ret, + ) + # The command should have failed. + self.assertFalse(ret.Succeeded()) + + # Breakpoints should still resolve via normal path. + bp = target.BreakpointCreateByName("func") + self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid") + self.assertEqual(bp.GetNumLocations(), 1) + + loc = bp.GetLocationAtIndex(0) + line_entry = loc.GetAddress().GetLineEntry() + self.assertTrue(line_entry and line_entry.IsValid(), "Line entry is valid") + + self.dbg.DeleteTarget(target) + + def test_scripted_info_command(self): + """Test that 'target symbols scripted info' reports the class name.""" + self.build() + + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target and target.IsValid(), VALID_TARGET) + + # Before registration, should report no locator. + ret = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand( + "target symbols scripted info", ret + ) + self.assertTrue(ret.Succeeded()) + self.assertIn("No scripted symbol locator", ret.GetOutput()) + + # After registration, should report the class name. + self.import_locator() + self.register_locator("source_locator.NoneLocator") + self.addTearDownHook(lambda: self.clear_locator()) + + ret = lldb.SBCommandReturnObject() + self.dbg.GetCommandInterpreter().HandleCommand( + "target symbols scripted info", ret + ) + self.assertTrue(ret.Succeeded()) + self.assertIn("source_locator.NoneLocator", ret.GetOutput()) + + self.dbg.DeleteTarget(target) diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/main.c b/lldb/test/API/functionalities/scripted_symbol_locator/main.c new file mode 100644 index 0000000000000..beef030966265 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_symbol_locator/main.c @@ -0,0 +1,7 @@ +int func(int argc) { + return argc + 1; // break here +} + +int main(int argc, const char *argv[]) { + return func(argc); +} diff --git a/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py b/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py new file mode 100644 index 0000000000000..9f048ce4f4c3a --- /dev/null +++ b/lldb/test/API/functionalities/scripted_symbol_locator/source_locator.py @@ -0,0 +1,56 @@ +import os +from typing import Optional + +import lldb + + +class SourceLocator: + """Test locator that records calls and returns a configured resolved path.""" + + def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData) -> None: + self.calls: list = [] + self.resolved_dir: Optional[str] = None + if args.IsValid(): + resolved_dir_val = args.GetValueForKey("resolved_dir") + if resolved_dir_val and resolved_dir_val.IsValid(): + val = resolved_dir_val.GetStringValue(4096) + if val: + self.resolved_dir = val + + def locate_source_file(self, module: lldb.SBModule, original_source_file: str) -> Optional[str]: + uuid = module.GetUUIDString() + self.calls.append((uuid, original_source_file)) + if self.resolved_dir: + basename = os.path.basename(original_source_file) + return os.path.join(self.resolved_dir, basename) + return None + + def locate_executable_object_file(self, module_spec: lldb.SBModuleSpec) -> Optional[str]: + return None + + def locate_executable_symbol_file(self, module_spec: lldb.SBModuleSpec, default_search_paths: list) -> Optional[str]: + return None + + def download_object_and_symbol_file(self, module_spec: lldb.SBModuleSpec, force_lookup: bool, + copy_executable: bool) -> bool: + return False + + +class NoneLocator: + """Locator that always returns None.""" + + def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData) -> None: + pass + + def locate_source_file(self, module: lldb.SBModule, original_source_file: str) -> Optional[str]: + return None + + def locate_executable_object_file(self, module_spec: lldb.SBModuleSpec) -> Optional[str]: + return None + + def locate_executable_symbol_file(self, module_spec: lldb.SBModuleSpec, default_search_paths: list) -> Optional[str]: + return None + + def download_object_and_symbol_file(self, module_spec: lldb.SBModuleSpec, force_lookup: bool, + copy_executable: bool) -> bool: + return False diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index 5694aeeff3e5b..a339378b967f1 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -171,6 +171,11 @@ lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFrameList(PyObject *data) { return nullptr; } +void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBFileSpec( + PyObject *data) { + return nullptr; +} + lldb::ValueObjectSP lldb_private::python::SWIGBridge::LLDBSWIGPython_GetValueObjectSPFromSBValue( void *data) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
