lemo created this revision.
lemo added reviewers: clayborg, labath, amccarth.
lemo added a project: LLDB.
Herald added subscribers: JDevlieghere, aprantl.

Normally, LLDB is creating a high-fidelity representation of a live
process, including a list of modules and sections, with the 
associated memory address ranges. In order to build the module and
section map LLDB tries to locate the local module image (object file)
and will parse it.

This does not work for postmortem debugging scenarios where the crash
dump (minidump in this case) was captured on a different machine.

Also, we may want to completly disable the search for matching
local object files if we load minidumps unless we can prove that the
local image matches the one from the crash origin.
(not part of this change, see: llvm.org/pr35193)

Fortunatelly the minidump format encodes enough information about
each module's memory range to allow us to create placeholder modules.
This enables most LLDB functionality involving address-to-module
translations:

Example: Identify the module from a stack frame PC:
---------------------------------------------------

Before:

- thread #1, stop reason = Exception 0xc0000005 encountered at address 0x164d14
  - frame #0: 0x00164d14 frame #1: 0x00167c79 frame #2: 0x00167e6d frame #3: 
0x7510336a frame #4: 0x77759882 frame #5: 0x77759855

After:

- thread #1, stop reason = Exception 0xc0000005 encountered at address 0x164d14
  - frame #0: 0x00164d14 C:\Users\amccarth\Documents\Visual Studio 
2013\Projects\fizzbuzz\Debug\fizzbuzz.exe frame #1: 0x00167c79 
C:\Users\amccarth\Documents\Visual Studio 
2013\Projects\fizzbuzz\Debug\fizzbuzz.exe frame #2: 0x00167e6d 
C:\Users\amccarth\Documents\Visual Studio 
2013\Projects\fizzbuzz\Debug\fizzbuzz.exe frame #3: 0x7510336a 
C:\Windows\SysWOW64\kernel32.dll frame #4: 0x77759882 
C:\Windows\SysWOW64\ntdll.dll frame #5: 0x77759855 C:\Windows\SysWOW64\ntdll.dll

Example: target modules list
----------------------------

Before:
error: the target has no associated executable images

After:
[  0] C:\Windows\System32\MSVCP120D.dll 
[  1] C:\Windows\SysWOW64\kernel32.dll 
[  2] C:\Users\amccarth\Documents\Visual Studio 
2013\Projects\fizzbuzz\Debug\fizzbuzz.exe 
[  3] C:\Windows\System32\MSVCR120D.dll 
[  4] C:\Windows\SysWOW64\KERNELBASE.dll 
[  5] C:\Windows\SysWOW64\ntdll.dll

NOTE: the minidump format also includes the debug info GUID, so we can
fill-in the module UUID from it, but this part was excluded from this change
to keep the changes simple (the LLDB UUID is hardcoded to be either 16 or 20
bytes, while the CodeView GUIDs are normally 24 bytes)


https://reviews.llvm.org/D45700

Files:
  
packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
  
packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py
  source/Commands/CommandObjectTarget.cpp
  source/Core/Section.cpp
  source/Plugins/Process/minidump/ProcessMinidump.cpp
  source/Plugins/Process/minidump/ProcessMinidump.h

Index: source/Plugins/Process/minidump/ProcessMinidump.h
===================================================================
--- source/Plugins/Process/minidump/ProcessMinidump.h
+++ source/Plugins/Process/minidump/ProcessMinidump.h
@@ -61,6 +61,8 @@
 
   uint32_t GetPluginVersion() override;
 
+  SystemRuntime *GetSystemRuntime() override { return nullptr; }
+
   Status DoDestroy() override;
 
   void RefreshStateAfterStop() override;
Index: source/Plugins/Process/minidump/ProcessMinidump.cpp
===================================================================
--- source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -19,6 +19,7 @@
 #include "lldb/Core/State.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/SectionLoadList.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/UnixSignals.h"
 #include "lldb/Utility/DataBufferLLVM.h"
@@ -31,9 +32,53 @@
 // C includes
 // C++ includes
 
+using namespace lldb;
 using namespace lldb_private;
 using namespace minidump;
 
+//------------------------------------------------------------------
+/// A placeholder module used for minidumps, where the original
+/// object files may not be available (so we can't parse the object
+/// files to extract the set of sections/segments)
+///
+/// This placeholder module has a single synthetic section (.module_image)
+/// which represents the module memory range covering the whole module.
+//------------------------------------------------------------------
+class PlaceholderModule : public Module {
+public:
+  PlaceholderModule(const FileSpec &file_spec, const ArchSpec &arch) :
+    Module(file_spec, arch) {}
+
+  // Creates a synthetic module section covering the whole module image
+  void CreateImageSection(const MinidumpModule *module, Target& target) {
+    const ConstString section_name(".module_image");
+    lldb::SectionSP section_sp(new Section(
+        shared_from_this(),     // Module to which this section belongs.
+        nullptr,                // ObjectFile
+        0,                      // Section ID.
+        section_name,           // Section name.
+        eSectionTypeContainer,  // Section type.
+        module->base_of_image,  // VM address.
+        module->size_of_image,  // VM size in bytes of this section.
+        0,                      // Offset of this section in the file.
+        module->size_of_image,  // Size of the section as found in the file.
+        12,                     // Alignment of the section (log2)
+        0,                      // Flags for this section.
+        1));                    // Number of host bytes per target byte
+    section_sp->SetPermissions(ePermissionsExecutable | ePermissionsReadable);
+    m_sections.AddSection(section_sp);
+    target.GetSectionLoadList().SetSectionLoadAddress(
+        section_sp, module->base_of_image);
+  }
+
+  ObjectFile *GetObjectFile() override { return nullptr; }
+
+  SectionList *GetSectionList() override { return &m_sections; }
+
+private:
+  SectionList m_sections;
+};
+
 ConstString ProcessMinidump::GetPluginNameStatic() {
   static ConstString g_name("minidump");
   return g_name;
@@ -281,7 +326,18 @@
     Status error;
     lldb::ModuleSP module_sp = GetTarget().GetSharedModule(module_spec, &error);
     if (!module_sp || error.Fail()) {
-      continue;
+      // We failed to locate a matching local object file. Fortunatelly
+      // the minidump format encodes enough information about each module's
+      // memory range to allow us to create placeholder modules.
+      //
+      // This enables most LLDB functionality involving address-to-module
+      // translations (ex. identifing the module for a stack frame PC) and
+      // modules/sections commands (ex. target modules list, ...)
+      auto my_module =
+          std::make_shared<PlaceholderModule>(file_spec, GetArchitecture());
+      my_module->CreateImageSection(module, GetTarget());
+      module_sp = my_module;
+      GetTarget().GetImages().Append(module_sp);
     }
 
     if (log) {
Index: source/Core/Section.cpp
===================================================================
--- source/Core/Section.cpp
+++ source/Core/Section.cpp
@@ -326,10 +326,11 @@
     // The top most section prints the module basename
     const char *name = NULL;
     ModuleSP module_sp(GetModule());
-    const FileSpec &file_spec = m_obj_file->GetFileSpec();
 
-    if (m_obj_file)
+    if (m_obj_file) {
+      const FileSpec &file_spec = m_obj_file->GetFileSpec();
       name = file_spec.GetFilename().AsCString();
+    }
     if ((!name || !name[0]) && module_sp)
       name = module_sp->GetFileSpec().GetFilename().AsCString();
     if (name && name[0])
Index: source/Commands/CommandObjectTarget.cpp
===================================================================
--- source/Commands/CommandObjectTarget.cpp
+++ source/Commands/CommandObjectTarget.cpp
@@ -1388,7 +1388,8 @@
           strm.EOL();
         }
         ObjectFile *objfile = module->GetObjectFile();
-        objfile->Dump(&strm);
+        if (objfile)
+          objfile->Dump(&strm);
       }
     }
     strm.IndentLess();
Index: packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
===================================================================
--- packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
+++ packages/Python/lldbsuite/test/functionalities/postmortem/minidump-new/TestMiniDumpNew.py
@@ -70,6 +70,17 @@
         self.assertEqual(self.process.GetProcessID(), self._linux_x86_64_pid)
         self.check_state()
 
+    def test_modules_in_mini_dump(self):
+        """Test that lldb can read the list of modules from the minidump."""
+        # target create -c linux-x86_64.dmp
+        self.dbg.CreateTarget(None)
+        self.target = self.dbg.GetSelectedTarget()
+        self.process = self.target.LoadCore("linux-x86_64.dmp")
+        self.assertTrue(self.process, PROCESS_IS_VALID)
+        self.assertEqual(self.target.GetNumModules(), 9)
+        for module in self.target.modules:
+            self.assertTrue(module.IsValid())
+
     def test_thread_info_in_minidump(self):
         """Test that lldb can read the thread information from the Minidump."""
         # target create -c linux-x86_64.dmp
@@ -100,6 +111,7 @@
         self.assertEqual(thread.GetNumFrames(), 2)
         frame = thread.GetFrameAtIndex(0)
         self.assertTrue(frame.IsValid())
+        self.assertTrue(frame.GetModule().IsValid())
         pc = frame.GetPC()
         eip = frame.FindRegister("pc")
         self.assertTrue(eip.IsValid())
Index: packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py
===================================================================
--- packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py
+++ packages/Python/lldbsuite/test/functionalities/postmortem/minidump/TestMiniDump.py
@@ -41,6 +41,17 @@
         stop_description = thread.GetStopDescription(256)
         self.assertTrue("0xc0000005" in stop_description)
 
+    def test_modules_in_mini_dump(self):
+        """Test that lldb can read the list of modules from the minidump."""
+        # target create -c fizzbuzz_no_heap.dmp
+        self.dbg.CreateTarget("")
+        self.target = self.dbg.GetSelectedTarget()
+        self.process = self.target.LoadCore("fizzbuzz_no_heap.dmp")
+        self.assertTrue(self.process, PROCESS_IS_VALID)
+        self.assertEqual(self.target.GetNumModules(), 6)
+        for module in self.target.modules:
+            self.assertTrue(module.IsValid())
+
     @expectedFailureAll(bugnumber="llvm.org/pr35193", hostoslist=["windows"])
     def test_stack_info_in_mini_dump(self):
         """Test that we can see a trivial stack in a VS-generate mini dump."""
@@ -58,6 +69,7 @@
             frame = thread.GetFrameAtIndex(i)
             self.assertTrue(frame.IsValid())
             self.assertEqual(frame.GetPC(), pc_list[i])
+            self.assertTrue(frame.GetModule().IsValid())
 
     @skipUnlessWindows # Minidump saving works only on windows
     def test_deeper_stack_in_mini_dump(self):
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to