[Lldb-commits] [lldb] [lldb] Add ScriptedSymbolLocator plugin for source file resolution (PR #181334)
Med Ismail Bennani via lldb-commits
lldb-commits at lists.llvm.org
Fri Feb 13 08:09:41 PST 2026
================
@@ -0,0 +1,278 @@
+//===-- ScriptedSymbolLocatorPythonInterface.cpp
+//---------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Host/Config.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"
+
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBModuleSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::python;
+using Locker = ScriptInterpreterPythonImpl::Locker;
+
+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);
+}
+
+/// Helper to convert an internal ModuleSpec to a Python SBModuleSpec object.
+/// Must be called with the GIL held.
+static PythonObject ToSWIGModuleSpec(const ModuleSpec &module_spec) {
+ // Build an SBModuleSpec using public API setters since the constructor
+ // from ModuleSpec is private.
+ SBModuleSpec sb_module_spec;
+
+ const UUID &uuid = module_spec.GetUUID();
+ if (uuid.IsValid())
+ sb_module_spec.SetUUIDBytes(uuid.GetBytes().data(), uuid.GetBytes().size());
+
+ const FileSpec &file = module_spec.GetFileSpec();
+ if (file)
+ sb_module_spec.SetFileSpec(SBFileSpec(file.GetPath().c_str(), false));
+
+ const FileSpec &platform_file = module_spec.GetPlatformFileSpec();
+ if (platform_file)
+ sb_module_spec.SetPlatformFileSpec(
+ SBFileSpec(platform_file.GetPath().c_str(), false));
+
+ const FileSpec &symbol_file = module_spec.GetSymbolFileSpec();
+ if (symbol_file)
+ sb_module_spec.SetSymbolFileSpec(
+ SBFileSpec(symbol_file.GetPath().c_str(), false));
+
+ const ArchSpec &arch = module_spec.GetArchitecture();
+ if (arch.IsValid())
+ sb_module_spec.SetTriple(arch.GetTriple().getTriple().c_str());
+
+ ConstString object_name = module_spec.GetObjectName();
+ if (object_name)
+ sb_module_spec.SetObjectName(object_name.GetCString());
+
+ sb_module_spec.SetObjectOffset(module_spec.GetObjectOffset());
+ sb_module_spec.SetObjectSize(module_spec.GetObjectSize());
+
+ return SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBModuleSpec>(sb_module_spec));
+}
+
+/// Helper to convert an internal FileSpec to a Python SBFileSpec object.
+/// Must be called with the GIL held.
+static PythonObject ToSWIGFileSpec(const FileSpec &file_spec) {
+ return SWIGBridge::ToSWIGWrapper(
+ std::make_unique<SBFileSpec>(file_spec.GetPath().c_str(), false));
+}
+
+std::optional<ModuleSpec>
+ScriptedSymbolLocatorPythonInterface::LocateExecutableObjectFile(
+ const ModuleSpec &module_spec, Status &error) {
+ if (!m_object_instance_sp)
+ return {};
+
+ // Acquire the GIL before creating any Python objects.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return {};
+
+ PythonObject py_module_spec = ToSWIGModuleSpec(module_spec);
+
+ auto expected =
+ implementor.CallMethod("locate_executable_object_file", py_module_spec);
+ if (!expected) {
+ // Consume the PythonException while the GIL is held. Converting to string
+ // forces PythonException destruction before the GIL is released.
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return {};
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (!py_return.IsAllocated() || py_return.IsNone())
+ return {};
+
+ auto obj = py_return.CreateStructuredObject();
+ if (!obj)
+ 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) {
+ if (!m_object_instance_sp)
+ return {};
+
+ // Acquire the GIL before creating any Python objects.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return {};
+
+ PythonObject py_module_spec = ToSWIGModuleSpec(module_spec);
+
+ // Convert FileSpecList to a Python list of SBFileSpec.
+ PythonList py_paths(default_search_paths.GetSize());
+ for (size_t i = 0; i < default_search_paths.GetSize(); i++) {
+ py_paths.SetItemAtIndex(
+ i, ToSWIGFileSpec(default_search_paths.GetFileSpecAtIndex(i)));
+ }
+
+ auto expected = implementor.CallMethod("locate_executable_symbol_file",
+ py_module_spec, py_paths);
+ if (!expected) {
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return {};
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (!py_return.IsAllocated() || py_return.IsNone())
+ return {};
+
+ auto obj = py_return.CreateStructuredObject();
+ if (!obj)
+ 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) {
+ if (!m_object_instance_sp)
+ return false;
+
+ // Acquire the GIL before creating any Python objects.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return false;
+
+ PythonObject py_module_spec = ToSWIGModuleSpec(module_spec);
+
+ auto expected = implementor.CallMethod(
+ "download_object_and_symbol_file", py_module_spec,
+ PythonBoolean(force_lookup), PythonBoolean(copy_executable));
+ if (!expected) {
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return false;
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (!py_return.IsAllocated() || py_return.IsNone())
+ return false;
+
+ auto obj = py_return.CreateStructuredObject();
+ if (!obj)
+ return false;
+
+ return obj->GetBooleanValue();
+}
+
+std::optional<FileSpec> ScriptedSymbolLocatorPythonInterface::LocateSourceFile(
+ const lldb::ModuleSP &module_sp, const FileSpec &original_source_file,
+ Status &error) {
+ if (!m_object_instance_sp)
+ return {};
+
+ std::optional<FileSpec> result;
+
+ {
+ // Acquire the GIL before creating any Python objects. All Python objects
+ // (including error objects) must be destroyed within this scope.
+ Locker py_lock(&m_interpreter, Locker::AcquireLock | Locker::NoSTDIN,
+ Locker::FreeLock);
+
+ PythonObject implementor(PyRefType::Borrowed,
+ (PyObject *)m_object_instance_sp->GetValue());
+ if (!implementor.IsAllocated())
+ return {};
+
+ PythonObject py_module = SWIGBridge::ToSWIGWrapper(module_sp);
+ std::string source_path = original_source_file.GetPath();
+ PythonString py_source_path(source_path);
+
+ auto expected =
+ implementor.CallMethod("locate_source_file", py_module, py_source_path);
+ if (!expected) {
+ // Consume the error (which may contain PythonException) while the GIL
+ // is still held. Convert to string to force PythonException destruction
+ // before the GIL is released.
+ std::string msg = llvm::toString(expected.takeError());
+ error = Status(msg);
+ return {};
+ }
+
+ PythonObject py_return = std::move(*expected);
+ if (py_return.IsAllocated() && !py_return.IsNone()) {
+ auto obj = py_return.CreateStructuredObject();
+ if (obj) {
+ llvm::StringRef value = obj->GetStringValue();
+ if (!value.empty()) {
+ FileSpec file_spec;
+ file_spec.SetPath(value);
+ result = file_spec;
+ }
+ }
+ }
+ } // GIL released here, after all Python objects are destroyed.
+
+ return result;
----------------
medismailben wrote:
If you want to reconstruct a private python from your scripted affordance, you can do that using ScriptedPythonInterface::Dispatch<T>, you'll need to implement the template specialization for `T ExtractValueFromPythonObject<T>(python::PythonObject &p, Status &error)`
I'd recommend looking at the Stream specialization since it's pretty similar to FileSpec, in the sense that SBFileSpec holds a unique_ptr<lldb_private::FileSpec> like SBStream holds a unique_ptr<lldb_private::Stream>.
https://github.com/llvm/llvm-project/pull/181334
More information about the lldb-commits
mailing list