Skip to content

Add a python JIT loader class. #142514

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

clayborg
Copy link
Collaborator

@clayborg clayborg commented Jun 3, 2025

Users can implement a JIT loader in python by implementing a class that named "JITLoaderPlugin" in a module. The path to this module is specified in the settings using:

 (lldb) setting set target.process.python-jit-loader-path <path>

When the process starts up it will load this module and call methods on the python class. The python class must implement the following methods:

class JITLoaderPlugin:
    #-----------------------------------------------------------------------
    # Construct this object with reference to the process that owns this
    # JIT loader.
    #-----------------------------------------------------------------------
    def __init__(self, process: lldb.SBProcess):
        self.process = process

    #-----------------------------------------------------------------------
    # Called when attaching is completed.
    #-----------------------------------------------------------------------
    def did_attach(self):
        pass

    #-----------------------------------------------------------------------
    # Called when launching is completed.
    #-----------------------------------------------------------------------
    def did_launch(self):
        pass

    #-----------------------------------------------------------------------
    # Called once for each module that is loaded into the debug sessions.
    # This allows clients to search to symbols or references to JIT'ed
    # functions in each module as it gets loaded. Note that this function
    # can be called prior to did_attach() or did_launch() being called as
    # libraries get loaded during the attach or launch.
    #-----------------------------------------------------------------------
    def module_did_load(self, module: lldb.SBModule):
        pass

Users can implement a JIT loader in python by implementing a class that named "JITLoaderPlugin" in a module. The path to this module is specified in the settings using:

     (lldb) setting set target.process.python-jit-loader-path <path>

When the process starts up it will load this module and call methods on the python class. The python class must implement the following methods:

```
class JITLoaderPlugin:
    #-----------------------------------------------------------------------
    # Construct this object with reference to the process that owns this
    # JIT loader.
    #-----------------------------------------------------------------------
    def __init__(self, process: lldb.SBProcess):
        self.process = process

    #-----------------------------------------------------------------------
    # Called when attaching is completed.
    #-----------------------------------------------------------------------
    def did_attach(self):
        pass

    #-----------------------------------------------------------------------
    # Called when launching is completed.
    #-----------------------------------------------------------------------
    def did_launch(self):
        pass

    #-----------------------------------------------------------------------
    # Called once for each module that is loaded into the debug sessions.
    # This allows clients to search to symbols or references to JIT'ed
    # functions in each module as it gets loaded. Note that this function
    # can be called prior to did_attach() or did_launch() being called as
    # libraries get loaded during the attach or launch.
    #-----------------------------------------------------------------------
    def module_did_load(self, module: lldb.SBModule):
        pass

```
@llvmbot
Copy link
Member

llvmbot commented Jun 3, 2025

@llvm/pr-subscribers-lldb

Author: Greg Clayton (clayborg)

Changes

Users can implement a JIT loader in python by implementing a class that named "JITLoaderPlugin" in a module. The path to this module is specified in the settings using:

 (lldb) setting set target.process.python-jit-loader-path &lt;path&gt;

When the process starts up it will load this module and call methods on the python class. The python class must implement the following methods:

class JITLoaderPlugin:
    #-----------------------------------------------------------------------
    # Construct this object with reference to the process that owns this
    # JIT loader.
    #-----------------------------------------------------------------------
    def __init__(self, process: lldb.SBProcess):
        self.process = process

    #-----------------------------------------------------------------------
    # Called when attaching is completed.
    #-----------------------------------------------------------------------
    def did_attach(self):
        pass

    #-----------------------------------------------------------------------
    # Called when launching is completed.
    #-----------------------------------------------------------------------
    def did_launch(self):
        pass

    #-----------------------------------------------------------------------
    # Called once for each module that is loaded into the debug sessions.
    # This allows clients to search to symbols or references to JIT'ed
    # functions in each module as it gets loaded. Note that this function
    # can be called prior to did_attach() or did_launch() being called as
    # libraries get loaded during the attach or launch.
    #-----------------------------------------------------------------------
    def module_did_load(self, module: lldb.SBModule):
        pass


Patch is 40.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/142514.diff

