Skip to content

Commit 347c5a7

Browse files
authored
Add a new affordance that the Python module in a dSYM (llvm#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.
1 parent ec290a4 commit 347c5a7

File tree

12 files changed

+155
-9
lines changed

12 files changed

+155
-9
lines changed

lldb/bindings/python/python-wrapper.swig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,28 @@ bool lldb_private::python::SWIGBridge::LLDBSWIGPythonRunScriptKeywordValue(
966966
return true;
967967
}
968968

969+
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleNewTarget(
970+
const char *python_module_name, const char *session_dictionary_name,
971+
lldb::TargetSP target_sp) {
972+
std::string python_function_name_string = python_module_name;
973+
python_function_name_string += ".__lldb_module_added_to_target";
974+
const char *python_function_name = python_function_name_string.c_str();
975+
976+
PyErr_Cleaner py_err_cleaner(true);
977+
978+
auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
979+
session_dictionary_name);
980+
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
981+
python_function_name, dict);
982+
983+
if (!pfunc.IsAllocated())
984+
return true;
985+
986+
pfunc(SWIGBridge::ToSWIGWrapper(std::move(target_sp)), dict);
987+
988+
return true;
989+
}
990+
969991
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit(
970992
const char *python_module_name, const char *session_dictionary_name,
971993
lldb::DebuggerSP debugger) {

lldb/include/lldb/Interpreter/ScriptInterpreter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,8 @@ class ScriptInterpreter : public PluginInterface {
522522
LoadScriptingModule(const char *filename, const LoadScriptOptions &options,
523523
lldb_private::Status &error,
524524
StructuredData::ObjectSP *module_sp = nullptr,
525-
FileSpec extra_search_dir = {});
525+
FileSpec extra_search_dir = {},
526+
lldb::TargetSP loaded_into_target_sp = {});
526527

527528
virtual bool IsReservedWord(const char *word) { return false; }
528529

lldb/source/Core/Module.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,9 @@ bool Module::LoadScriptingResourceInTarget(Target *target, Status &error,
14851485
scripting_fspec.Dump(scripting_stream.AsRawOstream());
14861486
LoadScriptOptions options;
14871487
bool did_load = script_interpreter->LoadScriptingModule(
1488-
scripting_stream.GetData(), options, error);
1488+
scripting_stream.GetData(), options, error,
1489+
/*module_sp*/ nullptr, /*extra_path*/ {},
1490+
target->shared_from_this());
14891491
if (!did_load)
14901492
return false;
14911493
}

lldb/source/Interpreter/ScriptInterpreter.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
4848
return nullptr;
4949
}
5050

