[Lldb-commits] [lldb] 2ff3b18 - Allow option to ignore module load errors in ScriptedProcess (#127153)

via lldb-commits lldb-commits at lists.llvm.org
Sun Feb 23 00:51:46 PST 2025


Author: rchamala
Date: 2025-02-23T00:51:43-08:00
New Revision: 2ff3b18554b115b17d5085b9a4cd779eeafd278a

URL: https://github.com/llvm/llvm-project/commit/2ff3b18554b115b17d5085b9a4cd779eeafd278a
DIFF: https://github.com/llvm/llvm-project/commit/2ff3b18554b115b17d5085b9a4cd779eeafd278a.diff

LOG: Allow option to ignore module load errors in ScriptedProcess (#127153)

Current state in scripted process expects [all the
modules](https://github.com/llvm/llvm-project/blob/912b154f3a3f8c3cebf5cc5731fd8b0749762da5/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp#L498)
passed into "get_loaded_images" to load successfully else none of them
load. Even if a module loads fine, [but has already been
appended](https://github.com/llvm/llvm-project/blob/912b154f3a3f8c3cebf5cc5731fd8b0749762da5/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp#L495)
it still fails. This is restrictive and does not help our usecase.

**Usecase**: We have a parent scripted process using coredump +
tombstone.

1) Scripted process uses child elf-core process to read memory dump

2) Uses tombstones to pass thread names and modules.

We do not know whether the modules will be successfully downloaded
before creating the scripted process. We use [python module
callbacks](https://github.com/llvm/llvm-project/blob/a57e58dbfaae0e86eb5cafeddf8b598f14b96e36/lldb/source/Target/Platform.cpp#L1593)
to download a module from symbol server at LLDB load time when the
scripted process is being created. The issue is that if one of the
symbol is not found from the list specified in tombstone, none of the
modules load in scripted process. Even if we ensure symbols are present
in symbol server before creating the scripted process, if the load
address is wrong or if the module is already appended, all module loads
are skipped.

**Solution**: Pass in a custom boolean option arg for every module from
python scripted process plugin which will indicate whether to ignore the
module load error. This will provide the flexibility to user for loading
the successfully fetched modules into target while ignoring the failed
ones

---------

Co-authored-by: rchamala <rachamal at fb.com>

Added: 
    

Modified: 
    lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
    lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
    lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index d2111ce877ce5..6b371a151668d 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -25,6 +25,8 @@
 #include "lldb/Utility/ScriptedMetadata.h"
 #include "lldb/Utility/State.h"
 
+#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"
+
 #include <mutex>
 
 LLDB_PLUGIN_DEFINE(ScriptedProcess)
@@ -165,7 +167,7 @@ Status ScriptedProcess::DoLoadCore() {
 Status ScriptedProcess::DoLaunch(Module *exe_module,
                                  ProcessLaunchInfo &launch_info) {
   LLDB_LOGF(GetLog(LLDBLog::Process), "ScriptedProcess::%s launching process", __FUNCTION__);
-  
+
   /* MARK: This doesn't reflect how lldb actually launches a process.
            In reality, it attaches to debugserver, then resume the process.
            That's not true in all cases.  If debugserver is remote, lldb
@@ -444,7 +446,6 @@ ScriptedProcess::GetLoadedDynamicLibrariesInfos() {
       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");
@@ -453,22 +454,17 @@ ScriptedProcess::GetLoadedDynamicLibrariesInfos() {
     if (!dict->HasKey("load_addr"))
       return error_with_message("Dictionary is missing key 'load_addr'");
 
+    llvm::StringRef path = "";
     if (has_path) {
-      dict->GetValueForKeyAsString("path", value);
-      module_spec.GetFileSpec().SetPath(value);
+      dict->GetValueForKeyAsString("path", path);
+      module_spec.GetFileSpec().SetPath(path);
     }
 
+    llvm::StringRef uuid = "";
     if (has_uuid) {
-      dict->GetValueForKeyAsString("uuid", value);
-      module_spec.GetUUID().SetFromStringRef(value);
+      dict->GetValueForKeyAsString("uuid", uuid);
+      module_spec.GetUUID().SetFromStringRef(uuid);
     }
-    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::offset_t slide = LLDB_INVALID_OFFSET;
@@ -481,6 +477,27 @@ ScriptedProcess::GetLoadedDynamicLibrariesInfos() {
     if (slide != LLDB_INVALID_OFFSET)
       load_addr += slide;
 
+    module_spec.GetArchitecture() = target.GetArchitecture();
+
+    ModuleSP module_sp =
+        target.GetOrCreateModule(module_spec, true /* notify */);
+
+    bool is_placeholder_module = false;
+
+    if (!module_sp) {
+      // Create a placeholder module
+      LLDB_LOGF(
+          GetLog(LLDBLog::Process),
+          "ScriptedProcess::%s unable to locate the matching "
+          "object file path %s, creating a placeholder module at 0x%" PRIx64,
+          __FUNCTION__, path.str().c_str(), load_addr);
+
+      module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>(
+          module_spec, load_addr, module_spec.GetFileSpec().MemorySize());
+
+      is_placeholder_module = true;
+    }
+
     bool changed = false;
     module_sp->SetLoadAddress(target, load_addr, false /*=value_is_offset*/,
                               changed);
@@ -488,16 +505,27 @@ ScriptedProcess::GetLoadedDynamicLibrariesInfos() {
     if (!changed && !module_sp->GetObjectFile())
       return error_with_message("Couldn't set the load address for module.");
 
-    dict->GetValueForKeyAsString("path", value);
-    FileSpec objfile(value);
+    FileSpec objfile(path);
     module_sp->SetFileSpecAndObjectName(objfile, objfile.GetFilename());
 
+    if (is_placeholder_module) {
+      target.GetImages().AppendIfNeeded(module_sp, true /*notify=*/);
+      return true;
+    }
+
     return module_list.AppendIfNeeded(module_sp);
   };
 
-  if (!loaded_images_sp->ForEach(reload_image))
-    return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(
-        LLVM_PRETTY_FUNCTION, "Couldn't reload all images.", error);
+  size_t loaded_images_size = loaded_images_sp->GetSize();
+  bool print_error = true;
+  for (size_t idx = 0; idx < loaded_images_size; idx++) {
+    const auto &loaded_image = loaded_images_sp->GetItemAtIndex(idx);
+    if (!reload_image(loaded_image.get()) && print_error) {
+      print_error = false;
+      ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(
+          LLVM_PRETTY_FUNCTION, "Couldn't reload all images.", error);
+    }
+  }
 
   target.ModulesDidLoad(module_list);
 

diff  --git a/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py b/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
index a5c79378bab50..22b0d01c2c173 100644
--- a/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
+++ b/lldb/test/API/functionalities/scripted_process/TestStackCoreScriptedProcess.py
@@ -76,6 +76,12 @@ def cleanup():
             )
         self.assertTrue(corefile_process, PROCESS_IS_VALID)
 
+        # Create a random lib which does not exist in the corefile.
+        random_dylib = self.get_module_with_name(corefile_target, "random.dylib")
+        self.assertFalse(
+            random_dylib, "Dynamic library random.dylib should not be found."
+        )
+
         structured_data = lldb.SBStructuredData()
         structured_data.SetFromJSON(
             json.dumps(
@@ -83,7 +89,15 @@ def cleanup():
                     "backing_target_idx": self.dbg.GetIndexOfTarget(
                         corefile_process.GetTarget()
                     ),
-                    "libbaz_path": self.getBuildArtifact("libbaz.dylib"),
+                    "custom_modules": [
+                        {
+                            "path": self.getBuildArtifact("libbaz.dylib"),
+                        },
+                        {
+                            "path": "/random/path/random.dylib",
+                            "load_addr": 12345678,
+                        },
+                    ],
                 }
             )
         )

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 8641d9a7ced35..736bb69397f9b 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
@@ -46,22 +46,63 @@ def __init__(self, exe_ctx: lldb.SBExecutionContext, args: lldb.SBStructuredData
         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
-        )
+        custom_modules = args.GetValueForKey("custom_modules")
+        if custom_modules.GetType() == lldb.eStructuredDataTypeArray:
+            for id in range(custom_modules.GetSize()):
+
+                custom_module = custom_modules.GetItemAtIndex(id)
+                if (
+                    not custom_module
+                    or not custom_module.IsValid()
+                    or not custom_module.GetType() == lldb.eStructuredDataTypeDictionary
+                ):
+                    continue
+
+                # Get custom module path from args
+                module_path_arg = custom_module.GetValueForKey("path")
+                module_path = None
+                if (
+                    not module_path_arg
+                    or not module_path_arg.IsValid()
+                    or not module_path_arg.GetType() == lldb.eStructuredDataTypeString
+                ):
+                    continue
+
+                module_path = module_path_arg.GetStringValue(100)
+
+                # Get custom module load address from args
+                module_load_addr = None
+                module_load_addr_arg = custom_module.GetValueForKey("load_addr")
+                if (
+                    module_load_addr_arg
+                    and module_load_addr_arg.IsValid()
+                    and module_load_addr_arg.GetType()
+                    == lldb.eStructuredDataTypeInteger
+                ):
+                    module_load_addr = module_load_addr_arg.GetIntegerValue()
+
+                # If module load address is not specified/valid, try to find it from corefile module
+                if module_load_addr is None:
+                    module_name = os.path.basename(module_path)
+                    corefile_module = self.get_module_with_name(
+                        self.corefile_target, module_name
+                    )
 
-        self.loaded_images.append({"path": module_path, "load_addr": module_load_addr})
+                    if not corefile_module or not corefile_module.IsValid():
+                        continue
+
+                    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


        


More information about the lldb-commits mailing list