[Lldb-commits] [lldb] 680ca7f - [lldb/Plugins] Add ability to load modules to Scripted Processes

Med Ismail Bennani via lldb-commits lldb-commits at lists.llvm.org
Fri Mar 4 13:35:44 PST 2022


Author: Med Ismail Bennani
Date: 2022-03-04T13:35:28-08:00
New Revision: 680ca7f21a7716698227966a9e70e7efb75cff7e

URL: https://github.com/llvm/llvm-project/commit/680ca7f21a7716698227966a9e70e7efb75cff7e
DIFF: https://github.com/llvm/llvm-project/commit/680ca7f21a7716698227966a9e70e7efb75cff7e.diff

LOG: [lldb/Plugins] Add ability to load modules to Scripted Processes

This patch introduces a new way to load modules programatically with
Scripted Processes. To do so, the scripted process blueprint holds a
list of dictionary describing the modules to load, which their path or
uuid, load address and eventually a slide offset.

LLDB will fetch that list after launching the ScriptedProcess, and
iterate over each entry to create the module that will be loaded in the
Scripted Process' target.

The patch also refactors the StackCoreScriptedProcess test to stop
inside the `libbaz` module and make sure it's loaded correctly and that
we can fetch some variables from it.

rdar://74520238

Differential Revision: https://reviews.llvm.org/D120969

Signed-off-by: Med Ismail Bennani <medismail.bennani at gmail.com>

Added: 
    lldb/test/API/functionalities/scripted_process/baz.c
    lldb/test/API/functionalities/scripted_process/baz.h

Modified: 
    lldb/examples/python/scripted_process/scripted_process.py
    lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
    lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
    lldb/source/Plugins/Process/scripted/ScriptedProcess.h
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
    lldb/test/API/functionalities/scripted_process/Makefile
    lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
    lldb/test/API/functionalities/scripted_process/main.cpp
    lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py

Removed: 
    


################################################################################
diff  --git a/lldb/examples/python/scripted_process/scripted_process.py b/lldb/examples/python/scripted_process/scripted_process.py
index 2d3092d70e8e0..5969737c8b4bd 100644
--- a/lldb/examples/python/scripted_process/scripted_process.py
+++ b/lldb/examples/python/scripted_process/scripted_process.py
@@ -19,7 +19,7 @@ class ScriptedProcess:
     memory_regions = None
     stack_memory_dump = None
     loaded_images = None
-    threads = {}
+    threads = None
 
     @abstractmethod
     def __init__(self, target, args):
@@ -41,6 +41,8 @@ def __init__(self, target, args):
             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 @@ def get_loaded_images(self):
 
         ```
         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
index 0712b3bf4a3ee..26d285b3a3e08 100644
--- a/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
+++ b/lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
@@ -57,7 +57,7 @@ class ScriptedProcessInterface : virtual public ScriptedInterface {
     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.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index 96ae305f875ad..e36b29db0feb5 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -170,6 +170,7 @@ Status ScriptedProcess::DoLaunch(Module *exe_module,
 void ScriptedProcess::DidLaunch() {
   CheckInterpreterAndScriptObject();
   m_pid = GetInterface().GetProcessID();
+  GetLoadedDynamicLibrariesInfos();
 }
 
 Status ScriptedProcess::DoResume() {
@@ -378,6 +379,93 @@ bool ScriptedProcess::GetProcessInfo(ProcessInstanceInfo &info) {
   return true;
 }
 
+lldb_private::StructuredData::ObjectSP
+ScriptedProcess::GetLoadedDynamicLibrariesInfos() {
+  CheckInterpreterAndScriptObject();
+
+  Status error;
+  auto error_with_message = [&error](llvm::StringRef message) {
+    return ScriptedInterface::ErrorWithMessage<bool>(LLVM_PRETTY_FUNCTION,
+                                                     message.data(), error);
+  };
+
+  StructuredData::ArraySP loaded_images_sp = GetInterface().GetLoadedImages();
+
+  if (!loaded_images_sp || !loaded_images_sp->GetSize())
+    return GetInterface().ErrorWithMessage<StructuredData::ObjectSP>(
+        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<StructuredData::ObjectSP>(
+        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/Process/scripted/ScriptedProcess.h b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
index c8355f35548a8..7edd95e230a16 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
@@ -89,6 +89,9 @@ class ScriptedProcess : public Process {
 
   bool GetProcessInfo(ProcessInstanceInfo &info) override;
 
+  lldb_private::StructuredData::ObjectSP
+  GetLoadedDynamicLibrariesInfos() override;
+
 protected:
   Status DoStop();
 

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
index f8edf228965f4..576bf69c9258e 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
@@ -124,9 +124,21 @@ lldb::DataExtractorSP ScriptedProcessPythonInterface::ReadMemoryAtAddress(
                                          address, size);
 }
 
-StructuredData::DictionarySP ScriptedProcessPythonInterface::GetLoadedImages() {
-  // TODO: Implement
-  return {};
+StructuredData::ArraySP ScriptedProcessPythonInterface::GetLoadedImages() {
+  Status error;
+  StructuredData::ArraySP array =
+      Dispatch<StructuredData::ArraySP>("get_loaded_images", error);
+
+  if (!array || !array->IsValid() || error.Fail()) {
+    return ScriptedInterface::ErrorWithMessage<StructuredData::ArraySP>(
+        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/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
index e34a181849eb4..7f458b1dd9bdb 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
@@ -49,7 +49,7 @@ class ScriptedProcessPythonInterface : public ScriptedProcessInterface,
   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/test/API/functionalities/scripted_process/Makefile b/lldb/test/API/functionalities/scripted_process/Makefile
index 785b17c7fd698..ca7f8f7d6d827 100644
--- 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
index 3f4222fe3b79c..b24648e80f1d8 100644
--- a/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
+++ b/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
@@ -25,13 +25,19 @@ def tearDown(self):
     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 @@ def test_launch_scripted_process_stack_frames(self):
         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 @@ def cleanup():
 
         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 @@ def cleanup():
         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 @@ def cleanup():
         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.c b/lldb/test/API/functionalities/scripted_process/baz.c
new file mode 100644
index 0000000000000..cfd452e7779c8
--- /dev/null
+++ b/lldb/test/API/functionalities/scripted_process/baz.c
@@ -0,0 +1,8 @@
+#include "baz.h"
+
+#include <math.h>
+
+int baz(int j) {
+  int k = sqrt(j);
+  return k; // break here
+}

diff  --git a/lldb/test/API/functionalities/scripted_process/baz.h b/lldb/test/API/functionalities/scripted_process/baz.h
new file mode 100644
index 0000000000000..aa667d2333922
--- /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/main.cpp b/lldb/test/API/functionalities/scripted_process/main.cpp
index 26dc123558921..41d137eeff8f3 100644
--- a/lldb/test/API/functionalities/scripted_process/main.cpp
+++ b/lldb/test/API/functionalities/scripted_process/main.cpp
@@ -2,16 +2,20 @@
 #include <mutex>
 #include <thread>
 
+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
index 5ac4e8d68a9df..67a1e53b7fbdd 100644
--- 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 @@ def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData):
 
                 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 @@ def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData:
         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:


        


More information about the lldb-commits mailing list