From c02eb5b571a45299a75d2a4063c56cf002c8b16e Mon Sep 17 00:00:00 2001 From: jimingham Date: Tue, 1 Apr 2025 09:54:06 -0700 Subject: [PATCH 1/3] Add a new affordance that the Python module in a dSYM (#133290) So the dSYM can be told what target it has been loaded into. When lldb is loading modules, while creating a target, it will run "command script import" on any Python modules in Resources/Python in the dSYM. However, this happens WHILE the target is being created, so it is not yet in the target list. That means that these scripts can't act on the target that they a part of when they get loaded. This patch adds a new python API that lldb will call: __lldb_module_added_to_target if it is defined in the module, passing in the Target the module was being added to, so that code in these dSYM's don't have to guess. (cherry picked from commit 347c5a7af5adfe81b79dd77f7f88c626b09e8534) --- lldb/bindings/python/python-wrapper.swig | 22 +++++++ .../lldb/Interpreter/ScriptInterpreter.h | 3 +- lldb/source/Core/Module.cpp | 4 +- lldb/source/Interpreter/ScriptInterpreter.cpp | 9 ++- .../Python/SWIGPythonBridge.h | 5 ++ .../Python/ScriptInterpreterPython.cpp | 8 ++- .../Python/ScriptInterpreterPythonImpl.h | 3 +- lldb/test/API/macosx/dsym_modules/Makefile | 4 ++ .../macosx/dsym_modules/TestdSYMModuleInit.py | 63 +++++++++++++++++++ lldb/test/API/macosx/dsym_modules/has_dsym.py | 28 +++++++++ lldb/test/API/macosx/dsym_modules/main.c | 9 +++ .../Python/PythonTestSuite.cpp | 6 ++ 12 files changed, 155 insertions(+), 9 deletions(-) create mode 100644 lldb/test/API/macosx/dsym_modules/Makefile create mode 100644 lldb/test/API/macosx/dsym_modules/TestdSYMModuleInit.py create mode 100644 lldb/test/API/macosx/dsym_modules/has_dsym.py create mode 100644 lldb/test/API/macosx/dsym_modules/main.c diff --git a/lldb/bindings/python/python-wrapper.swig b/lldb/bindings/python/python-wrapper.swig index d01e1720de8a1..5bcce04fc9055 100644 --- a/lldb/bindings/python/python-wrapper.swig +++ b/lldb/bindings/python/python-wrapper.swig @@ -1051,6 +1051,28 @@ bool lldb_private::python::SWIGBridge::LLDBSWIGPythonRunScriptKeywordValue( return true; } +bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleNewTarget( + const char *python_module_name, const char *session_dictionary_name, + lldb::TargetSP target_sp) { + std::string python_function_name_string = python_module_name; + python_function_name_string += ".__lldb_module_added_to_target"; + const char *python_function_name = python_function_name_string.c_str(); + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName( + session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary( + python_function_name, dict); + + if (!pfunc.IsAllocated()) + return true; + + pfunc(SWIGBridge::ToSWIGWrapper(std::move(target_sp)), dict); + + return true; +} + bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit( const char *python_module_name, const char *session_dictionary_name, lldb::DebuggerSP debugger) { diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index c6e01475621c0..e67b179da1dec 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -539,7 +539,8 @@ class ScriptInterpreter : public PluginInterface { LoadScriptingModule(const char *filename, const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp = nullptr, - FileSpec extra_search_dir = {}); + FileSpec extra_search_dir = {}, + lldb::TargetSP loaded_into_target_sp = {}); virtual bool IsReservedWord(const char *word) { return false; } diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index cfe03d84185da..951e1432e6194 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -1624,7 +1624,9 @@ bool Module::LoadScriptingResourceInTarget(Target *target, Status &error, scripting_fspec.Dump(scripting_stream.AsRawOstream()); LoadScriptOptions options; bool did_load = script_interpreter->LoadScriptingModule( - scripting_stream.GetData(), options, error); + scripting_stream.GetData(), options, error, + /*module_sp*/ nullptr, /*extra_path*/ {}, + target->shared_from_this()); if (!did_load) return false; } diff --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp index 1c0c97d876a9f..4db95b67e6467 100644 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -48,11 +48,10 @@ StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() { return nullptr; } -bool ScriptInterpreter::LoadScriptingModule(const char *filename, - const LoadScriptOptions &options, - lldb_private::Status &error, - StructuredData::ObjectSP *module_sp, - FileSpec extra_search_dir) { +bool ScriptInterpreter::LoadScriptingModule( + const char *filename, const LoadScriptOptions &options, + lldb_private::Status &error, StructuredData::ObjectSP *module_sp, + FileSpec extra_search_dir, lldb::TargetSP loaded_into_target_sp) { error = Status::FromErrorString( "This script interpreter does not support importing modules."); return false; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h index d513ba15ab02c..677df615609d4 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h @@ -224,6 +224,11 @@ class SWIGBridge { const char *session_dictionary_name, lldb::DebuggerSP debugger); + static bool + LLDBSwigPythonCallModuleNewTarget(const char *python_module_name, + const char *session_dictionary_name, + lldb::TargetSP target); + static python::PythonObject LLDBSWIGPythonCreateOSPlugin(const char *python_class_name, const char *session_dictionary_name, diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 4b4fedc7507f3..696f8eebdb35d 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -2377,7 +2377,7 @@ uint64_t replace_all(std::string &str, const std::string &oldStr, bool ScriptInterpreterPythonImpl::LoadScriptingModule( const char *pathname, const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp, - FileSpec extra_search_dir) { + FileSpec extra_search_dir, lldb::TargetSP target_sp) { namespace fs = llvm::sys::fs; namespace path = llvm::sys::path; @@ -2555,6 +2555,12 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule( PyRefType::Owned, static_cast(module_pyobj))); } + // Finally, if we got a target passed in, then we should tell the new module + // about this target: + if (target_sp) + return SWIGBridge::LLDBSwigPythonCallModuleNewTarget( + module_name.c_str(), m_dictionary_name.c_str(), target_sp); + return true; } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h index 04d495fad26a4..812b44cb66b61 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h @@ -252,7 +252,8 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython { const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp = nullptr, - FileSpec extra_search_dir = {}) override; + FileSpec extra_search_dir = {}, + lldb::TargetSP loaded_into_target_sp = {}) override; bool IsReservedWord(const char *word) override; diff --git a/lldb/test/API/macosx/dsym_modules/Makefile b/lldb/test/API/macosx/dsym_modules/Makefile new file mode 100644 index 0000000000000..695335e068c0c --- /dev/null +++ b/lldb/test/API/macosx/dsym_modules/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -std=c99 + +include Makefile.rules diff --git a/lldb/test/API/macosx/dsym_modules/TestdSYMModuleInit.py b/lldb/test/API/macosx/dsym_modules/TestdSYMModuleInit.py new file mode 100644 index 0000000000000..cd2293acbc82a --- /dev/null +++ b/lldb/test/API/macosx/dsym_modules/TestdSYMModuleInit.py @@ -0,0 +1,63 @@ +""" +Test that we read in the Python module from a dSYM, and run the +init in debugger and the init in target routines. +""" + +import os, shutil + +import lldb +import lldbsuite.test.lldbutil as lldbutil +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +@skipUnlessDarwin +class TestdSYMModuleInit(TestBase): + @no_debug_info_test + def test_add_module(self): + """This loads a file into a target and ensures that the python module was + correctly added and the two intialization functions are called.""" + self.exe_name = "has_dsym" + self.py_name = self.exe_name + ".py" + + # Now load the target the first time into the debugger: + self.runCmd("settings set target.load-script-from-symbol-file true") + self.interp = self.dbg.GetCommandInterpreter() + + executable = self.build_dsym(self.exe_name + "_1") + target = self.createTestTarget(file_path=executable) + self.check_answers(executable, ["1", "1", "has_dsym_1"]) + + # Now make a second target and make sure both get called: + executable_2 = self.build_dsym(self.exe_name + "_2") + target_2 = self.createTestTarget(file_path=executable_2) + self.check_answers(executable_2, ["2", "2", "has_dsym_2"]) + + def check_answers(self, name, answers): + result = lldb.SBCommandReturnObject() + self.interp.HandleCommand("report_command", result) + self.assertTrue( + result.Succeeded(), f"report_command succeeded {result.GetError()}" + ) + + cmd_results = result.GetOutput().split() + self.assertEqual(answers[0], cmd_results[0], "Right number of module imports") + self.assertEqual(answers[1], cmd_results[1], "Right number of target notices") + self.assertIn(answers[2], name, "Right target name") + + def build_dsym(self, name): + self.build(debug_info="dsym", dictionary={"EXE": name}) + executable = self.getBuildArtifact(name) + dsym_path = self.getBuildArtifact(name + ".dSYM") + python_dir_path = dsym_path + python_dir_path = os.path.join(dsym_path, "Contents", "Resources", "Python") + if not os.path.exists(python_dir_path): + os.mkdir(python_dir_path) + + python_file_name = name + ".py" + + module_dest_path = os.path.join(python_dir_path, python_file_name) + module_origin_path = os.path.join(self.getSourceDir(), self.py_name) + shutil.copy(module_origin_path, module_dest_path) + + return executable diff --git a/lldb/test/API/macosx/dsym_modules/has_dsym.py b/lldb/test/API/macosx/dsym_modules/has_dsym.py new file mode 100644 index 0000000000000..babaa9e64cdb8 --- /dev/null +++ b/lldb/test/API/macosx/dsym_modules/has_dsym.py @@ -0,0 +1,28 @@ +import lldb + + +def report_command(debugger, command, exe_ctx, result, internal_dict): + result.AppendMessage( + f'{lldb.num_module_inits} {lldb.num_target_inits} "{lldb.target_name}"' + ) + result.SetStatus(lldb.eReturnStatusSuccessFinishResult) + + +def __lldb_init_module(debugger, internal_dict): + # We only want to make one copy of the report command so it will be shared + if "has_dsym_1" in __name__: + # lldb is a convenient place to store our counters. + lldb.num_module_inits = 0 + lldb.num_target_inits = 0 + lldb.target_name = "" + + debugger.HandleCommand( + f"command script add -o -f '{__name__}.report_command' report_command" + ) + + lldb.num_module_inits += 1 + + +def __lldb_module_added_to_target(target, internal_dict): + lldb.num_target_inits += 1 + target_name = target.executable.fullpath diff --git a/lldb/test/API/macosx/dsym_modules/main.c b/lldb/test/API/macosx/dsym_modules/main.c new file mode 100644 index 0000000000000..97dc01f98693b --- /dev/null +++ b/lldb/test/API/macosx/dsym_modules/main.c @@ -0,0 +1,9 @@ +#include + +int global_test_var = 10; + +int main() { + int test_var = 10; + printf("Set a breakpoint here: %d.\n", test_var); + return global_test_var; +} diff --git a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp index a2318afdf8cd5..ce1d30a5f437f 100644 --- a/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp +++ b/lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp @@ -225,6 +225,12 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit( return false; } +bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleNewTarget( + const char *python_module_name, const char *session_dictionary_name, + lldb::TargetSP target) { + return false; +} + python::PythonObject lldb_private::python::SWIGBridge::LLDBSWIGPythonCreateOSPlugin( const char *python_class_name, const char *session_dictionary_name, From 233147d834259acc203f379878246f7822728aab Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Tue, 1 Apr 2025 22:12:09 -0700 Subject: [PATCH 2/3] [lldb] Update ScriptInterpreterLua::LoadScriptingModule Update the ScriptInterpreterLua::LoadScriptingModule signature after the TargetSP argument was added in #133290. (cherry picked from commit 28b300d546948baf4396c3467507dea8b9e34881) --- .../Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp | 2 +- .../Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp index d4df74ad98b6a..fa0abe971b7af 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp @@ -228,7 +228,7 @@ void ScriptInterpreterLua::ExecuteInterpreterLoop() { bool ScriptInterpreterLua::LoadScriptingModule( const char *filename, const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp, - FileSpec extra_search_dir) { + FileSpec extra_search_dir, lldb::TargetSP loaded_into_target_sp) { if (llvm::Error e = m_lua->LoadModule(filename)) { error = Status::FromErrorStringWithFormatv( diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h index ca14e189acd84..7bfbac0ef9d00 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h @@ -47,7 +47,8 @@ class ScriptInterpreterLua : public ScriptInterpreter { const LoadScriptOptions &options, lldb_private::Status &error, StructuredData::ObjectSP *module_sp = nullptr, - FileSpec extra_search_dir = {}) override; + FileSpec extra_search_dir = {}, + lldb::TargetSP loaded_into_target_sp = {}) override; StructuredData::DictionarySP GetInterpreterInfo() override; From fdd9045c57be0ad52b7c86305f44c973fb1664db Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Wed, 9 Apr 2025 23:21:11 -0700 Subject: [PATCH 3/3] [lldb] Regenerate python static bindings Signed-off-by: Med Ismail Bennani --- .../python/static-binding/LLDBWrapPython.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lldb/bindings/python/static-binding/LLDBWrapPython.cpp b/lldb/bindings/python/static-binding/LLDBWrapPython.cpp index 46cf358c829ac..462e087aa5b1f 100644 --- a/lldb/bindings/python/static-binding/LLDBWrapPython.cpp +++ b/lldb/bindings/python/static-binding/LLDBWrapPython.cpp @@ -5895,6 +5895,28 @@ bool lldb_private::python::SWIGBridge::LLDBSWIGPythonRunScriptKeywordValue( return true; } +bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleNewTarget( + const char *python_module_name, const char *session_dictionary_name, + lldb::TargetSP target_sp) { + std::string python_function_name_string = python_module_name; + python_function_name_string += ".__lldb_module_added_to_target"; + const char *python_function_name = python_function_name_string.c_str(); + + PyErr_Cleaner py_err_cleaner(true); + + auto dict = PythonModule::MainModule().ResolveName( + session_dictionary_name); + auto pfunc = PythonObject::ResolveNameWithDictionary( + python_function_name, dict); + + if (!pfunc.IsAllocated()) + return true; + + pfunc(SWIGBridge::ToSWIGWrapper(std::move(target_sp)), dict); + + return true; +} + bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit( const char *python_module_name, const char *session_dictionary_name, lldb::DebuggerSP debugger) {