51-
bool ScriptInterpreter::LoadScriptingModule(const char *filename,
52-
const LoadScriptOptions &options,
53-
lldb_private::Status &error,
54-
StructuredData::ObjectSP *module_sp,
55-
FileSpec extra_search_dir) {
51+
bool ScriptInterpreter::LoadScriptingModule(
52+
const char *filename, const LoadScriptOptions &options,
53+
lldb_private::Status &error, StructuredData::ObjectSP *module_sp,
54+
FileSpec extra_search_dir, lldb::TargetSP loaded_into_target_sp) {
5655
error = Status::FromErrorString(
5756
"This script interpreter does not support importing modules.");
5857
return false;

lldb/source/Plugins/ScriptInterpreter/Python/SWIGPythonBridge.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ class SWIGBridge {
215215
const char *session_dictionary_name,
216216
lldb::DebuggerSP debugger);
217217

218+
static bool
219+
LLDBSwigPythonCallModuleNewTarget(const char *python_module_name,
220+
const char *session_dictionary_name,
221+
lldb::TargetSP target);
222+
218223
static python::PythonObject
219224
LLDBSWIGPythonCreateOSPlugin(const char *python_class_name,
220225
const char *session_dictionary_name,

lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2316,7 +2316,7 @@ uint64_t replace_all(std::string &str, const std::string &oldStr,
23162316
bool ScriptInterpreterPythonImpl::LoadScriptingModule(
23172317
const char *pathname, const LoadScriptOptions &options,
23182318
lldb_private::Status &error, StructuredData::ObjectSP *module_sp,
2319-
FileSpec extra_search_dir) {
2319+
FileSpec extra_search_dir, lldb::TargetSP target_sp) {
23202320
namespace fs = llvm::sys::fs;
23212321
namespace path = llvm::sys::path;
23222322

@@ -2495,6 +2495,12 @@ bool ScriptInterpreterPythonImpl::LoadScriptingModule(
24952495
PyRefType::Owned, static_cast<PyObject *>(module_pyobj)));
24962496
}
24972497

2498+
// Finally, if we got a target passed in, then we should tell the new module
2499+
// about this target:
2500+
if (target_sp)
2501+
return SWIGBridge::LLDBSwigPythonCallModuleNewTarget(
2502+
module_name.c_str(), m_dictionary_name.c_str(), target_sp);
2503+
24982504
return true;
24992505
}
25002506

lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPythonImpl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,8 @@ class ScriptInterpreterPythonImpl : public ScriptInterpreterPython {
245245
const LoadScriptOptions &options,
246246
lldb_private::Status &error,
247247
StructuredData::ObjectSP *module_sp = nullptr,
248-
FileSpec extra_search_dir = {}) override;
248+
FileSpec extra_search_dir = {},
249+
lldb::TargetSP loaded_into_target_sp = {}) override;
249250

250251
bool IsReservedWord(const char *word) override;
251252

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
CFLAGS_EXTRAS := -std=c99
3+
4+
include Makefile.rules
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""
2+
Test that we read in the Python module from a dSYM, and run the
3+
init in debugger and the init in target routines.
4+
"""
5+
6+
import os, shutil
7+
8+
import lldb
9+
import lldbsuite.test.lldbutil as lldbutil
10+
from lldbsuite.test.lldbtest import *
11+
from lldbsuite.test.decorators import *
12+
13+
14+
@skipUnlessDarwin
15+
class TestdSYMModuleInit(TestBase):
16+
@no_debug_info_test
17+
def test_add_module(self):
18+
"""This loads a file into a target and ensures that the python module was
19+
correctly added and the two intialization functions are called."""
20+
self.exe_name = "has_dsym"
21+
self.py_name = self.exe_name + ".py"
22+
23+
# Now load the target the first time into the debugger:
24+
self.runCmd("settings set target.load-script-from-symbol-file true")
25+
self.interp = self.dbg.GetCommandInterpreter()
26+
27+
executable = self.build_dsym(self.exe_name + "_1")
28+
target = self.createTestTarget(file_path=executable)
29+
self.check_answers(executable, ["1", "1", "has_dsym_1"])
30+
31+
# Now make a second target and make sure both get called:
32+
executable_2 = self.build_dsym(self.exe_name + "_2")
33+
target_2 = self.createTestTarget(file_path=executable_2)
34+
self.check_answers(executable_2, ["2", "2", "has_dsym_2"])
35+
36+
def check_answers(self, name, answers):
37+
result = lldb.SBCommandReturnObject()
38+
self.interp.HandleCommand("report_command", result)
39+
self.assertTrue(
40+
result.Succeeded(), f"report_command succeeded {result.GetError()}"
41+
)
42+
43+
cmd_results = result.GetOutput().split()
44+
self.assertEqual(answers[0], cmd_results[0], "Right number of module imports")
45+
self.assertEqual(answers[1], cmd_results[1], "Right number of target notices")
46+
self.assertIn(answers[2], name, "Right target name")
47+
48+
def build_dsym(self, name):
49+
self.build(debug_info="dsym", dictionary={"EXE": name})
50+
executable = self.getBuildArtifact(name)
51+
dsym_path = self.getBuildArtifact(name + ".dSYM")
52+
python_dir_path = dsym_path
53+
python_dir_path = os.path.join(dsym_path, "Contents", "Resources", "Python")
54+
if not os.path.exists(python_dir_path):
55+
os.mkdir(python_dir_path)
56+
57+
python_file_name = name + ".py"
58+
59+
module_dest_path = os.path.join(python_dir_path, python_file_name)
60+
module_origin_path = os.path.join(self.getSourceDir(), self.py_name)
61+
shutil.copy(module_origin_path, module_dest_path)
62+
63+
return executable
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import lldb
2+
3+
4+
def report_command(debugger, command, exe_ctx, result, internal_dict):
5+
result.AppendMessage(
6+
f'{lldb.num_module_inits} {lldb.num_target_inits} "{lldb.target_name}"'
7+
)
8+
result.SetStatus(lldb.eReturnStatusSuccessFinishResult)
9+
10+
11+
def __lldb_init_module(debugger, internal_dict):
12+
# We only want to make one copy of the report command so it will be shared
13+
if "has_dsym_1" in __name__:
14+
# lldb is a convenient place to store our counters.
15+
lldb.num_module_inits = 0
16+
lldb.num_target_inits = 0
17+
lldb.target_name = "<unknown>"
18+
19+
debugger.HandleCommand(
20+
f"command script add -o -f '{__name__}.report_command' report_command"
21+
)
22+
23+
lldb.num_module_inits += 1
24+
25+
26+
def __lldb_module_added_to_target(target, internal_dict):
27+
lldb.num_target_inits += 1
28+
target_name = target.executable.fullpath
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <stdio.h>
2+
3+
int global_test_var = 10;
4+
5+
int main() {
6+
int test_var = 10;
7+
printf("Set a breakpoint here: %d.\n", test_var);
8+
return global_test_var;
9+
}

lldb/unittests/ScriptInterpreter/Python/PythonTestSuite.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleInit(
230230
return false;
231231
}
232232

233+
bool lldb_private::python::SWIGBridge::LLDBSwigPythonCallModuleNewTarget(
234+
const char *python_module_name, const char *session_dictionary_name,
235+
lldb::TargetSP target) {
236+
return false;
237+
}
238+
233239
python::PythonObject
234240
lldb_private::python::SWIGBridge::LLDBSWIGPythonCreateOSPlugin(
235241
const char *python_class_name, const char *session_dictionary_name,

0 commit comments

Comments
 (0)