28 Files Affected:

  • (modified) lldb/bindings/python/python-wrapper.swig (+12)
  • (modified) lldb/include/lldb/API/SBModule.h (+1)
  • (added) lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h (+30)
  • (modified) lldb/include/lldb/Interpreter/ScriptInterpreter.h (+8)
  • (modified) lldb/include/lldb/Target/Process.h (+1)
  • (modified) lldb/include/lldb/lldb-forward.h (+2)
  • (modified) lldb/source/Interpreter/ScriptInterpreter.cpp (+6)
  • (modified) lldb/source/Plugins/JITLoader/CMakeLists.txt (+1)
  • (added) lldb/source/Plugins/JITLoader/Python/CMakeLists.txt (+11)
  • (added) lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp (+146)
  • (added) lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h (+62)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt (+1-2)
  • (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.cpp (+79)
  • (added) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h (+104)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h (+1)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp (+18)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h (+9)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h (+1)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (+5)
  • (modified) lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h (+2)
  • (modified) lldb/source/Target/Process.cpp (+6)
  • (modified) lldb/source/Target/TargetProperties.td (+3)
  • (added) lldb/test/API/functionalities/jitloader_python/Makefile (+10)
  • (added) lldb/test/API/functionalities/jitloader_python/TestJITLoaderPython.py (+105)
  • (added) lldb/test/API/functionalities/jitloader_python/jit.cpp (+44)
  • (added) lldb/test/API/functionalities/jitloader_python/jit_loader.py (+79)
  • (added) lldb/test/API/functionalities/jitloader_python/main.cpp (+48)
  • (modified) lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp (+5)
diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig
index 3d1d04e47e70b..9e8528357bf76 100644
--- a/lldb/bindings/python/python-wrapper.swig
+++ b/lldb/bindings/python/python-wrapper.swig
@@ -542,6 +542,18 @@ void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBEvent(PyObject * data
   return sb_ptr;
 }
 
+void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBModule(PyObject * data) {
+  lldb::SBModule *sb_ptr = nullptr;
+
+  int valid_cast =
+      SWIG_ConvertPtr(data, (void **)&sb_ptr, SWIGTYPE_p_lldb__SBModule, 0);
+
+  if (valid_cast == -1)
+    return NULL;
+
+  return sb_ptr;
+}
+
 void *lldb_private::python::LLDBSWIGPython_CastPyObjectToSBStream(PyObject * data) {
   lldb::SBStream *sb_ptr = nullptr;
 
diff --git a/lldb/include/lldb/API/SBModule.h b/lldb/include/lldb/API/SBModule.h
index 85332066ee687..7f4e9ef470532 100644
--- a/lldb/include/lldb/API/SBModule.h
+++ b/lldb/include/lldb/API/SBModule.h
@@ -306,6 +306,7 @@ class LLDB_API SBModule {
   friend class SBType;
 
   friend class lldb_private::python::SWIGBridge;
+  friend class lldb_private::ScriptInterpreter;
 
   explicit SBModule(const lldb::ModuleSP &module_sp);
 
diff --git a/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h b/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h
new file mode 100644
index 0000000000000..b18d7ae5ae3f5
--- /dev/null
+++ b/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h
@@ -0,0 +1,30 @@
+//===-- JITLoaderInterface.h ------------------------------------*- C++ -*-===//
+//
+// 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_JITLOADERINTERFACE_H
+#define LLDB_INTERPRETER_INTERFACES_JITLOADERINTERFACE_H
+
+#include "ScriptedThreadInterface.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+class JITLoaderInterface : virtual public ScriptedInterface {
+public:
+
+  virtual llvm::Expected<StructuredData::GenericSP>
+  CreatePluginObject(llvm::StringRef class_name, 
+                     lldb_private::ExecutionContext &exe_ctx) = 0;
+
+  virtual void DidAttach() {};
+  virtual void DidLaunch() {};
+  virtual void ModulesDidLoad(lldb_private::ModuleList &module_list) {};
+
+};
+} // namespace lldb_private
+
+#endif // LLDB_INTERPRETER_INTERFACES_JITLOADERINTERFACE_H
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 3a4a7ae924584..99531844c80f3 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -16,6 +16,7 @@
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBExecutionContext.h"
 #include "lldb/API/SBLaunchInfo.h"
+#include "lldb/API/SBModule.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/Breakpoint/BreakpointOptions.h"
@@ -24,6 +25,7 @@
 #include "lldb/Core/ThreadedCommunication.h"
 #include "lldb/Host/PseudoTerminal.h"
 #include "lldb/Host/StreamFile.h"
+#include "lldb/Interpreter/Interfaces/JITLoaderInterface.h"
 #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h"
 #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h"
@@ -560,6 +562,10 @@ class ScriptInterpreter : public PluginInterface {
     return {};
   }
 
+  virtual lldb::JITLoaderInterfaceSP CreateJITLoaderInterface() {
+    return {};
+  }
+
   virtual lldb::ScriptedPlatformInterfaceUP GetScriptedPlatformInterface() {
     return {};
   }
@@ -597,6 +603,8 @@ class ScriptInterpreter : public PluginInterface {
   lldb::ExecutionContextRefSP GetOpaqueTypeFromSBExecutionContext(
       const lldb::SBExecutionContext &exe_ctx) const;
 
+  lldb::ModuleSP GetOpaqueTypeFromSBModule(const lldb::SBModule &module) const;
+  
 protected:
   Debugger &m_debugger;
   lldb::ScriptLanguage m_script_lang;
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 536a69fb89759..305a682f0ad83 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -87,6 +87,7 @@ class ProcessProperties : public Properties {
   Args GetExtraStartupCommands() const;
   void SetExtraStartupCommands(const Args &args);
   FileSpec GetPythonOSPluginPath() const;
+  FileSpec GetPythonJITLoaderPath() const;
   uint32_t GetVirtualAddressableBits() const;
   void SetVirtualAddressableBits(uint32_t bits);
   uint32_t GetHighmemVirtualAddressableBits() const;
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index c664d1398f74d..837c734a98059 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -114,6 +114,7 @@ class Instruction;
 class InstructionList;
 class InstrumentationRuntime;
 class JITLoader;
+class JITLoaderInterface;
 class JITLoaderList;
 class Language;
 class LanguageCategory;
@@ -364,6 +365,7 @@ typedef std::shared_ptr<lldb_private::IOHandler> IOHandlerSP;
 typedef std::shared_ptr<lldb_private::IOObject> IOObjectSP;
 typedef std::shared_ptr<lldb_private::IRExecutionUnit> IRExecutionUnitSP;
 typedef std::shared_ptr<lldb_private::JITLoader> JITLoaderSP;
+typedef std::shared_ptr<lldb_private::JITLoaderInterface> JITLoaderInterfaceSP;
 typedef std::unique_ptr<lldb_private::JITLoaderList> JITLoaderListUP;
 typedef std::shared_ptr<lldb_private::LanguageRuntime> LanguageRuntimeSP;
 typedef std::unique_ptr<lldb_private::SystemRuntime> SystemRuntimeUP;
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 63655cc5a50c6..0b12425c1c504 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -130,6 +130,12 @@ ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext(
   return exe_ctx.m_exe_ctx_sp;
 }
 
+lldb::ModuleSP
+ScriptInterpreter::GetOpaqueTypeFromSBModule(
+    const lldb::SBModule &module) const {
+  return module.m_opaque_sp;
+}
+
 lldb::ScriptLanguage
 ScriptInterpreter::StringToLanguage(const llvm::StringRef &language) {
   if (language.equals_insensitive(LanguageToString(eScriptLanguageNone)))
diff --git a/lldb/source/Plugins/JITLoader/CMakeLists.txt b/lldb/source/Plugins/JITLoader/CMakeLists.txt
index e52230199109f..aad64f1e7c8bd 100644
--- a/lldb/source/Plugins/JITLoader/CMakeLists.txt
+++ b/lldb/source/Plugins/JITLoader/CMakeLists.txt
@@ -1 +1,2 @@
 add_subdirectory(GDB)
+add_subdirectory(Python)
diff --git a/lldb/source/Plugins/JITLoader/Python/CMakeLists.txt b/lldb/source/Plugins/JITLoader/Python/CMakeLists.txt
new file mode 100644
index 0000000000000..a64ade6009a2b
--- /dev/null
+++ b/lldb/source/Plugins/JITLoader/Python/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_lldb_library(lldbPluginJITLoaderPython PLUGIN
+  JITLoaderPython.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbInterpreter
+    lldbSymbol
+    lldbTarget
+    lldbValueObject
+    lldbPluginProcessUtility
+  )
diff --git a/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp
new file mode 100644
index 0000000000000..8981d55fc4661
--- /dev/null
+++ b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp
@@ -0,0 +1,146 @@
+//===-- JITLoaderPython.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"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "JITLoaderPython.h"
+
+#include "Plugins/Process/Utility/RegisterContextDummy.h"
+#include "Plugins/Process/Utility/RegisterContextMemory.h"
+#include "Plugins/Process/Utility/ThreadMemory.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/RegisterValue.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StructuredData.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
+
+#include <memory>
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(JITLoaderPython)
+
+void JITLoaderPython::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(), CreateInstance,
+                                nullptr);
+}
+
+void JITLoaderPython::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+JITLoaderSP JITLoaderPython::CreateInstance(Process *process, bool force) {
+  // Python JITLoader plug-ins must be requested by name, so force must
+  // be true
+  FileSpec python_os_plugin_spec(process->GetPythonJITLoaderPath());
+  if (python_os_plugin_spec &&
+      FileSystem::Instance().Exists(python_os_plugin_spec)) {
+    std::unique_ptr<JITLoaderPython> os_up(
+        new JITLoaderPython(process, python_os_plugin_spec));
+    if (os_up.get() && os_up->IsValid())
+      return os_up;
+  }
+  return nullptr;
+}
+
+llvm::StringRef JITLoaderPython::GetPluginDescriptionStatic() {
+  return "JIT loader plug-in that implements a JIT loader using a python "
+         "class that implements the necessary JITLoader functionality.";
+}
+
+JITLoaderPython::JITLoaderPython(lldb_private::Process *process,
+                                 const FileSpec &python_module_path)
+    : JITLoader(process), m_interpreter(nullptr), m_script_object_sp() {
+  if (!process)
+    return;
+  TargetSP target_sp = process->CalculateTarget();
+  if (!target_sp)
+    return;
+  m_interpreter = target_sp->GetDebugger().GetScriptInterpreter();
+  if (!m_interpreter)
+    return;
+
+  std::string os_plugin_class_name(
+      python_module_path.GetFilename().AsCString(""));
+  if (os_plugin_class_name.empty())
+    return;
+
+  LoadScriptOptions options;
+  char python_module_path_cstr[PATH_MAX];
+  python_module_path.GetPath(python_module_path_cstr,
+                             sizeof(python_module_path_cstr));
+  Status error;
+  if (!m_interpreter->LoadScriptingModule(python_module_path_cstr, options,
+                                          error))
+    return;
+
+  // Strip the ".py" extension if there is one
+  size_t py_extension_pos = os_plugin_class_name.rfind(".py");
+  if (py_extension_pos != std::string::npos)
+    os_plugin_class_name.erase(py_extension_pos);
+  // Add ".JITLoaderPlugin" to the module name to get a string like
+  // "modulename.JITLoaderPlugin"
+  os_plugin_class_name += ".JITLoaderPlugin";
+
+  JITLoaderInterfaceSP interface_sp =
+      m_interpreter->CreateJITLoaderInterface();
+  if (!interface_sp)
+    return;
+
+  ExecutionContext exe_ctx(process);
+  auto obj_or_err = interface_sp->CreatePluginObject(
+      os_plugin_class_name, exe_ctx);
+
+  if (!obj_or_err) {
+    llvm::consumeError(obj_or_err.takeError());
+    return;
+  }
+
+  StructuredData::GenericSP owned_script_object_sp = *obj_or_err;
+  if (!owned_script_object_sp->IsValid())
+    return;
+
+  m_script_object_sp = owned_script_object_sp;
+  m_interface_sp = interface_sp;
+}
+
+JITLoaderPython::~JITLoaderPython() = default;
+
+void JITLoaderPython::DidAttach() {
+  if (m_interface_sp)
+    m_interface_sp->DidAttach();
+}
+
+void JITLoaderPython::DidLaunch() {
+  if (m_interface_sp)
+    m_interface_sp->DidLaunch();
+}
+
+void JITLoaderPython::ModulesDidLoad(lldb_private::ModuleList &module_list) {
+  if (m_interface_sp)
+    m_interface_sp->ModulesDidLoad(module_list);
+}
+
+#endif // #if LLDB_ENABLE_PYTHON
diff --git a/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h
new file mode 100644
index 0000000000000..87146ad216de3
--- /dev/null
+++ b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h
@@ -0,0 +1,62 @@
+//===-- JITLoaderPython.h ---------------------------------------*- C++ -*-===//
+//
+// 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 liblldb_JITLoaderPython_h_
+#define liblldb_JITLoaderPython_h_
+
+#include "lldb/Host/Config.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "lldb/Target/JITLoader.h"
+#include "lldb/Utility/StructuredData.h"
+
+namespace lldb_private {
+class ScriptInterpreter;
+}
+
+class JITLoaderPython : public lldb_private::JITLoader {
+public:
+  JITLoaderPython(lldb_private::Process *process,
+                  const lldb_private::FileSpec &python_module_path);
+
+  ~JITLoaderPython() override;
+
+  // Static Functions
+  static lldb::JITLoaderSP CreateInstance(lldb_private::Process *process, 
+                                          bool force);
+
+  static void Initialize();
+
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() { return "python"; }
+
+  static llvm::StringRef GetPluginDescriptionStatic();
+
+  // lldb_private::PluginInterface Methods
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+  // lldb_private::JITLoader Methods
+  void DidAttach() override;
+  void DidLaunch() override;
+  void ModulesDidLoad(lldb_private::ModuleList &module_list) override;
+
+protected:
+  bool IsValid() const {
+    return m_script_object_sp && m_script_object_sp->IsValid();
+  }
+
+  lldb_private::ScriptInterpreter *m_interpreter = nullptr;
+  lldb::JITLoaderInterfaceSP m_interface_sp;
+  lldb_private::StructuredData::GenericSP m_script_object_sp;
+};
+
+#endif // LLDB_ENABLE_PYTHON
+
+#endif // liblldb_JITLoaderPython_h_
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
index ee5e48ad5cdc3..ed254707ebb00 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/CMakeLists.txt
@@ -20,6 +20,7 @@ if (LLDB_ENABLE_LIBEDIT)
 endif()
 
 add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
+  JITLoaderPythonInterface.cpp
   OperatingSystemPythonInterface.cpp
   ScriptInterpreterPythonInterfaces.cpp
   ScriptedPlatformPythonInterface.cpp
@@ -40,5 +41,3 @@ add_lldb_library(lldbPluginScriptInterpreterPythonInterfaces PLUGIN
   LINK_COMPONENTS
     Support
   )
-
-
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.cpp
new file mode 100644
index 0000000000000..43d77964efa49
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.cpp
@@ -0,0 +1,79 @@
+//===-- JITLoaderPythonInterface.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/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#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 "JITLoaderPythonInterface.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+JITLoaderPythonInterface::JITLoaderPythonInterface(
+    ScriptInterpreterPythonImpl &interpreter)
+    : ScriptedPythonInterface(interpreter) {}
+
+llvm::Expected<StructuredData::GenericSP>
+JITLoaderPythonInterface::CreatePluginObject(
+    llvm::StringRef class_name, ExecutionContext &exe_ctx) {
+  return ScriptedPythonInterface::CreatePluginObject(class_name, nullptr,
+                                                     exe_ctx.GetProcessSP());
+}
+
+void JITLoaderPythonInterface::Initialize() {
+  const std::vector<llvm::StringRef> ci_usages = {
+      "settings set target.process.python-jit-loader-plugin-path <script-path>"};
+  const std::vector<llvm::StringRef> api_usages = {};
+  PluginManager::RegisterPlugin(
+      GetPluginNameStatic(), llvm::StringRef("JIT loader python plugin"),
+      CreateInstance, eScriptLanguagePython, {ci_usages, api_usages});
+}
+
+void JITLoaderPythonInterface::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+//------------------------------------------------------------------------------
+// JITLoader API overrides
+//------------------------------------------------------------------------------
+
+void JITLoaderPythonInterface::DidAttach() {
+  Status error;
+  Dispatch("did_attach", error);
+}
+
+void JITLoaderPythonInterface::DidLaunch() {
+  Status error;
+  Dispatch("did_launch", error);
+}
+
+void JITLoaderPythonInterface::ModulesDidLoad(ModuleList &module_list) {
+  Status error;
+  // There is no SBModuleList, so we need to deliver each module individually 
+  // to the python scripts since it uses the LLDB public API.
+  module_list.ForEach([&](const lldb::ModuleSP &module_sp_ref) {
+    lldb::ModuleSP module_sp(module_sp_ref);
+    Dispatch("module_did_load", error, module_sp);
+    return true; // Keep iterating  
+  });
+}
+
+#endif
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h
new file mode 100644
index 0000000000000..cb8ef5b8c43ad
--- /dev/null
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h
@@ -0,0 +1,104 @@
+//===-- JITLoaderPythonInterface.h ------------------------------*- C++ -*-===//
+//
+// 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_JITLOADERPYTHONINTERFACE_H
+#define LLDB_PLUGINS_SCRIPTINTERPRETER_PYTHON_INTERFACES_JITLOADERPYTHONINTERFACE_H
+
+#include "lldb/Host/Config.h"
+#include "lldb/Interpreter/Interfaces/JITLoaderInterface.h"
+
+#if LLDB_ENABLE_PYTHON
+
+#include "ScriptedThreadPythonInterface.h"
+
+#include <optional>
+
+/// Defines a JITLoader interface for Python.
+/// 
+/// Users can implement a JIT loader in python by implementing a class that
+/// named "JITLoaderPlugin" in a module. The path to this module is specified
+/// in the settings using:
+///
+///     (lldb) setting set target.process.python-jit-loader-path <path>
+///
+/// When the process starts up it will load this module and call methods on the
+/// python class. The python class must implement the following methods:
+///
+/// #---------------------------------------------------------------------------
+/// # The class must be named "JITLoaderPlugin" in the python file.
+/// #---------------------------------------------------------------------------
+/// class JITLoaderPlugin:
+///     #-----------------------------------------------------------------------
+///     # Construct this object with reference to the process that owns this 
+///     # JIT loader.
+///     #-----------------------------------------------------------------------
+///     def __init__(self, process: lldb.SBProce...
[truncated]

Copy link

github-actions bot commented Jun 3, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp,h -- lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.cpp lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h lldb/test/API/functionalities/jitloader_python/jit.cpp lldb/test/API/functionalities/jitloader_python/main.cpp lldb/include/lldb/API/SBModule.h lldb/include/lldb/Interpreter/ScriptInterpreter.h lldb/include/lldb/Target/Process.h lldb/include/lldb/lldb-forward.h lldb/source/Interpreter/ScriptInterpreter.cpp lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptInterpreterPythonInterfaces.h lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h lldb/source/Target/Process.cpp lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp
View the diff from clang-format here.
diff --git a/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h b/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h
index b18d7ae5a..84729a2c2 100644
--- a/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h
+++ b/lldb/include/lldb/Interpreter/Interfaces/JITLoaderInterface.h
@@ -15,15 +15,13 @@
 namespace lldb_private {
 class JITLoaderInterface : virtual public ScriptedInterface {
 public:
-
   virtual llvm::Expected<StructuredData::GenericSP>
-  CreatePluginObject(llvm::StringRef class_name, 
+  CreatePluginObject(llvm::StringRef class_name,
                      lldb_private::ExecutionContext &exe_ctx) = 0;
 
   virtual void DidAttach() {};
   virtual void DidLaunch() {};
   virtual void ModulesDidLoad(lldb_private::ModuleList &module_list) {};
-
 };
 } // namespace lldb_private
 
diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index f8885987f..a8027fa24 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -16,8 +16,8 @@
 #include "lldb/API/SBEvent.h"
 #include "lldb/API/SBExecutionContext.h"
 #include "lldb/API/SBLaunchInfo.h"
-#include "lldb/API/SBModule.h"
 #include "lldb/API/SBMemoryRegionInfo.h"
+#include "lldb/API/SBModule.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/Breakpoint/BreakpointOptions.h"
 #include "lldb/Core/PluginInterface.h"
@@ -560,9 +560,7 @@ public:
     return {};
   }
 
-  virtual lldb::JITLoaderInterfaceSP CreateJITLoaderInterface() {
-    return {};
-  }
+  virtual lldb::JITLoaderInterfaceSP CreateJITLoaderInterface() { return {}; }
 
   virtual lldb::ScriptedPlatformInterfaceUP GetScriptedPlatformInterface() {
     return {};
@@ -602,7 +600,7 @@ public:
       const lldb::SBExecutionContext &exe_ctx) const;
 
   lldb::ModuleSP GetOpaqueTypeFromSBModule(const lldb::SBModule &module) const;
-  
+
 protected:
   Debugger &m_debugger;
   lldb::ScriptLanguage m_script_lang;
diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 0b12425c1..c95312a8f 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -130,8 +130,7 @@ ScriptInterpreter::GetOpaqueTypeFromSBExecutionContext(
   return exe_ctx.m_exe_ctx_sp;
 }
 
-lldb::ModuleSP
-ScriptInterpreter::GetOpaqueTypeFromSBModule(
+lldb::ModuleSP ScriptInterpreter::GetOpaqueTypeFromSBModule(
     const lldb::SBModule &module) const {
   return module.m_opaque_sp;
 }
diff --git a/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp
index 8981d55fc..d01109b14 100644
--- a/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp
+++ b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.cpp
@@ -104,14 +104,13 @@ JITLoaderPython::JITLoaderPython(lldb_private::Process *process,
   // "modulename.JITLoaderPlugin"
   os_plugin_class_name += ".JITLoaderPlugin";
 
-  JITLoaderInterfaceSP interface_sp =
-      m_interpreter->CreateJITLoaderInterface();
+  JITLoaderInterfaceSP interface_sp = m_interpreter->CreateJITLoaderInterface();
   if (!interface_sp)
     return;
 
   ExecutionContext exe_ctx(process);
-  auto obj_or_err = interface_sp->CreatePluginObject(
-      os_plugin_class_name, exe_ctx);
+  auto obj_or_err =
+      interface_sp->CreatePluginObject(os_plugin_class_name, exe_ctx);
 
   if (!obj_or_err) {
     llvm::consumeError(obj_or_err.takeError());
diff --git a/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h
index 87146ad21..a86e2531d 100644
--- a/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h
+++ b/lldb/source/Plugins/JITLoader/Python/JITLoaderPython.h
@@ -28,7 +28,7 @@ public:
   ~JITLoaderPython() override;
 
   // Static Functions
-  static lldb::JITLoaderSP CreateInstance(lldb_private::Process *process, 
+  static lldb::JITLoaderSP CreateInstance(lldb_private::Process *process,
                                           bool force);
 
   static void Initialize();
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h
index cb8ef5b8c..a087b4823 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/JITLoaderPythonInterface.h
@@ -19,7 +19,7 @@
 #include <optional>
 
 /// Defines a JITLoader interface for Python.
-/// 
+///
 /// Users can implement a JIT loader in python by implementing a class that
 /// named "JITLoaderPlugin" in a module. The path to this module is specified
 /// in the settings using:
@@ -34,7 +34,7 @@
 /// #---------------------------------------------------------------------------
 /// class JITLoaderPlugin:
 ///     #-----------------------------------------------------------------------
-///     # Construct this object with reference to the process that owns this 
+///     # Construct this object with reference to the process that owns this
 ///     # JIT loader.
 ///     #-----------------------------------------------------------------------
 ///     def __init__(self, process: lldb.SBProcess):
@@ -53,8 +53,8 @@
 ///         pass
 ///
 ///     #-----------------------------------------------------------------------
-///     # Called once for each module that is loaded into the debug sessions. 
-///     # This allows clients to search to symbols or references to JIT'ed 
+///     # Called once for each module that is loaded into the debug sessions.
+///     # This allows clients to search to symbols or references to JIT'ed
 ///     # functions in each module as it gets loaded. Note that this function
 ///     # can be called prior to did_attach() or did_launch() being called as
 ///     # libraries get loaded during the attach or launch.
@@ -64,30 +64,26 @@
 ///
 
 namespace lldb_private {
-class JITLoaderPythonInterface
-    : virtual public JITLoaderInterface,
-      virtual public ScriptedPythonInterface,
-      public PluginInterface {
+class JITLoaderPythonInterface : virtual public JITLoaderInterface,
+                                 virtual public ScriptedPythonInterface,
+                                 public PluginInterface {
 public:
   JITLoaderPythonInterface(ScriptInterpreterPythonImpl &interpreter);
 
   llvm::Expected<StructuredData::GenericSP>
-  CreatePluginObject(llvm::StringRef class_name, 
+  CreatePluginObject(llvm::StringRef class_name,
                      ExecutionContext &exe_ctx) override;
 
-
   llvm::SmallVector<AbstractMethodRequirement>
   GetAbstractMethodRequirements() const override {
     return llvm::SmallVector<AbstractMethodRequirement>(
-        {{"did_attach"},
-         {"did_launch"},
-         {"module_did_load", 1}});
+        {{"did_attach"}, {"did_launch"}, {"module_did_load", 1}});
   }
-          
+
   void DidAttach() override;
   void DidLaunch() override;
   void ModulesDidLoad(lldb_private::ModuleList &module_list) override;
-                   
+
   static void Initialize();
 
   static void Terminate();
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
index 6d2b95fd7..8291ab58d 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.cpp
@@ -180,12 +180,11 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
 
 template <>
 lldb::ModuleSP
-ScriptedPythonInterface::ExtractValueFromPythonObject<
-    lldb::ModuleSP>(python::PythonObject &p, Status &error) {
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ModuleSP>(
+    python::PythonObject &p, Status &error) {
 
-  lldb::SBModule *sb_module =
-      reinterpret_cast<lldb::SBModule *>(
-          python::LLDBSWIGPython_CastPyObjectToSBModule(p.get()));
+  lldb::SBModule *sb_module = reinterpret_cast<lldb::SBModule *>(
+      python::LLDBSWIGPython_CastPyObjectToSBModule(p.get()));
 
   if (!sb_module) {
     error = Status::FromErrorStringWithFormat(
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
index 011cee2ea..7cc21de3f 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/Interfaces/ScriptedPythonInterface.h
@@ -594,8 +594,8 @@ ScriptedPythonInterface::ExtractValueFromPythonObject<
 
 template <>
 lldb::ModuleSP
-ScriptedPythonInterface::ExtractValueFromPythonObject<
-    lldb::ModuleSP>(python::PythonObject &p, Status &error);
+ScriptedPythonInterface::ExtractValueFromPythonObject<lldb::ModuleSP>(
+    python::PythonObject &p, Status &error);
 
 } // namespace lldb_private
 
diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 54093765f..83373a889 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -1569,7 +1569,6 @@ JITLoaderInterfaceSP ScriptInterpreterPythonImpl::CreateJITLoaderInterface() {
   return std::make_shared<JITLoaderPythonInterface>(*this);
 }
 
-
 StructuredData::ObjectSP
 ScriptInterpreterPythonImpl::CreateStructuredDataFromScriptObject(
     ScriptObject obj) {
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 5f2dfed22..e7a98606d 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -213,7 +213,6 @@ FileSpec ProcessProperties::GetPythonJITLoaderPath() const {
   return GetPropertyAtIndexAs<FileSpec>(idx, {});
 }
 
-
 uint32_t ProcessProperties::GetVirtualAddressableBits() const {
   const uint32_t idx = ePropertyVirtualAddressableBits;
   return GetPropertyAtIndexAs<uint64_t>(
diff --git a/lldb/test/API/functionalities/jitloader_python/jit.cpp b/lldb/test/API/functionalities/jitloader_python/jit.cpp
index 6a8ec50e6..82e34de0b 100644
--- a/lldb/test/API/functionalities/jitloader_python/jit.cpp
+++ b/lldb/test/API/functionalities/jitloader_python/jit.cpp
@@ -3,42 +3,38 @@
 // GDB JIT interface
 enum JITAction { JIT_NOACTION, JIT_REGISTER_FN, JIT_UNREGISTER_FN };
 
-struct JITCodeEntry
-{
-    struct JITCodeEntry* next;
-    struct JITCodeEntry* prev;
-    const char *symfile_addr;
-    uint64_t symfile_size;
+struct JITCodeEntry {
+  struct JITCodeEntry *next;
+  struct JITCodeEntry *prev;
+  const char *symfile_addr;
+  uint64_t symfile_size;
 };
 
-struct JITDescriptor
-{
-    uint32_t version;
-    uint32_t action_flag;
-    struct JITCodeEntry* relevant_entry;
-    struct JITCodeEntry* first_entry;
+struct JITDescriptor {
+  uint32_t version;
+  uint32_t action_flag;
+  struct JITCodeEntry *relevant_entry;
+  struct JITCodeEntry *first_entry;
 };
 
-struct JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, 0, 0 };
+struct JITDescriptor __jit_debug_descriptor = {1, JIT_NOACTION, 0, 0};
 
-void __jit_debug_register_code()
-{
-}
+void __jit_debug_register_code() {}
 // end GDB JIT interface
 
 struct JITCodeEntry entry;
 
-int main()
-{
-    // Create a code entry with a bogus size
-    entry.next = entry.prev = 0;
-    entry.symfile_addr = (char *)&entry;
-    entry.symfile_size = (uint64_t)47<<32;
+int main() {
+  // Create a code entry with a bogus size
+  entry.next = entry.prev = 0;
+  entry.symfile_addr = (char *)&entry;
+  entry.symfile_size = (uint64_t)47 << 32;
 
-    __jit_debug_descriptor.relevant_entry = __jit_debug_descriptor.first_entry = &entry;
-    __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+  __jit_debug_descriptor.relevant_entry = __jit_debug_descriptor.first_entry =
+      &entry;
+  __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
 
-    __jit_debug_register_code();
+  __jit_debug_register_code();
 
-    return 0;
+  return 0;
 }
diff --git a/lldb/test/API/functionalities/jitloader_python/main.cpp b/lldb/test/API/functionalities/jitloader_python/main.cpp
index 3b1d5e229..fbca82b35 100644
--- a/lldb/test/API/functionalities/jitloader_python/main.cpp
+++ b/lldb/test/API/functionalities/jitloader_python/main.cpp
@@ -1,48 +1,37 @@
 #include <inttypes.h>
 #include <stdio.h>
 
-enum JITAction { 
-  JIT_NOACTION = 0, 
-  JIT_LOAD = 1, 
-  JIT_UNLOAD = 2 
-};
+enum JITAction { JIT_NOACTION = 0, JIT_LOAD = 1, JIT_UNLOAD = 2 };
 
-struct JITEntry
-{
-    struct JITEntry* next = nullptr;
-    const char *path = nullptr;
-    uint64_t address = 0;
+struct JITEntry {
+  struct JITEntry *next = nullptr;
+  const char *path = nullptr;
+  uint64_t address = 0;
 };
 
-
 JITEntry *g_entry_list = nullptr;
 
-void jit_module_action(JITEntry *entry, JITAction action)
-{
-    printf("entry = %p, action = %i\n", (void *)entry, action);
+void jit_module_action(JITEntry *entry, JITAction action) {
+  printf("entry = %p, action = %i\n", (void *)entry, action);
 }
 // end GDB JIT interface
 
-
-int main()
-{
-    // Create an empty JITEntry. The test case will set the path and address
-    // with valid values at the first breakpoint. We build a "jit.out" binary
-    // in the python test and we will load it at a address, so the test case
-    // will calculate the path to the "jit.out" and the address to load it at
-    // and set the values with some expressions.
-    JITEntry entry;
-
-    // Call the "jit_module_action" function to cause our JIT module to be
-    // added to our target and loaded at an address.
-    jit_module_action(&entry, JIT_LOAD); // Breakpoint 1
-    printf("loaded module %s at %16.16" PRIx64 "\n", 
-           entry.path, 
-           entry.address);
-    // Call the "jit_module_action" function to cause our JIT module to be
-    // unloaded at an address.
-    jit_module_action(&entry, JIT_UNLOAD); // Breakpoint 2
-    printf("unloaded module %s" PRIx64 "\n", // Breakpoint 3
-        entry.path);
- return 0;
+int main() {
+  // Create an empty JITEntry. The test case will set the path and address
+  // with valid values at the first breakpoint. We build a "jit.out" binary
+  // in the python test and we will load it at a address, so the test case
+  // will calculate the path to the "jit.out" and the address to load it at
+  // and set the values with some expressions.
+  JITEntry entry;
+
+  // Call the "jit_module_action" function to cause our JIT module to be
+  // added to our target and loaded at an address.
+  jit_module_action(&entry, JIT_LOAD); // Breakpoint 1
+  printf("loaded module %s at %16.16" PRIx64 "\n", entry.path, entry.address);
+  // Call the "jit_module_action" function to cause our JIT module to be
+  // unloaded at an address.
+  jit_module_action(&entry, JIT_UNLOAD);   // Breakpoint 2
+  printf("unloaded module %s" PRIx64 "\n", // Breakpoint 3
+         entry.path);
+  return 0;
 }

@JDevlieghere JDevlieghere requested a review from medismailben June 3, 2025 04:20
@jimingham
Copy link
Collaborator

This seems like a generic module loading observer. I don't see anything JIT specific about it. Not saying a generic module loading observer is not a good idea. But calling it a JITLoader seems pretty confusing to me.

@jimingham
Copy link
Collaborator

This also seems like an awkward way to do what we've wanted for a while, which is the equivalent of stop hooks for "launch", "attach" and "module loaded", since you have to do all three, even if you only wanted to do one of the set.

@jimingham
Copy link
Collaborator

jimingham commented Jun 3, 2025

I changed the stop-hooks recently so they optionally fire when lldb first gets control of the process, so you can already write python code that intervenes when your "did_attach" and "did_launch" callbacks fire. So only the module_loaded callback is new.

@jimingham
Copy link
Collaborator

jimingham commented Jun 3, 2025

One thing that's nicer about your approach here over independent callbacks for each of the hooks is that it allows you to group the three callbacks in the same class instance, so that you can more easily keep shared state.

But that's a general problem with these affordances. For instance, it's super inconvenient that the summary providers and synthetic child providers produce separate objects to handle the callbacks. You end up computing the sizes of container classes twice, once for the summary and once because you need it to present the type... If the summary & child provider could share state, you'd only need to do that once per stop.

And as we are adding more of these callbacks for "lifecycle events" it would be really convenient, as you have done here, to be able to get one object to manage multiple different types of callback hits.

For the case of hooks, I wonder if we could augment the -C options we currently use to add affordances backed by a Python class with a __call__ method so you could say:

target stop-hook add -C my_python_class --shared-instance 1 --call_method stop_hook_handler

and then to finish off your design, we'd add:

target module-hook add -C my_python_class --shared-instance 1 --call_method module_hook_handler

The --shared-instance that would tell lldb to make a single object instance and reuse if for any --shared-instance addition that uses this class (one for each target in this case). Allowing you to specify the method name means you don't have to do the discrimination in __call__...

That way as we add more of these callbacks (which I agree we really need to do) people could mix and match them as the wish, and we wouldn't have to figure out the right combination(s) for everybody's use cases.

Note for summary & synthetic child providers you wouldn't need to specify the methods as those are pre-determined. So the --call-method would not be necessary. In their case the shared instance would be held by a ValueObject, since we make formatter instances for each value object we format.

It might also be handy to be able to define several commands that share state, so you could do the same thing for command classes (though in that case the shared instance would be held by the debugger not the target...)

@jimingham
Copy link
Collaborator

But if you don't care so much about shared state, then I think a better way of doing what you want is just to add target module-hook add...

Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @clayborg, this is pretty cool. I'm glad you were able to use and extend the ScriptedPythonInterface to implement this, hopefully it wasn't too complicated. LGTM!

@vogelsgesang vogelsgesang changed the title Add a pythin JIT loader class. Add a python JIT loader class. Jun 3, 2025
@bulbazord
Copy link
Member

This seems like a generic module loading observer. I don't see anything JIT specific about it. Not saying a generic module loading observer is not a good idea. But calling it a JITLoader seems pretty confusing to me.

+1 on the name. The design seems very general, so maybe calling it something like ModuleObserver or something would make more sense.

Your PR summary describes what users can do, but why might they want to do it? Do you have a motivation for this change?

@medismailben
Copy link
Member

Hey @clayborg, this is pretty cool. I'm glad you were able to use and extend the ScriptedPythonInterface to implement this, hopefully it wasn't too complicated. LGTM!

I meant LGTM on the scripting side of things. I still thing we should address Jim's comments:

For the case of hooks, I wonder if we could augment the -C options we currently use to add affordances backed by a Python class with a call method so you could say:

target stop-hook add -C my_python_class --shared-instance 1 --call_method stop_hook_handler

If you use a OptionGroupPythonClassWithDict, you automatically get -C option to specify either a class or a function to a CommandObject.

I like the idea of being able to create or share instances between scripted affordances but I think we need a way to list the existing instances (and what scripted affordance are they driving), otherwise, how would the user be able to tell what number to pass to --shared-instance ?

Suggestion: if you make this more generic, you could implement a template that's included in the lldb python module like we do for other scripted affordances (ScriptedProcess, ScriptedThread, ScriptedThreadPlan ...) with a base implementation for every function the script should implement. That would make it easier to write new scripts for this.

@medismailben medismailben self-requested a review June 3, 2025 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants