diff --git a/lldb/examples/python/scripted_process/scripted_process.py b/lldb/examples/python/scripted_process/scripted_process.py --- a/lldb/examples/python/scripted_process/scripted_process.py +++ b/lldb/examples/python/scripted_process/scripted_process.py @@ -19,7 +19,7 @@ memory_regions = None stack_memory_dump = None loaded_images = None - threads = {} + threads = None @abstractmethod def __init__(self, target, args): @@ -41,6 +41,8 @@ self.dbg = target.GetDebugger() if isinstance(args, lldb.SBStructuredData) and args.IsValid(): self.args = args + self.threads = {} + self.loaded_images = [] @abstractmethod def get_memory_region_containing_address(self, addr): @@ -116,8 +118,7 @@ ``` class ScriptedProcessImage: - def __init__(name, file_spec, uuid, load_address): - self.name = name + def __init__(file_spec, uuid, load_address): self.file_spec = file_spec self.uuid = uuid self.load_address = load_address diff --git a/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h b/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h --- a/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h +++ b/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h @@ -57,7 +57,7 @@ return nullptr; } - virtual StructuredData::DictionarySP GetLoadedImages() { return nullptr; } + virtual StructuredData::ArraySP GetLoadedImages() { return nullptr; } virtual lldb::pid_t GetProcessID() { return LLDB_INVALID_PROCESS_ID; } diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h @@ -89,6 +89,9 @@ bool GetProcessInfo(ProcessInstanceInfo &info) override; + lldb_private::StructuredData::ObjectSP + GetLoadedDynamicLibrariesInfos() override; + protected: Status DoStop(); diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp --- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp +++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp @@ -170,6 +170,7 @@ void ScriptedProcess::DidLaunch() { CheckInterpreterAndScriptObject(); m_pid = GetInterface().GetProcessID(); + GetLoadedDynamicLibrariesInfos(); } Status ScriptedProcess::DoResume() { @@ -378,6 +379,93 @@ return true; } +lldb_private::StructuredData::ObjectSP +ScriptedProcess::GetLoadedDynamicLibrariesInfos() { + CheckInterpreterAndScriptObject(); + + Status error; + auto error_with_message = [&error](llvm::StringRef message) { + return ScriptedInterface::ErrorWithMessage(LLVM_PRETTY_FUNCTION, + message.data(), error); + }; + + StructuredData::ArraySP loaded_images_sp = GetInterface().GetLoadedImages(); + + if (!loaded_images_sp || !loaded_images_sp->GetSize()) + return GetInterface().ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "No loaded images.", error); + + ModuleList module_list; + Target &target = GetTarget(); + + auto reload_image = [&target, &module_list, &error_with_message]( + StructuredData::Object *obj) -> bool { + StructuredData::Dictionary *dict = obj->GetAsDictionary(); + + if (!dict) + return error_with_message("Couldn't cast image object into dictionary."); + + ModuleSpec module_spec; + llvm::StringRef value; + + bool has_path = dict->HasKey("path"); + bool has_uuid = dict->HasKey("uuid"); + if (!has_path && !has_uuid) + return error_with_message("Dictionary should have key 'path' or 'uuid'"); + if (!dict->HasKey("load_addr")) + return error_with_message("Dictionary is missing key 'load_addr'"); + + if (has_path) { + dict->GetValueForKeyAsString("path", value); + module_spec.GetFileSpec().SetPath(value); + } + + if (has_uuid) { + dict->GetValueForKeyAsString("uuid", value); + module_spec.GetUUID().SetFromStringRef(value); + } + module_spec.GetArchitecture() = target.GetArchitecture(); + + ModuleSP module_sp = + target.GetOrCreateModule(module_spec, true /* notify */); + + if (!module_sp) + return error_with_message("Couldn't create or get module."); + + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + lldb::addr_t slide = LLDB_INVALID_OFFSET; + dict->GetValueForKeyAsInteger("load_addr", load_addr); + dict->GetValueForKeyAsInteger("slide", slide); + if (load_addr == LLDB_INVALID_ADDRESS) + return error_with_message( + "Couldn't get valid load address or slide offset."); + + if (slide != LLDB_INVALID_OFFSET) + load_addr += slide; + + bool changed = false; + module_sp->SetLoadAddress(target, load_addr, false /*=value_is_offset*/, + changed); + + if (!changed && !module_sp->GetObjectFile()) + return error_with_message("Couldn't set the load address for module."); + + dict->GetValueForKeyAsString("path", value); + FileSpec objfile(value); + module_sp->SetFileSpecAndObjectName(objfile, objfile.GetFilename()); + + return module_list.AppendIfNeeded(module_sp); + }; + + if (!loaded_images_sp->ForEach(reload_image)) + return GetInterface().ErrorWithMessage( + LLVM_PRETTY_FUNCTION, "Couldn't reload all images.", error); + + target.ModulesDidLoad(module_list); + + return loaded_images_sp; +} + ScriptedProcessInterface &ScriptedProcess::GetInterface() const { return m_interpreter->GetScriptedProcessInterface(); } diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h @@ -49,7 +49,7 @@ lldb::DataExtractorSP ReadMemoryAtAddress(lldb::addr_t address, size_t size, Status &error) override; - StructuredData::DictionarySP GetLoadedImages() override; + StructuredData::ArraySP GetLoadedImages() override; lldb::pid_t GetProcessID() override; diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp @@ -124,9 +124,21 @@ address, size); } -StructuredData::DictionarySP ScriptedProcessPythonInterface::GetLoadedImages() { - // TODO: Implement - return {}; +StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() { + Status error; + StructuredData::ArraySP array = + Dispatch("get_loaded_images", error); + + if (!array || !array->IsValid() || error.Fail()) { + return ScriptedInterface::ErrorWithMessage( + LLVM_PRETTY_FUNCTION, + llvm::Twine("Null or invalid object (" + + llvm::Twine(error.AsCString()) + llvm::Twine(").")) + .str(), + error); + } + + return array; } lldb::pid_t ScriptedProcessPythonInterface::GetProcessID() { diff --git a/lldb/test/API/functionalities/scripted_process/Makefile b/lldb/test/API/functionalities/scripted_process/Makefile --- a/lldb/test/API/functionalities/scripted_process/Makefile +++ b/lldb/test/API/functionalities/scripted_process/Makefile @@ -1,4 +1,13 @@ CXX_SOURCES := main.cpp ENABLE_THREADS := YES -include Makefile.rules +LD_EXTRAS := -L. -lbaz -I. + +override ARCH := $(shell uname -m) + +all: libbaz.dylib a.out +libbaz.dylib: baz.c + $(MAKE) -f $(MAKEFILE_RULES) ARCH=$(ARCH) \ + DYLIB_ONLY=YES DYLIB_NAME=baz DYLIB_C_SOURCES=baz.c + +include Makefile.rules diff --git a/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py --- a/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py +++ b/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py @@ -25,13 +25,19 @@ def create_stack_skinny_corefile(self, file): self.build() target, process, thread, _ = lldbutil.run_to_source_breakpoint(self, "// break here", - lldb.SBFileSpec("main.cpp")) + lldb.SBFileSpec("baz.c")) self.assertTrue(process.IsValid(), "Process is invalid.") # FIXME: Use SBAPI to save the process corefile. self.runCmd("process save-core -s stack " + file) self.assertTrue(os.path.exists(file), "No stack-only corefile found.") self.assertTrue(self.dbg.DeleteTarget(target), "Couldn't delete target") + def get_module_with_name(self, target, name): + for module in target.modules: + if name in module.GetFileSpec().GetFilename(): + return module + return None + @skipUnlessDarwin @skipIfOutOfTreeDebugserver def test_launch_scripted_process_stack_frames(self): @@ -41,15 +47,15 @@ target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) self.assertTrue(target, VALID_TARGET) - for module in target.modules: - if 'a.out' in module.GetFileSpec().GetFilename(): - main_module = module - break - + main_module = self.get_module_with_name(target, 'a.out') self.assertTrue(main_module, "Invalid main module.") error = target.SetModuleLoadAddress(main_module, 0) self.assertSuccess(error, "Reloading main module at offset 0 failed.") + scripted_dylib = self.get_module_with_name(target, 'libbaz.dylib') + self.assertTrue(scripted_dylib, "Dynamic library libbaz.dylib not found.") + self.assertEqual(scripted_dylib.GetObjectFileHeaderAddress().GetLoadAddress(target), 0xffffffffffffffff) + os.environ['SKIP_SCRIPTED_PROCESS_LAUNCH'] = '1' def cleanup(): del os.environ["SKIP_SCRIPTED_PROCESS_LAUNCH"] @@ -68,7 +74,8 @@ structured_data = lldb.SBStructuredData() structured_data.SetFromJSON(json.dumps({ - "backing_target_idx" : self.dbg.GetIndexOfTarget(corefile_process.GetTarget()) + "backing_target_idx" : self.dbg.GetIndexOfTarget(corefile_process.GetTarget()), + "libbaz_path" : self.getBuildArtifact("libbaz.dylib") })) launch_info = lldb.SBLaunchInfo(None) launch_info.SetProcessPluginName("ScriptedProcess") @@ -81,10 +88,10 @@ self.assertTrue(process, PROCESS_IS_VALID) self.assertEqual(process.GetProcessID(), 42) - self.assertEqual(process.GetNumThreads(), 3) + self.assertEqual(process.GetNumThreads(), 2) thread = process.GetSelectedThread() self.assertTrue(thread, "Invalid thread.") - self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-2") + self.assertEqual(thread.GetName(), "StackCoreScriptedThread.thread-1") self.assertTrue(target.triple, "Invalid target triple") arch = target.triple.split('-')[0] @@ -102,9 +109,17 @@ else: self.assertTrue(thread.GetStopReason(), lldb.eStopReasonSignal) - self.assertEqual(thread.GetNumFrames(), 6) + self.assertEqual(thread.GetNumFrames(), 5) frame = thread.GetSelectedFrame() self.assertTrue(frame, "Invalid frame.") - self.assertIn("bar", frame.GetFunctionName()) - self.assertEqual(int(frame.FindValue("i", lldb.eValueTypeVariableArgument).GetValue()), 42) - self.assertEqual(int(frame.FindValue("j", lldb.eValueTypeVariableLocal).GetValue()), 42 * 42) + func = frame.GetFunction() + self.assertTrue(func, "Invalid function.") + + self.assertIn("baz", frame.GetFunctionName()) + self.assertEqual(frame.vars.GetSize(), 2) + self.assertEqual(int(frame.vars.GetFirstValueByName('j').GetValue()), 42 * 42) + self.assertEqual(int(frame.vars.GetFirstValueByName('k').GetValue()), 42) + + scripted_dylib = self.get_module_with_name(target, 'libbaz.dylib') + self.assertTrue(scripted_dylib, "Dynamic library libbaz.dylib not found.") + self.assertEqual(scripted_dylib.GetObjectFileHeaderAddress().GetLoadAddress(target), 0x1001e0000) diff --git a/lldb/test/API/functionalities/scripted_process/baz.h b/lldb/test/API/functionalities/scripted_process/baz.h new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_process/baz.h @@ -0,0 +1,3 @@ +#pragma once + +int baz(int j); diff --git a/lldb/test/API/functionalities/scripted_process/baz.c b/lldb/test/API/functionalities/scripted_process/baz.c new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/scripted_process/baz.c @@ -0,0 +1,8 @@ +#include "baz.h" + +#include + +int baz(int j) { + int k = sqrt(j); + return k; // break here +} diff --git a/lldb/test/API/functionalities/scripted_process/main.cpp b/lldb/test/API/functionalities/scripted_process/main.cpp --- a/lldb/test/API/functionalities/scripted_process/main.cpp +++ b/lldb/test/API/functionalities/scripted_process/main.cpp @@ -2,16 +2,20 @@ #include #include +extern "C" { +int baz(int); +} + int bar(int i) { int j = i * i; - return j; // break here + return j; } int foo(int i) { return bar(i); } void call_and_wait(int &n) { std::cout << "waiting for computation!" << std::endl; - while (n != 42 * 42) + while (baz(n) != 42) ; std::cout << "finished computation!" << std::endl; } diff --git a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py --- a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py @@ -7,13 +7,19 @@ from lldb.plugins.scripted_process import ScriptedThread class StackCoreScriptedProcess(ScriptedProcess): + def get_module_with_name(self, target, name): + for module in target.modules: + if name in module.GetFileSpec().GetFilename(): + return module + return None + def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData): super().__init__(target, args) - self.backing_target_idx = args.GetValueForKey("backing_target_idx") - self.corefile_target = None self.corefile_process = None + + self.backing_target_idx = args.GetValueForKey("backing_target_idx") if (self.backing_target_idx and self.backing_target_idx.IsValid()): if self.backing_target_idx.GetType() == lldb.eStructuredDataTypeInteger: idx = self.backing_target_idx.GetIntegerValue(42) @@ -30,9 +36,22 @@ self.threads[corefile_thread.GetThreadID()] = StackCoreScriptedThread(self, structured_data) - if len(self.threads) == 3: + if len(self.threads) == 2: self.threads[len(self.threads) - 1].is_stopped = True + corefile_module = self.get_module_with_name(self.corefile_target, + "libbaz.dylib") + if not corefile_module or not corefile_module.IsValid(): + return + module_path = os.path.join(corefile_module.GetFileSpec().GetDirectory(), + corefile_module.GetFileSpec().GetFilename()) + if not os.path.exists(module_path): + return + module_load_addr = corefile_module.GetObjectFileHeaderAddress().GetLoadAddress(self.corefile_target) + + self.loaded_images.append({"path": module_path, + "load_addr": module_load_addr}) + def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: mem_region = lldb.SBMemoryRegionInfo() error = self.corefile_process.GetMemoryRegionInfo(addr, mem_region) @@ -61,8 +80,6 @@ return data def get_loaded_images(self): - # TODO: Iterate over corefile_target modules and build a data structure - # from it. return self.loaded_images def get_process_id(self) -> int: