[Lldb-commits] [lldb] [lldb][split-dwarf] Add --errors-only argument separate-debug-info list (PR #71000)

Tom Yang via lldb-commits lldb-commits at lists.llvm.org
Wed Nov 1 16:06:18 PDT 2023


https://github.com/zhyty created https://github.com/llvm/llvm-project/pull/71000

Often, we only care about the split-dwarf files that have failed to load. This can be useful when diagnosing binaries with many separate debug info files where only some have errors. 

```
(lldb) help image dump separate-debug-info
List the separate debug info symbol files for one or more target modules.

Syntax: target modules dump separate-debug-info <cmd-options> [<filename> [<filename> [...]]]

Command Options Usage:
  target modules dump separate-debug-info [-ej] [<filename> [<filename> [...]]]

       -e ( --errors-only )
            Filter to show only debug info files with errors.

       -j ( --json )
            Output the details in JSON format.

     This command takes options and free-form arguments.  If your arguments
     resemble option specifiers (i.e., they start with a - or --), you must use
     ' -- ' between the end of the command options and the beginning of the
     arguments.

'image' is an abbreviation for 'target modules'
```

I updated the following tests
```
# on Linux
bin/lldb-dotest -p TestDumpDwo

# on Mac
bin/lldb-dotest -p TestDumpOso
```

This change applies to both the table and JSON outputs.

>From c6900333c54d1c3f5dd3e6a88f0627b65ff0efca Mon Sep 17 00:00:00 2001
From: Tom Yang <toyang at fb.com>
Date: Wed, 1 Nov 2023 00:53:19 -0700
Subject: [PATCH] [lldb] Add --errors-only argument separate-debug-info list

---
 lldb/include/lldb/Symbol/SymbolFile.h         |  6 +++++-
 lldb/source/Commands/CommandObjectTarget.cpp  | 20 +++++++++++++------
 lldb/source/Commands/Options.td               |  4 +++-
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      |  6 ++++--
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |  3 ++-
 .../DWARF/SymbolFileDWARFDebugMap.cpp         |  5 +++--
 .../DWARF/SymbolFileDWARFDebugMap.h           |  3 ++-
 .../dwo/TestDumpDwo.py                        | 20 ++++++++++++-------
 .../oso/TestDumpOso.py                        | 18 ++++++++++++-----
 9 files changed, 59 insertions(+), 26 deletions(-)

diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index b40d0f03b6e0130..9fc90ad49361be8 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -445,7 +445,11 @@ class SymbolFile : public PluginInterface {
   ///     contains the keys "type", "symfile", and "separate-debug-info-files".
   ///     "type" can be used to assume the structure of each object in
   ///     "separate-debug-info-files".
-  virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d) {
+  /// \param errors_only
+  ///     If true, then only return separate debug info files that encountered
+  ///     errors during loading.
+  virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                                    bool errors_only) {
     return false;
   };
 
diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp
index c84a6550d6c75cc..ca8484cc79d4054 100644
--- a/lldb/source/Commands/CommandObjectTarget.cpp
+++ b/lldb/source/Commands/CommandObjectTarget.cpp
@@ -1452,11 +1452,11 @@ static bool DumpModuleSymbolFile(Stream &strm, Module *module) {
 }
 
 static bool GetSeparateDebugInfoList(StructuredData::Array &list,
-                                     Module *module) {
+                                     Module *module, bool errors_only) {
   if (module) {
     if (SymbolFile *symbol_file = module->GetSymbolFile(/*can_create=*/true)) {
       StructuredData::Dictionary d;
-      if (symbol_file->GetSeparateDebugInfo(d)) {
+      if (symbol_file->GetSeparateDebugInfo(d, errors_only)) {
         list.AddItem(
             std::make_shared<StructuredData::Dictionary>(std::move(d)));
         return true;
@@ -2561,7 +2561,10 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
         m_json.SetCurrentValue(true);
         m_json.SetOptionWasSet();
         break;
-
+      case 'e':
+        m_errors_only.SetCurrentValue(true);
+        m_errors_only.SetOptionWasSet();
+        break;
       default:
         llvm_unreachable("Unimplemented option");
       }
@@ -2570,6 +2573,7 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
 
     void OptionParsingStarting(ExecutionContext *execution_context) override {
       m_json.Clear();
+      m_errors_only.Clear();
     }
 
     llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
@@ -2577,6 +2581,7 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
     }
 
     OptionValueBoolean m_json = false;
+    OptionValueBoolean m_errors_only = false;
   };
 
 protected:
@@ -2607,7 +2612,8 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
           break;
 
         if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
-                                     module_sp.get()))
+                                     module_sp.get(),
+                                     bool(m_options.m_errors_only)))
           num_dumped++;
       }
     } else {
@@ -2628,7 +2634,7 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
               break;
             Module *module = module_list.GetModulePointerAtIndex(i);
             if (GetSeparateDebugInfoList(separate_debug_info_lists_by_module,
-                                         module))
+                                         module, bool(m_options.m_errors_only)))
               num_dumped++;
           }
         } else
@@ -2639,11 +2645,13 @@ class CommandObjectTargetModulesDumpSeparateDebugInfoFiles
 
     if (num_dumped > 0) {
       Stream &strm = result.GetOutputStream();
+      // Display the debug info files in some format.
       if (m_options.m_json) {
+        // JSON format
         separate_debug_info_lists_by_module.Dump(strm,
                                                  /*pretty_print=*/true);
       } else {
-        // List the debug info files in human readable form.
+        // Human-readable table format
         separate_debug_info_lists_by_module.ForEach(
             [&result, &strm](StructuredData::Object *obj) {
               if (!obj) {
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 078b23e09e4fa83..542c78be5a12dad 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -10,7 +10,9 @@ let Command = "target modules dump symtab" in {
 
 let Command = "target modules dump separate debug info" in {
   def tm_json : Option<"json", "j">, Group<1>,
-  Desc<"Output the details in JSON format.">;
+    Desc<"Output the details in JSON format.">;
+  def tm_errors_only : Option<"errors-only", "e">, Group<1>,
+    Desc<"Filter to show only debug info files with errors.">;
 }
 
 let Command = "help" in {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index ee7164d2f050ed1..97c7dd933201d98 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4243,7 +4243,8 @@ void SymbolFileDWARF::DumpClangAST(Stream &s) {
   clang->Dump(s.AsRawOstream());
 }
 
-bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d) {
+bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                                           bool errors_only) {
   StructuredData::Array separate_debug_info_files;
   DWARFDebugInfo &info = DebugInfo();
   const size_t num_cus = info.GetNumUnits();
@@ -4296,7 +4297,8 @@ bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d) {
                               dwarf_cu->GetDwoError().AsCString("unknown"));
     }
     dwo_data->AddBooleanItem("loaded", dwo_symfile != nullptr);
-    separate_debug_info_files.AddItem(dwo_data);
+    if (!errors_only || (errors_only && dwo_data->HasKey("error")))
+      separate_debug_info_files.AddItem(dwo_data);
   }
 
   d.AddStringItem("type", "dwo");
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 069a2050f0eaadc..28430ccb87924b5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -268,7 +268,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
   void DumpClangAST(Stream &s) override;
 
   /// List separate dwo files.
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d) override;
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                            bool errors_only) override;
 
   DWARFContext &GetDWARFContext() { return m_context; }
 
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 2135ed784252f41..3eecd2005a1b2ef 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1278,7 +1278,7 @@ void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s) {
 }
 
 bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo(
-    lldb_private::StructuredData::Dictionary &d) {
+    lldb_private::StructuredData::Dictionary &d, bool errors_only) {
   StructuredData::Array separate_debug_info_files;
   const uint32_t cu_count = GetNumCompileUnits();
   for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
@@ -1302,7 +1302,8 @@ bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo(
       oso_data->AddStringItem("error", info.oso_load_error.AsCString());
     }
     oso_data->AddBooleanItem("loaded", loaded_successfully);
-    separate_debug_info_files.AddItem(oso_data);
+    if (!errors_only || (errors_only && oso_data->HasKey("error")))
+      separate_debug_info_files.AddItem(oso_data);
   }
 
   d.AddStringItem("type", "oso");
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index a337a76b7a69a66..13f94f6d93e9168 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -136,7 +136,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   void DumpClangAST(Stream &s) override;
 
   /// List separate oso files.
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d) override;
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                            bool errors_only) override;
 
   // PluginInterface protocol
   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py
index 3d9d8e8e77adbf9..163f5a112367693 100644
--- a/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py
@@ -12,7 +12,7 @@
 class TestDumpDWO(lldbtest.TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    def get_dwos_from_json(self):
+    def get_dwos_from_json_output(self):
         """Returns a dictionary of `symfile` -> {`dwo_name` -> dwo_info object}."""
         result = {}
         output = json.loads(self.res.GetOutput())
@@ -42,7 +42,7 @@ def test_dwos_loaded_json_output(self):
         self.runCmd("target modules dump separate-debug-info --json")
 
         # Check the output
-        output = self.get_dwos_from_json()
+        output = self.get_dwos_from_json_output()
         self.assertTrue(output[exe]["main.dwo"]["loaded"])
         self.assertTrue(output[exe]["foo.dwo"]["loaded"])
 
@@ -55,9 +55,8 @@ def test_dwos_not_loaded_json_output(self):
         main_dwo = self.getBuildArtifact("main.dwo")
         foo_dwo = self.getBuildArtifact("foo.dwo")
 
-        # REMOVE the dwo files
+        # REMOVE one of the dwo files
         os.unlink(main_dwo)
-        os.unlink(foo_dwo)
 
         target = self.dbg.CreateTarget(exe)
         self.assertTrue(target, lldbtest.VALID_TARGET)
@@ -65,11 +64,18 @@ def test_dwos_not_loaded_json_output(self):
         self.runCmd("target modules dump separate-debug-info --json")
 
         # Check the output
-        output = self.get_dwos_from_json()
+        output = self.get_dwos_from_json_output()
+        self.assertFalse(output[exe]["main.dwo"]["loaded"])
+        self.assertIn("error", output[exe]["main.dwo"])
+        self.assertTrue(output[exe]["foo.dwo"]["loaded"])
+        self.assertNotIn("error", output[exe]["foo.dwo"])
+
+        # Check with --errors-only
+        self.runCmd("target modules dump separate-debug-info --json --errors-only")
+        output = self.get_dwos_from_json_output()
         self.assertFalse(output[exe]["main.dwo"]["loaded"])
-        self.assertFalse(output[exe]["foo.dwo"]["loaded"])
         self.assertIn("error", output[exe]["main.dwo"])
-        self.assertIn("error", output[exe]["foo.dwo"])
+        self.assertNotIn("foo.dwo", output[exe])
 
     @skipIfRemote
     @skipIfDarwin
diff --git a/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py b/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py
index 05beed0eacfb00b..b69938454659bda 100644
--- a/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py
+++ b/lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py
@@ -12,7 +12,7 @@
 class TestDumpOso(lldbtest.TestBase):
     NO_DEBUG_INFO_TESTCASE = True
 
-    def get_osos_from_json(self):
+    def get_osos_from_json_output(self):
         """Returns a dictionary of `symfile` -> {`OSO_PATH` -> oso_info object}."""
         result = {}
         output = json.loads(self.res.GetOutput())
@@ -41,7 +41,7 @@ def test_shows_oso_loaded_json_output(self):
         self.runCmd("target modules dump separate-debug-info --json")
 
         # Check the output
-        osos = self.get_osos_from_json()
+        osos = self.get_osos_from_json_output()
         self.assertTrue(osos[exe][main_o]["loaded"])
         self.assertTrue(osos[exe][foo_o]["loaded"])
 
@@ -55,7 +55,6 @@ def test_shows_oso_not_loaded_json_output(self):
 
         # REMOVE the o files
         os.unlink(main_o)
-        os.unlink(foo_o)
 
         target = self.dbg.CreateTarget(exe)
         self.assertTrue(target, lldbtest.VALID_TARGET)
@@ -63,9 +62,18 @@ def test_shows_oso_not_loaded_json_output(self):
         self.runCmd("target modules dump separate-debug-info --json")
 
         # Check the output
-        osos = self.get_osos_from_json()
+        osos = self.get_osos_from_json_output()
         self.assertFalse(osos[exe][main_o]["loaded"])
-        self.assertFalse(osos[exe][foo_o]["loaded"])
+        self.assertIn("error", osos[exe][main_o])
+        self.assertTrue(osos[exe][foo_o]["loaded"])
+        self.assertNotIn("error", osos[exe][foo_o])
+
+        # Check with --errors-only
+        self.runCmd("target modules dump separate-debug-info --json --errors-only")
+        output = self.get_osos_from_json_output()
+        self.assertFalse(output[exe][main_o]["loaded"])
+        self.assertIn("error", output[exe][main_o])
+        self.assertNotIn(foo_o, output[exe])
 
     @skipIfRemote
     @skipUnlessDarwin



More information about the lldb-commits mailing list