Skip to content

[lldb][split-dwarf] Add --errors-only argument separate-debug-info list #71000

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 2, 2023

Conversation

zhyty
Copy link
Contributor

@zhyty zhyty commented Nov 1, 2023

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.

@zhyty zhyty requested a review from JDevlieghere as a code owner November 1, 2023 23:06
@zhyty zhyty requested review from clayborg and jeffreytan81 and removed request for JDevlieghere November 1, 2023 23:06
@llvmbot llvmbot added the lldb label Nov 1, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 1, 2023

@llvm/pr-subscribers-lldb

Author: Tom Yang (zhyty)

Changes

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 &lt;cmd-options&gt; [&lt;filename&gt; [&lt;filename&gt; [...]]]

Command Options Usage:
  target modules dump separate-debug-info [-ej] [&lt;filename&gt; [&lt;filename&gt; [...]]]

       -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.


Full diff: https://github.com/llvm/llvm-project/pull/71000.diff

9 Files Affected:

  • (modified) lldb/include/lldb/Symbol/SymbolFile.h (+5-1)
  • (modified) lldb/source/Commands/CommandObjectTarget.cpp (+14-6)
  • (modified) lldb/source/Commands/Options.td (+3-1)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp (+4-2)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h (+2-1)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp (+3-2)
  • (modified) lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h (+2-1)
  • (modified) lldb/test/API/commands/target/dump-separate-debug-info/dwo/TestDumpDwo.py (+13-7)
  • (modified) lldb/test/API/commands/target/dump-separate-debug-info/oso/TestDumpOso.py (+13-5)
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

@zhyty zhyty merged commit 9e0a5be into llvm:main Nov 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants