mib created this revision.
mib added reviewers: JDevlieghere, jasonmolenda.
mib added a project: LLDB.
Herald added a project: All.
mib requested review of this revision.
Herald added a subscriber: lldb-commits.

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

Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D120969

Files:
  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/baz.c
  lldb/test/API/functionalities/scripted_process/baz.h
  lldb/test/API/functionalities/scripted_process/main.cpp
  lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py

Index: lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py
===================================================================
--- lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py
+++ lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py
@@ -10,10 +10,10 @@
     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 +30,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
 
+        lib_path = args.GetValueForKey("libbaz_path")
+        if not (lib_path and lib_path.IsValid()) \
+            or lib_path.GetType() != lldb.eStructuredDataTypeString:
+                return
+
+        self.lib_path = lib_path.GetStringValue(256)
+        if not os.path.exists(self.lib_path):
+            return
+
+        lib_load_addr = 0x00000001001e0000
+        self.loaded_images.append({"path": self.lib_path,
+                                   "load_addr": lib_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)
Index: lldb/test/API/functionalities/scripted_process/main.cpp
===================================================================
--- lldb/test/API/functionalities/scripted_process/main.cpp
+++ 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;
 }
Index: lldb/test/API/functionalities/scripted_process/baz.h
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/scripted_process/baz.h
@@ -0,0 +1,3 @@
+#pragma once
+
+int baz(int j);
Index: lldb/test/API/functionalities/scripted_process/baz.c
===================================================================
--- /dev/null
+++ 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
+}
Index: lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
===================================================================
--- lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
+++ 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_named(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_named(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_named(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_named(target, 'libbaz.dylib')
+        self.assertTrue(scripted_dylib, "Dynamic library libbaz.dylib not found.")
+        self.assertEqual(scripted_dylib.GetObjectFileHeaderAddress().GetLoadAddress(target), 0x1001e0000)
Index: lldb/test/API/functionalities/scripted_process/Makefile
===================================================================
--- lldb/test/API/functionalities/scripted_process/Makefile
+++ 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
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.h
+++ 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;
 
Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
===================================================================
--- lldb/source/Plugins/ScriptInterpreter/Python/ScriptedProcessPythonInterface.cpp
+++ 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<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() {
Index: lldb/source/Plugins/Process/scripted/ScriptedProcess.h
===================================================================
--- lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ 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();
 
@@ -109,6 +112,7 @@
 
   // Member variables.
   const ScriptedProcessInfo m_scripted_process_info;
+
   lldb_private::ScriptInterpreter *m_interpreter = nullptr;
   lldb_private::StructuredData::ObjectSP m_script_object_sp = nullptr;
   //@}
Index: lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
===================================================================
--- lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -149,6 +149,8 @@
                                  ProcessLaunchInfo &launch_info) {
   CheckInterpreterAndScriptObject();
 
+  // clear image load list and section load list.
+
   /* FIXME: This doesn't reflect how lldb actually launches a process.
            In reality, it attaches to debugserver, then resume the process. */
   Status error = GetInterface().Launch();
@@ -170,6 +172,7 @@
 void ScriptedProcess::DidLaunch() {
   CheckInterpreterAndScriptObject();
   m_pid = GetInterface().GetProcessID();
+  GetLoadedDynamicLibrariesInfos();
 }
 
 Status ScriptedProcess::DoResume() {
@@ -378,6 +381,93 @@
   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 field '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();
 }
Index: lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
===================================================================
--- lldb/include/lldb/Interpreter/ScriptedProcessInterface.h
+++ 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; }
 
Index: lldb/examples/python/scripted_process/scripted_process.py
===================================================================
--- lldb/examples/python/scripted_process/scripted_process.py
+++ 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
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to