[Lldb-commits] [lldb] [lldb] Add count for number of DWO files loaded in statistics (PR #144424)

via lldb-commits lldb-commits at lists.llvm.org
Wed Jun 18 11:17:44 PDT 2025


https://github.com/qxy11 updated https://github.com/llvm/llvm-project/pull/144424

>From 57483ee0f44f8bbed325268e7d1d40b1a0403aa1 Mon Sep 17 00:00:00 2001
From: Janet Yang <qxy11 at meta.com>
Date: Mon, 16 Jun 2025 11:06:24 -0700
Subject: [PATCH 1/5] Add counter for number of DWO files loaded in statistics

---
 lldb/include/lldb/Symbol/SymbolFile.h         |  5 +++
 .../Python/lldbsuite/test/builders/builder.py | 25 ++++++++++-----
 .../Python/lldbsuite/test/make/Makefile.rules |  4 +++
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      |  1 +
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |  9 ++++++
 .../SymbolFile/DWARF/SymbolFileDWARFDwo.cpp   |  2 +-
 lldb/source/Target/Statistics.cpp             |  5 +++
 .../commands/statistics/basic/TestStats.py    | 31 +++++++++++++++++++
 .../API/commands/statistics/basic/baz.cpp     | 12 +++++++
 .../API/commands/statistics/basic/third.cpp   |  7 +++++
 10 files changed, 93 insertions(+), 8 deletions(-)
 create mode 100644 lldb/test/API/commands/statistics/basic/baz.cpp
 create mode 100644 lldb/test/API/commands/statistics/basic/third.cpp

diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index 75c7f230ddf3d..42fbc2508889a 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -425,6 +425,11 @@ class SymbolFile : public PluginInterface {
   /// Reset the statistics for the symbol file.
   virtual void ResetStatistics() {}
 
+  /// Get the number of loaded DWO files for this symbol file.
+  /// This is used for statistics gathering and will return 0 for most
+  /// symbol file implementations except DWARF symbol files.
+  virtual uint32_t GetLoadedDwoFileCount() const { return 0; }
+
   /// Get the additional modules that this symbol file uses to parse debug info.
   ///
   /// Some debug info is stored in stand alone object files that are represented
diff --git a/lldb/packages/Python/lldbsuite/test/builders/builder.py b/lldb/packages/Python/lldbsuite/test/builders/builder.py
index de05732469448..572abf590345c 100644
--- a/lldb/packages/Python/lldbsuite/test/builders/builder.py
+++ b/lldb/packages/Python/lldbsuite/test/builders/builder.py
@@ -247,13 +247,24 @@ def getLLDBObjRoot(self):
     def _getDebugInfoArgs(self, debug_info):
         if debug_info is None:
             return []
-        if debug_info == "dwarf":
-            return ["MAKE_DSYM=NO"]
-        if debug_info == "dwo":
-            return ["MAKE_DSYM=NO", "MAKE_DWO=YES"]
-        if debug_info == "gmodules":
-            return ["MAKE_DSYM=NO", "MAKE_GMODULES=YES"]
-        return None
+        
+        debug_options = debug_info if isinstance(debug_info, list) else [debug_info]
+        option_flags = {
+            "dwarf": {"MAKE_DSYM": "NO"},
+            "dwo": {"MAKE_DSYM": "NO", "MAKE_DWO": "YES"},
+            "gmodules": {"MAKE_DSYM": "NO", "MAKE_GMODULES": "YES"},
+            "debug_names": {"MAKE_DEBUG_NAMES": "YES"},
+        }
+        
+        # Collect all flags, with later options overriding earlier ones
+        flags = {}
+        
+        for option in debug_options:
+            if not option or option not in option_flags:
+                return None # Invalid options
+            flags.update(option_flags[option])
+        
+        return [f"{key}={value}" for key, value in flags.items()]
 
     def getBuildCommand(
         self,
diff --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
index 06959f226066a..58833e1b0cc78 100644
--- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
+++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules
@@ -276,6 +276,10 @@ ifeq "$(MAKE_DWO)" "YES"
 	CFLAGS += -gsplit-dwarf
 endif
 
+ifeq "$(MAKE_DEBUG_NAMES)" "YES"
+	CFLAGS += -gpubnames
+endif
+
 ifeq "$(USE_PRIVATE_MODULE_CACHE)" "YES"
 THE_CLANG_MODULE_CACHE_DIR := $(BUILDDIR)/private-module-cache
 else
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 71f204c03a42a..acd9d2230c201 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4358,6 +4358,7 @@ StatsDuration::Duration SymbolFileDWARF::GetDebugInfoIndexTime() {
 
 void SymbolFileDWARF::ResetStatistics() {
   m_parse_time.reset();
+  m_loaded_dwo_file_count = 0;
   if (m_index)
     return m_index->ResetStatistics();
 }
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index d2d30d7decb16..d695961bbfc1d 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -315,6 +315,11 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   void ResetStatistics() override;
 
+  /// Get the number of loaded DWO files for this symbol file
+  uint32_t GetLoadedDwoFileCount() const override {
+    return m_loaded_dwo_file_count;
+  }
+
   virtual lldb::offset_t
   GetVendorDWARFOpcodeSize(const DataExtractor &data,
                            const lldb::offset_t data_offset,
@@ -497,6 +502,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   void InitializeFirstCodeAddress();
 
+  void IncrementLoadedDwoFileCount() { m_loaded_dwo_file_count++; }
+
   void
   GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
 
@@ -550,6 +557,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// valid value that can be used in DIERef objects which will contain
   /// an index that identifies the .DWO or .o file.
   std::optional<uint64_t> m_file_index;
+  /// Count of loaded DWO files for this symbol file
+  uint32_t m_loaded_dwo_file_count = 0;
 };
 } // namespace dwarf
 } // namespace lldb_private::plugin
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
index c1829abe72933..1f0e847da2905 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
@@ -31,7 +31,7 @@ SymbolFileDWARFDwo::SymbolFileDWARFDwo(SymbolFileDWARF &base_symbol_file,
                                    /*update_module_section_list*/ false)),
       m_base_symbol_file(base_symbol_file) {
   SetFileIndex(id);
-
+  m_base_symbol_file.IncrementLoadedDwoFileCount();
   // Parsing of the dwarf unit index is not thread-safe, so we need to prime it
   // to enable subsequent concurrent lookups.
   m_context.GetAsLLVM().getCUIndex();
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index 6ec8f8963baf9..b30d7f755f2fc 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -322,6 +322,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
   uint32_t num_modules_with_incomplete_types = 0;
   uint32_t num_stripped_modules = 0;
   uint32_t symtab_symbol_count = 0;
+  uint32_t total_loaded_dwo_file_count = 0;
   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
     Module *module = target != nullptr
                          ? target->GetImages().GetModuleAtIndex(image_idx).get()
@@ -353,6 +354,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
         for (const auto &symbol_module : symbol_modules.Modules())
           module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
       }
+      // Add DWO file count from this symbol file (will be 0 for non-DWARF
+      // symbol files)
+      total_loaded_dwo_file_count += sym_file->GetLoadedDwoFileCount();
       module_stat.debug_info_index_loaded_from_cache =
           sym_file->GetDebugInfoIndexWasLoadedFromCache();
       if (module_stat.debug_info_index_loaded_from_cache)
@@ -427,6 +431,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
       {"totalDebugInfoEnabled", num_debug_info_enabled_modules},
       {"totalSymbolTableStripped", num_stripped_modules},
       {"totalSymbolTableSymbolCount", symtab_symbol_count},
+      {"totalLoadedDwoFileCount", total_loaded_dwo_file_count},
   };
 
   if (include_targets) {
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 83132b40d85db..de4dafaf0a77d 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -177,6 +177,7 @@ def test_default_no_run(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoParseTime",
+            "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
         if self.getPlatform() != "windows":
@@ -287,6 +288,7 @@ def test_default_with_run(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoParseTime",
+            "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
         stats = debug_stats["targets"][0]
@@ -325,6 +327,7 @@ def test_memory(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoByteSize",
+            "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
 
@@ -377,6 +380,7 @@ def test_modules(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoByteSize",
+            "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
         stats = debug_stats["targets"][0]
@@ -485,6 +489,7 @@ def test_breakpoints(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoByteSize",
+            "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
         target_stats = debug_stats["targets"][0]
@@ -512,6 +517,32 @@ def test_breakpoints(self):
             self.verify_keys(
                 breakpoint, 'target_stats["breakpoints"]', bp_keys_exist, None
             )
+    
+    def test_loaded_dwo_file_count(self):
+        """
+        Test "statistics dump" and the loaded dwo file count.
+        Builds a binary w/ separate .dwo files and debug_names, and then
+        verifies the loaded dwo file count is the expected count after running
+        various commands
+        """
+        da = {"CXX_SOURCES": "third.cpp baz.cpp", "EXE": self.getBuildArtifact("a.out")}
+        self.build(dictionary=da, debug_info=["dwo", "debug_names"])
+        self.addTearDownCleanup(dictionary=da)
+        exe = self.getBuildArtifact("a.out")
+        target = self.createTestTarget(file_path=exe)
+        debug_stats = self.get_stats()
+
+        self.assertIn("totalLoadedDwoFileCount", debug_stats)
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
+        
+        self.runCmd("b main")
+        debug_stats_after_main = self.get_stats()
+        self.assertEqual(debug_stats_after_main["totalLoadedDwoFileCount"], 1)
+        
+        self.runCmd("type lookup Baz")
+        debug_stats_after_type_lookup = self.get_stats()
+        self.assertEqual(debug_stats_after_type_lookup["totalLoadedDwoFileCount"], 2)
+        
 
     @skipUnlessDarwin
     @no_debug_info_test
diff --git a/lldb/test/API/commands/statistics/basic/baz.cpp b/lldb/test/API/commands/statistics/basic/baz.cpp
new file mode 100644
index 0000000000000..536758b17d839
--- /dev/null
+++ b/lldb/test/API/commands/statistics/basic/baz.cpp
@@ -0,0 +1,12 @@
+// Helper that the lldb command `statistics dump` works in split-dwarf mode.
+
+struct Baz {
+  int x;
+  bool y;
+};
+
+void baz() {
+  Baz b;
+  b.x = 1;
+  b.y = true;
+}
diff --git a/lldb/test/API/commands/statistics/basic/third.cpp b/lldb/test/API/commands/statistics/basic/third.cpp
new file mode 100644
index 0000000000000..3943b9c2faafe
--- /dev/null
+++ b/lldb/test/API/commands/statistics/basic/third.cpp
@@ -0,0 +1,7 @@
+// Test that the lldb command `statistics dump` works.
+
+void baz();
+int main(void) {
+  baz();
+  return 0;
+}

>From 3292b8965ab59bf4e42767d350868898a96cd66b Mon Sep 17 00:00:00 2001
From: Janet Yang <qxy11 at meta.com>
Date: Tue, 17 Jun 2025 08:09:19 -0700
Subject: [PATCH 2/5] Use GetSeparateDebugInfo and add totalLoadedDwoFileCount

Address comments to use `GetSeparateDebugInfo` instead of adding a new
Symbol File function. Also add a totalLoadedDwoFileCount to statistics
dump and add some unit tests for expected behavior in the DWP case  and
non-split-dwarf cases.
---
 lldb/include/lldb/Symbol/SymbolFile.h         | 11 ++-
 lldb/include/lldb/Symbol/SymbolFileOnDemand.h |  7 +-
 .../Python/lldbsuite/test/builders/builder.py |  1 +
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      |  6 +-
 .../SymbolFile/DWARF/SymbolFileDWARF.h        | 13 +--
 .../DWARF/SymbolFileDWARFDebugMap.cpp         |  3 +-
 .../DWARF/SymbolFileDWARFDebugMap.h           |  4 +-
 .../SymbolFile/DWARF/SymbolFileDWARFDwo.cpp   |  2 +-
 lldb/source/Target/Statistics.cpp             | 29 ++++++-
 .../commands/statistics/basic/TestStats.py    | 82 +++++++++++++++++--
 10 files changed, 121 insertions(+), 37 deletions(-)

diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index 42fbc2508889a..55af626a6ed96 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -425,11 +425,6 @@ class SymbolFile : public PluginInterface {
   /// Reset the statistics for the symbol file.
   virtual void ResetStatistics() {}
 
-  /// Get the number of loaded DWO files for this symbol file.
-  /// This is used for statistics gathering and will return 0 for most
-  /// symbol file implementations except DWARF symbol files.
-  virtual uint32_t GetLoadedDwoFileCount() const { return 0; }
-
   /// Get the additional modules that this symbol file uses to parse debug info.
   ///
   /// Some debug info is stored in stand alone object files that are represented
@@ -472,8 +467,12 @@ class SymbolFile : public PluginInterface {
   ///     If true, then only return separate debug info files that encountered
   ///     errors during loading. If false, then return all expected separate
   ///     debug info files, regardless of whether they were successfully loaded.
+  /// \param load_all_debug_info
+  ///   If true, force loading any symbol files if they are not yet loaded and
+  ///   add its info to the debug info files. Default to true.
   virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                                    bool errors_only) {
+                                    bool errors_only,
+                                    bool load_all_debug_info = true) {
     return false;
   };
 
diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
index ba4a7f09afeaa..137e29e1e7a8d 100644
--- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
+++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
@@ -223,9 +223,10 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile {
     return m_sym_file_impl->SetDebugInfoHadFrameVariableErrors();
   }
 
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                            bool errors_only) override {
-    return m_sym_file_impl->GetSeparateDebugInfo(d, errors_only);
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
+                            bool load_all_debug_info = true) override {
+    return m_sym_file_impl->GetSeparateDebugInfo(d, errors_only,
+                                                 load_all_debug_info);
   }
 
   lldb::TypeSP MakeType(lldb::user_id_t uid, ConstString name,
diff --git a/lldb/packages/Python/lldbsuite/test/builders/builder.py b/lldb/packages/Python/lldbsuite/test/builders/builder.py
index 572abf590345c..efb1ba568e3e6 100644
--- a/lldb/packages/Python/lldbsuite/test/builders/builder.py
+++ b/lldb/packages/Python/lldbsuite/test/builders/builder.py
@@ -254,6 +254,7 @@ def _getDebugInfoArgs(self, debug_info):
             "dwo": {"MAKE_DSYM": "NO", "MAKE_DWO": "YES"},
             "gmodules": {"MAKE_DSYM": "NO", "MAKE_GMODULES": "YES"},
             "debug_names": {"MAKE_DEBUG_NAMES": "YES"},
+            "dwp": {"MAKE_DSYM": "NO", "MAKE_DWP": "YES"},
         }
         
         # Collect all flags, with later options overriding earlier ones
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index acd9d2230c201..7aee91d541e37 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4139,7 +4139,8 @@ void SymbolFileDWARF::DumpClangAST(Stream &s, llvm::StringRef filter) {
 }
 
 bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                                           bool errors_only) {
+                                           bool errors_only,
+                                           bool load_all_debug_info) {
   StructuredData::Array separate_debug_info_files;
   DWARFDebugInfo &info = DebugInfo();
   const size_t num_cus = info.GetNumUnits();
@@ -4182,7 +4183,7 @@ bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
 
     // If we have a DWO symbol file, that means we were able to successfully
     // load it.
-    SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile();
+    SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile(load_all_debug_info);
     if (dwo_symfile) {
       dwo_data->AddStringItem(
           "resolved_dwo_path",
@@ -4358,7 +4359,6 @@ StatsDuration::Duration SymbolFileDWARF::GetDebugInfoIndexTime() {
 
 void SymbolFileDWARF::ResetStatistics() {
   m_parse_time.reset();
-  m_loaded_dwo_file_count = 0;
   if (m_index)
     return m_index->ResetStatistics();
 }
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index d695961bbfc1d..5f0275be91c34 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -279,8 +279,8 @@ class SymbolFileDWARF : public SymbolFileCommon {
   void DumpClangAST(Stream &s, llvm::StringRef filter) override;
 
   /// List separate dwo files.
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                            bool errors_only) override;
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
+                            bool load_all_debug_info = true) override;
 
   DWARFContext &GetDWARFContext() { return m_context; }
 
@@ -315,11 +315,6 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   void ResetStatistics() override;
 
-  /// Get the number of loaded DWO files for this symbol file
-  uint32_t GetLoadedDwoFileCount() const override {
-    return m_loaded_dwo_file_count;
-  }
-
   virtual lldb::offset_t
   GetVendorDWARFOpcodeSize(const DataExtractor &data,
                            const lldb::offset_t data_offset,
@@ -502,8 +497,6 @@ class SymbolFileDWARF : public SymbolFileCommon {
 
   void InitializeFirstCodeAddress();
 
-  void IncrementLoadedDwoFileCount() { m_loaded_dwo_file_count++; }
-
   void
   GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
 
@@ -557,8 +550,6 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// valid value that can be used in DIERef objects which will contain
   /// an index that identifies the .DWO or .o file.
   std::optional<uint64_t> m_file_index;
-  /// Count of loaded DWO files for this symbol file
-  uint32_t m_loaded_dwo_file_count = 0;
 };
 } // namespace dwarf
 } // namespace lldb_private::plugin
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index f3a940b2ee396..dd94f0b36f2b2 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1278,7 +1278,8 @@ void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s, llvm::StringRef filter) {
 }
 
 bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo(
-    lldb_private::StructuredData::Dictionary &d, bool errors_only) {
+    lldb_private::StructuredData::Dictionary &d, bool errors_only,
+    bool load_all_debug_info) {
   StructuredData::Array separate_debug_info_files;
   const uint32_t cu_count = GetNumCompileUnits();
   for (uint32_t cu_idx = 0; cu_idx < cu_count; ++cu_idx) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index 35cbdbbb1692f..64c0e415f8bce 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -132,8 +132,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   void DumpClangAST(Stream &s, llvm::StringRef filter) override;
 
   /// List separate oso files.
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                            bool errors_only) override;
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
+                            bool load_all_debug_info = true) override;
 
   // PluginInterface protocol
   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
index 1f0e847da2905..c1829abe72933 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDwo.cpp
@@ -31,7 +31,7 @@ SymbolFileDWARFDwo::SymbolFileDWARFDwo(SymbolFileDWARF &base_symbol_file,
                                    /*update_module_section_list*/ false)),
       m_base_symbol_file(base_symbol_file) {
   SetFileIndex(id);
-  m_base_symbol_file.IncrementLoadedDwoFileCount();
+
   // Parsing of the dwarf unit index is not thread-safe, so we need to prime it
   // to enable subsequent concurrent lookups.
   m_context.GetAsLLVM().getCUIndex();
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index b30d7f755f2fc..5e0680794dbb0 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -323,6 +323,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
   uint32_t num_stripped_modules = 0;
   uint32_t symtab_symbol_count = 0;
   uint32_t total_loaded_dwo_file_count = 0;
+  uint32_t total_dwo_file_count = 0;
   for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
     Module *module = target != nullptr
                          ? target->GetImages().GetModuleAtIndex(image_idx).get()
@@ -354,9 +355,30 @@ llvm::json::Value DebuggerStats::ReportStatistics(
         for (const auto &symbol_module : symbol_modules.Modules())
           module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
       }
-      // Add DWO file count from this symbol file (will be 0 for non-DWARF
-      // symbol files)
-      total_loaded_dwo_file_count += sym_file->GetLoadedDwoFileCount();
+      // Count DWO files from this symbol file using GetSeparateDebugInfo
+      StructuredData::Dictionary separate_debug_info;
+      if (sym_file->GetSeparateDebugInfo(separate_debug_info,
+                                         /*errors_only=*/false,
+                                         /*load_all_debug_info*/ false)) {
+        llvm::StringRef type;
+        if (separate_debug_info.GetValueForKeyAsString("type", type) &&
+            type == "dwo") {
+          StructuredData::Array *files;
+          if (separate_debug_info.GetValueForKeyAsArray(
+                  "separate-debug-info-files", files)) {
+            files->ForEach([&](StructuredData::Object *obj) {
+              if (auto dict = obj->GetAsDictionary()) {
+                total_dwo_file_count++;
+
+                bool loaded = false;
+                if (dict->GetValueForKeyAsBoolean("loaded", loaded) && loaded)
+                  total_loaded_dwo_file_count++;
+              }
+              return true;
+            });
+          }
+        }
+      }
       module_stat.debug_info_index_loaded_from_cache =
           sym_file->GetDebugInfoIndexWasLoadedFromCache();
       if (module_stat.debug_info_index_loaded_from_cache)
@@ -432,6 +454,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
       {"totalSymbolTableStripped", num_stripped_modules},
       {"totalSymbolTableSymbolCount", symtab_symbol_count},
       {"totalLoadedDwoFileCount", total_loaded_dwo_file_count},
+      {"totalDwoFileCount", total_dwo_file_count},
   };
 
   if (include_targets) {
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index de4dafaf0a77d..1c891a5f78728 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -177,6 +177,7 @@ def test_default_no_run(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoParseTime",
+            "totalDwoFileCount",
             "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -288,6 +289,7 @@ def test_default_with_run(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoParseTime",
+            "totalDwoFileCount",
             "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -327,6 +329,7 @@ def test_memory(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoByteSize",
+            "totalDwoFileCount",
             "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -380,6 +383,7 @@ def test_modules(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoByteSize",
+            "totalDwoFileCount",
             "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -489,6 +493,7 @@ def test_breakpoints(self):
             "totalDebugInfoIndexLoadedFromCache",
             "totalDebugInfoIndexSavedToCache",
             "totalDebugInfoByteSize",
+            "totalDwoFileCount",
             "totalLoadedDwoFileCount",
         ]
         self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
@@ -517,14 +522,35 @@ def test_breakpoints(self):
             self.verify_keys(
                 breakpoint, 'target_stats["breakpoints"]', bp_keys_exist, None
             )
-    
-    def test_loaded_dwo_file_count(self):
+    def test_non_split_dwarf_has_no_dwo_files(self):
         """
-        Test "statistics dump" and the loaded dwo file count.
+        Test "statistics dump" and the dwo file count.
+        Builds a binary without split-dwarf mode, and then
+        verifies the dwo file count is zero after running "statistics dump"
+        """
+        da = {"CXX_SOURCES": "third.cpp baz.cpp", "EXE": self.getBuildArtifact("a.out")}
+        self.build(dictionary=da, debug_info=["debug_names"])
+        self.addTearDownCleanup(dictionary=da)
+        exe = self.getBuildArtifact("a.out")
+        target = self.createTestTarget(file_path=exe)
+        debug_stats = self.get_stats()
+        self.assertIn("totalDwoFileCount", debug_stats)
+        self.assertIn("totalLoadedDwoFileCount", debug_stats)
+        
+        # Verify that the dwo file count is zero
+        self.assertEqual(debug_stats["totalDwoFileCount"], 0)
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
+
+    def test_split_dwarf_dwo_file_count(self):
+        """
+        Test "statistics dump" and the dwo file count.
         Builds a binary w/ separate .dwo files and debug_names, and then
         verifies the loaded dwo file count is the expected count after running
         various commands
         """
+        # Build with split DWARF: -gsplit-dwarf creates separate .dwo files,
+        # -gpubnames enables accelerator tables for faster symbol lookup
+        # Expected output: third.dwo (contains main) and baz.dwo (contains Baz struct/function)
         da = {"CXX_SOURCES": "third.cpp baz.cpp", "EXE": self.getBuildArtifact("a.out")}
         self.build(dictionary=da, debug_info=["dwo", "debug_names"])
         self.addTearDownCleanup(dictionary=da)
@@ -532,16 +558,58 @@ def test_loaded_dwo_file_count(self):
         target = self.createTestTarget(file_path=exe)
         debug_stats = self.get_stats()
 
+        # Initially: 2 DWO files available but none loaded yet
         self.assertIn("totalLoadedDwoFileCount", debug_stats)
+        self.assertIn("totalDwoFileCount", debug_stats)
         self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
         
+        # Setting breakpoint in main triggers loading of third.dwo (contains main function)
         self.runCmd("b main")
-        debug_stats_after_main = self.get_stats()
-        self.assertEqual(debug_stats_after_main["totalLoadedDwoFileCount"], 1)
+        debug_stats = self.get_stats()
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 1)
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
         
+        # Type lookup forces loading of baz.dwo (contains struct Baz definition)
         self.runCmd("type lookup Baz")
-        debug_stats_after_type_lookup = self.get_stats()
-        self.assertEqual(debug_stats_after_type_lookup["totalLoadedDwoFileCount"], 2)
+        debug_stats = self.get_stats()
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+    
+    def test_dwp_dwo_file_count(self):
+        """
+        Test "statistics dump" and the loaded dwo file count.
+        Builds a binary w/ a separate .dwp file and debug_names, and then
+        verifies the loaded dwo file count is the expected count after running
+        various commands.
+
+        We expect the DWO file counters to reflect the number of compile units 
+        loaded from the DWP file (each representing what was originally a separate DWO file)
+        """
+        da = {"CXX_SOURCES": "third.cpp baz.cpp", "EXE": self.getBuildArtifact("a.out")}
+        self.build(dictionary=da, debug_info=["dwp", "debug_names"])
+        self.addTearDownCleanup(dictionary=da)
+        exe = self.getBuildArtifact("a.out")
+        target = self.createTestTarget(file_path=exe)
+        debug_stats = self.get_stats()
+
+        # Initially: 2 DWO files available but none loaded yet
+        self.assertIn("totalLoadedDwoFileCount", debug_stats)
+        self.assertIn("totalDwoFileCount", debug_stats)
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+        
+        # Setting breakpoint in main triggers parsing of the CU within a.dwp corresponding to third.dwo (contains main function)
+        self.runCmd("b main")
+        debug_stats = self.get_stats()
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 1)
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+        
+        # Type lookup forces parsing of the CU within a.dwp corresponding to baz.dwo (contains struct Baz definition)
+        self.runCmd("type lookup Baz")
+        debug_stats = self.get_stats()
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
         
 
     @skipUnlessDarwin

>From a90294dd182c9a118b86684315b81e66be0f0bcb Mon Sep 17 00:00:00 2001
From: Janet Yang <qxy11 at meta.com>
Date: Tue, 17 Jun 2025 12:46:52 -0700
Subject: [PATCH 3/5] Put file counting logic in UpdateDwoFileCounts and add
 test_debug_names_eager_loads_dwo_files

Adds an extra unit test and refactors some logic into a helper function.
Also added additional comments in code and tests to clarify purpose of
debug_names usage and how it prevents the eager loading of DWO files
---
 lldb/source/Target/Statistics.cpp             | 57 +++++++++++--------
 .../commands/statistics/basic/TestStats.py    | 30 +++++++++-
 2 files changed, 60 insertions(+), 27 deletions(-)

diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index 5e0680794dbb0..e4975c6cedb6e 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -33,6 +33,37 @@ static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
     obj.try_emplace(key, llvm::json::fixUTF8(str));
 }
 
+static void UpdateDwoFileCounts(SymbolFile *sym_file,
+                                uint32_t &total_dwo_file_count,
+                                uint32_t &total_loaded_dwo_file_count) {
+  // Count DWO files from this symbol file using GetSeparateDebugInfo
+  // For DWP files, this increments counts for both total and successfully
+  // loaded DWO CUs. For non split-dwarf files, these counts should not change
+  StructuredData::Dictionary separate_debug_info;
+  if (sym_file->GetSeparateDebugInfo(separate_debug_info,
+                                     /*errors_only=*/false,
+                                     /*load_all_debug_info*/ false)) {
+    llvm::StringRef type;
+    if (separate_debug_info.GetValueForKeyAsString("type", type) &&
+        type == "dwo") {
+      StructuredData::Array *files;
+      if (separate_debug_info.GetValueForKeyAsArray("separate-debug-info-files",
+                                                    files)) {
+        files->ForEach([&](StructuredData::Object *obj) {
+          if (auto dict = obj->GetAsDictionary()) {
+            total_dwo_file_count++;
+
+            bool loaded = false;
+            if (dict->GetValueForKeyAsBoolean("loaded", loaded) && loaded)
+              total_loaded_dwo_file_count++;
+          }
+          return true;
+        });
+      }
+    }
+  }
+}
+
 json::Value StatsSuccessFail::ToJSON() const {
   return json::Object{{"successes", successes}, {"failures", failures}};
 }
@@ -355,30 +386,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(
         for (const auto &symbol_module : symbol_modules.Modules())
           module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
       }
-      // Count DWO files from this symbol file using GetSeparateDebugInfo
-      StructuredData::Dictionary separate_debug_info;
-      if (sym_file->GetSeparateDebugInfo(separate_debug_info,
-                                         /*errors_only=*/false,
-                                         /*load_all_debug_info*/ false)) {
-        llvm::StringRef type;
-        if (separate_debug_info.GetValueForKeyAsString("type", type) &&
-            type == "dwo") {
-          StructuredData::Array *files;
-          if (separate_debug_info.GetValueForKeyAsArray(
-                  "separate-debug-info-files", files)) {
-            files->ForEach([&](StructuredData::Object *obj) {
-              if (auto dict = obj->GetAsDictionary()) {
-                total_dwo_file_count++;
-
-                bool loaded = false;
-                if (dict->GetValueForKeyAsBoolean("loaded", loaded) && loaded)
-                  total_loaded_dwo_file_count++;
-              }
-              return true;
-            });
-          }
-        }
-      }
+      UpdateDwoFileCounts(sym_file, total_dwo_file_count,
+                          total_loaded_dwo_file_count);
       module_stat.debug_info_index_loaded_from_cache =
           sym_file->GetDebugInfoIndexWasLoadedFromCache();
       if (module_stat.debug_info_index_loaded_from_cache)
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 1c891a5f78728..685d7fe9ab711 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -540,6 +540,29 @@ def test_non_split_dwarf_has_no_dwo_files(self):
         # Verify that the dwo file count is zero
         self.assertEqual(debug_stats["totalDwoFileCount"], 0)
         self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
+    
+    def test_debug_names_eager_loads_dwo_files(self):
+        """
+        Test the eager loading behavior of DWO files when debug_names is absent by
+        building a split-dwarf binary without debug_names and then running "statistics dump".
+        DWO file loading behavior:
+        - With debug_names: DebugNamesDWARFIndex allows for lazy loading.
+          DWO files are loaded on-demand when symbols are actually looked up
+        - Without debug_names: ManualDWARFIndex uses eager loading.
+          All DWO files are loaded upfront during the first symbol lookup to build a manual index. 
+        """
+        da = {"CXX_SOURCES": "third.cpp baz.cpp", "EXE": self.getBuildArtifact("a.out")}
+        self.build(dictionary=da, debug_info=["dwo"])
+        self.addTearDownCleanup(dictionary=da)
+        exe = self.getBuildArtifact("a.out")
+        target = self.createTestTarget(file_path=exe)
+        debug_stats = self.get_stats()
+        self.assertIn("totalDwoFileCount", debug_stats)
+        self.assertIn("totalLoadedDwoFileCount", debug_stats)
+        
+        # Verify that all DWO files are loaded
+        self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+        self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
 
     def test_split_dwarf_dwo_file_count(self):
         """
@@ -548,10 +571,11 @@ def test_split_dwarf_dwo_file_count(self):
         verifies the loaded dwo file count is the expected count after running
         various commands
         """
-        # Build with split DWARF: -gsplit-dwarf creates separate .dwo files,
-        # -gpubnames enables accelerator tables for faster symbol lookup
-        # Expected output: third.dwo (contains main) and baz.dwo (contains Baz struct/function)
         da = {"CXX_SOURCES": "third.cpp baz.cpp", "EXE": self.getBuildArtifact("a.out")}
+        # -gsplit-dwarf creates separate .dwo files,
+        # -gpubnames enables the debug_names accelerator tables for faster symbol lookup
+        #  and lazy loading of DWO files
+        # Expected output: third.dwo (contains main) and baz.dwo (contains Baz struct/function)
         self.build(dictionary=da, debug_info=["dwo", "debug_names"])
         self.addTearDownCleanup(dictionary=da)
         exe = self.getBuildArtifact("a.out")

>From 9b966482264b1c0188d474b013d1db7236da1448 Mon Sep 17 00:00:00 2001
From: Janet Yang <qxy11 at meta.com>
Date: Tue, 17 Jun 2025 17:23:19 -0700
Subject: [PATCH 4/5] Add dwo_file_count and loaded_dwo_file_count to
 ModuleStats

---
 lldb/include/lldb/Target/Statistics.h         |  2 ++
 lldb/source/Target/Statistics.cpp             | 12 ++++++----
 .../commands/statistics/basic/TestStats.py    | 23 +++++++++++++++----
 3 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h
index 2d0d25cd3c753..42f03798c219e 100644
--- a/lldb/include/lldb/Target/Statistics.h
+++ b/lldb/include/lldb/Target/Statistics.h
@@ -153,6 +153,8 @@ struct ModuleStats {
   bool symtab_stripped = false;
   bool debug_info_had_variable_errors = false;
   bool debug_info_had_incomplete_types = false;
+  uint32_t dwo_file_count = 0;
+  uint32_t loaded_dwo_file_count = 0;
 };
 
 struct ConstStringStats {
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index e4975c6cedb6e..db14d7e706877 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -42,7 +42,7 @@ static void UpdateDwoFileCounts(SymbolFile *sym_file,
   StructuredData::Dictionary separate_debug_info;
   if (sym_file->GetSeparateDebugInfo(separate_debug_info,
                                      /*errors_only=*/false,
-                                     /*load_all_debug_info*/ false)) {
+                                     /*load_all_debug_info=*/false)) {
     llvm::StringRef type;
     if (separate_debug_info.GetValueForKeyAsString("type", type) &&
         type == "dwo") {
@@ -104,6 +104,8 @@ json::Value ModuleStats::ToJSON() const {
                      debug_info_had_incomplete_types);
   module.try_emplace("symbolTableStripped", symtab_stripped);
   module.try_emplace("symbolTableSymbolCount", symtab_symbol_count);
+  module.try_emplace("dwoFileCount", dwo_file_count);
+  module.try_emplace("loadedDwoFileCount", loaded_dwo_file_count);
 
   if (!symbol_locator_time.map.empty()) {
     json::Object obj;
@@ -117,7 +119,7 @@ json::Value ModuleStats::ToJSON() const {
 
   if (!symfile_modules.empty()) {
     json::Array symfile_ids;
-    for (const auto symfile_id: symfile_modules)
+    for (const auto symfile_id : symfile_modules)
       symfile_ids.emplace_back(symfile_id);
     module.try_emplace("symbolFileModuleIdentifiers", std::move(symfile_ids));
   }
@@ -386,8 +388,10 @@ llvm::json::Value DebuggerStats::ReportStatistics(
         for (const auto &symbol_module : symbol_modules.Modules())
           module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
       }
-      UpdateDwoFileCounts(sym_file, total_dwo_file_count,
-                          total_loaded_dwo_file_count);
+      UpdateDwoFileCounts(sym_file, module_stat.dwo_file_count,
+                          module_stat.loaded_dwo_file_count);
+      total_dwo_file_count += module_stat.dwo_file_count;
+      total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
       module_stat.debug_info_index_loaded_from_cache =
           sym_file->GetDebugInfoIndexWasLoadedFromCache();
       if (module_stat.debug_info_index_loaded_from_cache)
diff --git a/lldb/test/API/commands/statistics/basic/TestStats.py b/lldb/test/API/commands/statistics/basic/TestStats.py
index 685d7fe9ab711..5281bde4c6479 100644
--- a/lldb/test/API/commands/statistics/basic/TestStats.py
+++ b/lldb/test/API/commands/statistics/basic/TestStats.py
@@ -405,6 +405,8 @@ def test_modules(self):
             "symbolTableLoadedFromCache",
             "symbolTableParseTime",
             "symbolTableSavedToCache",
+            "dwoFileCount",
+            "loadedDwoFileCount",
             "triple",
             "uuid",
         ]
@@ -541,7 +543,7 @@ def test_non_split_dwarf_has_no_dwo_files(self):
         self.assertEqual(debug_stats["totalDwoFileCount"], 0)
         self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
     
-    def test_debug_names_eager_loads_dwo_files(self):
+    def test_no_debug_names_eager_loads_dwo_files(self):
         """
         Test the eager loading behavior of DWO files when debug_names is absent by
         building a split-dwarf binary without debug_names and then running "statistics dump".
@@ -582,23 +584,36 @@ def test_split_dwarf_dwo_file_count(self):
         target = self.createTestTarget(file_path=exe)
         debug_stats = self.get_stats()
 
-        # Initially: 2 DWO files available but none loaded yet
+        # 1) 2 DWO files available but none loaded yet
+        self.assertEqual(len(debug_stats["modules"]), 1)
         self.assertIn("totalLoadedDwoFileCount", debug_stats)
         self.assertIn("totalDwoFileCount", debug_stats)
         self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 0)
         self.assertEqual(debug_stats["totalDwoFileCount"], 2)
         
-        # Setting breakpoint in main triggers loading of third.dwo (contains main function)
+        # Since there's only one module, module stats should have the same counts as total counts
+        self.assertIn("dwoFileCount", debug_stats["modules"][0])
+        self.assertIn("loadedDwoFileCount", debug_stats["modules"][0])
+        self.assertEqual(debug_stats["modules"][0]["loadedDwoFileCount"], 0)
+        self.assertEqual(debug_stats["modules"][0]["dwoFileCount"], 2)
+        
+        # 2) Setting breakpoint in main triggers loading of third.dwo (contains main function)
         self.runCmd("b main")
         debug_stats = self.get_stats()
         self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 1)
         self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+
+        self.assertEqual(debug_stats["modules"][0]["loadedDwoFileCount"], 1)
+        self.assertEqual(debug_stats["modules"][0]["dwoFileCount"], 2)
         
-        # Type lookup forces loading of baz.dwo (contains struct Baz definition)
+        # 3) Type lookup forces loading of baz.dwo (contains struct Baz definition)
         self.runCmd("type lookup Baz")
         debug_stats = self.get_stats()
         self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
         self.assertEqual(debug_stats["totalDwoFileCount"], 2)
+
+        self.assertEqual(debug_stats["modules"][0]["loadedDwoFileCount"], 2)
+        self.assertEqual(debug_stats["modules"][0]["dwoFileCount"], 2)
     
     def test_dwp_dwo_file_count(self):
         """

>From d3e89b75a3a3be0d48482b8394224143f69c6aee Mon Sep 17 00:00:00 2001
From: Janet Yang <qxy11 at meta.com>
Date: Wed, 18 Jun 2025 11:16:42 -0700
Subject: [PATCH 5/5] Add a separate GetDwoFileCounts instead of using
 GetSeparateDebugInfo

---
 lldb/include/lldb/Symbol/SymbolFile.h         | 14 +++++---
 lldb/include/lldb/Symbol/SymbolFileOnDemand.h |  7 ++--
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 34 ++++++++++++++++--
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |  9 +++--
 .../DWARF/SymbolFileDWARFDebugMap.cpp         |  3 +-
 .../DWARF/SymbolFileDWARFDebugMap.h           |  4 +--
 lldb/source/Target/Statistics.cpp             | 35 ++-----------------
 7 files changed, 55 insertions(+), 51 deletions(-)

diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index 55af626a6ed96..b0b608d0a5e79 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -467,15 +467,19 @@ class SymbolFile : public PluginInterface {
   ///     If true, then only return separate debug info files that encountered
   ///     errors during loading. If false, then return all expected separate
   ///     debug info files, regardless of whether they were successfully loaded.
-  /// \param load_all_debug_info
-  ///   If true, force loading any symbol files if they are not yet loaded and
-  ///   add its info to the debug info files. Default to true.
   virtual bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                                    bool errors_only,
-                                    bool load_all_debug_info = true) {
+                                    bool errors_only) {
     return false;
   };
 
+  /// Get number of loaded/parsed DWO files. This is emitted in "statistics
+  /// dump"
+  ///
+  /// \returns
+  ///     A pair containing (loaded_dwo_count, total_dwo_count). If this
+  ///     symbol file doesn't support DWO files, both counts will be 0.
+  virtual std::pair<uint32_t, uint32_t> GetDwoFileCounts() { return {0, 0}; }
+
   virtual lldb::TypeSP
   MakeType(lldb::user_id_t uid, ConstString name,
            std::optional<uint64_t> byte_size, SymbolContextScope *context,
diff --git a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
index 137e29e1e7a8d..ba4a7f09afeaa 100644
--- a/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
+++ b/lldb/include/lldb/Symbol/SymbolFileOnDemand.h
@@ -223,10 +223,9 @@ class SymbolFileOnDemand : public lldb_private::SymbolFile {
     return m_sym_file_impl->SetDebugInfoHadFrameVariableErrors();
   }
 
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
-                            bool load_all_debug_info = true) override {
-    return m_sym_file_impl->GetSeparateDebugInfo(d, errors_only,
-                                                 load_all_debug_info);
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                            bool errors_only) override {
+    return m_sym_file_impl->GetSeparateDebugInfo(d, errors_only);
   }
 
   lldb::TypeSP MakeType(lldb::user_id_t uid, ConstString name,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 7aee91d541e37..c83779c40a05b 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -4139,8 +4139,7 @@ void SymbolFileDWARF::DumpClangAST(Stream &s, llvm::StringRef filter) {
 }
 
 bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
-                                           bool errors_only,
-                                           bool load_all_debug_info) {
+                                           bool errors_only) {
   StructuredData::Array separate_debug_info_files;
   DWARFDebugInfo &info = DebugInfo();
   const size_t num_cus = info.GetNumUnits();
@@ -4183,7 +4182,7 @@ bool SymbolFileDWARF::GetSeparateDebugInfo(StructuredData::Dictionary &d,
 
     // If we have a DWO symbol file, that means we were able to successfully
     // load it.
-    SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile(load_all_debug_info);
+    SymbolFile *dwo_symfile = dwarf_cu->GetDwoSymbolFile();
     if (dwo_symfile) {
       dwo_data->AddStringItem(
           "resolved_dwo_path",
@@ -4421,3 +4420,32 @@ void SymbolFileDWARF::GetCompileOptions(
     args.insert({comp_unit, Args(flags)});
   }
 }
+
+std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
+  uint32_t total_dwo_count = 0;
+  uint32_t loaded_dwo_count = 0;
+
+  DWARFDebugInfo &info = DebugInfo();
+  const size_t num_cus = info.GetNumUnits();
+  for (size_t cu_idx = 0; cu_idx < num_cus; cu_idx++) {
+    DWARFUnit *dwarf_cu = info.GetUnitAtIndex(cu_idx);
+    if (dwarf_cu == nullptr)
+      continue;
+
+    // Check if this is a DWO unit by checking if it has a DWO ID.
+    if (!dwarf_cu->GetDWOId().has_value())
+      continue;
+
+    total_dwo_count++;
+
+    // If we have a DWO symbol file, that means we were able to successfully
+    // load it.
+    SymbolFile *dwo_symfile =
+        dwarf_cu->GetDwoSymbolFile(/*load_all_debug_info=*/false);
+    if (dwo_symfile) {
+      loaded_dwo_count++;
+    }
+  }
+
+  return {loaded_dwo_count, total_dwo_count};
+}
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 5f0275be91c34..942e0b5990fa4 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -279,8 +279,13 @@ class SymbolFileDWARF : public SymbolFileCommon {
   void DumpClangAST(Stream &s, llvm::StringRef filter) override;
 
   /// List separate dwo files.
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
-                            bool load_all_debug_info = true) override;
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                            bool errors_only) override;
+
+  // Gets a pair of loaded and total dwo file counts.
+  // For DWP files, this reports the counts for successfully loaded DWO CUs
+  // and total DWO CUs. For non-split-dwarf files, this reports 0 for both.
+  std::pair<uint32_t, uint32_t> GetDwoFileCounts() 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 dd94f0b36f2b2..f3a940b2ee396 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1278,8 +1278,7 @@ void SymbolFileDWARFDebugMap::DumpClangAST(Stream &s, llvm::StringRef filter) {
 }
 
 bool SymbolFileDWARFDebugMap::GetSeparateDebugInfo(
-    lldb_private::StructuredData::Dictionary &d, bool errors_only,
-    bool load_all_debug_info) {
+    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) {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index 64c0e415f8bce..35cbdbbb1692f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -132,8 +132,8 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   void DumpClangAST(Stream &s, llvm::StringRef filter) override;
 
   /// List separate oso files.
-  bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
-                            bool load_all_debug_info = true) override;
+  bool GetSeparateDebugInfo(StructuredData::Dictionary &d,
+                            bool errors_only) override;
 
   // PluginInterface protocol
   llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
diff --git a/lldb/source/Target/Statistics.cpp b/lldb/source/Target/Statistics.cpp
index db14d7e706877..909f335687b21 100644
--- a/lldb/source/Target/Statistics.cpp
+++ b/lldb/source/Target/Statistics.cpp
@@ -33,37 +33,6 @@ static void EmplaceSafeString(llvm::json::Object &obj, llvm::StringRef key,
     obj.try_emplace(key, llvm::json::fixUTF8(str));
 }
 
-static void UpdateDwoFileCounts(SymbolFile *sym_file,
-                                uint32_t &total_dwo_file_count,
-                                uint32_t &total_loaded_dwo_file_count) {
-  // Count DWO files from this symbol file using GetSeparateDebugInfo
-  // For DWP files, this increments counts for both total and successfully
-  // loaded DWO CUs. For non split-dwarf files, these counts should not change
-  StructuredData::Dictionary separate_debug_info;
-  if (sym_file->GetSeparateDebugInfo(separate_debug_info,
-                                     /*errors_only=*/false,
-                                     /*load_all_debug_info=*/false)) {
-    llvm::StringRef type;
-    if (separate_debug_info.GetValueForKeyAsString("type", type) &&
-        type == "dwo") {
-      StructuredData::Array *files;
-      if (separate_debug_info.GetValueForKeyAsArray("separate-debug-info-files",
-                                                    files)) {
-        files->ForEach([&](StructuredData::Object *obj) {
-          if (auto dict = obj->GetAsDictionary()) {
-            total_dwo_file_count++;
-
-            bool loaded = false;
-            if (dict->GetValueForKeyAsBoolean("loaded", loaded) && loaded)
-              total_loaded_dwo_file_count++;
-          }
-          return true;
-        });
-      }
-    }
-  }
-}
-
 json::Value StatsSuccessFail::ToJSON() const {
   return json::Object{{"successes", successes}, {"failures", failures}};
 }
@@ -388,8 +357,8 @@ llvm::json::Value DebuggerStats::ReportStatistics(
         for (const auto &symbol_module : symbol_modules.Modules())
           module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
       }
-      UpdateDwoFileCounts(sym_file, module_stat.dwo_file_count,
-                          module_stat.loaded_dwo_file_count);
+      std::tie(module_stat.loaded_dwo_file_count, module_stat.dwo_file_count) =
+          sym_file->GetDwoFileCounts();
       total_dwo_file_count += module_stat.dwo_file_count;
       total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
       module_stat.debug_info_index_loaded_from_cache =



More information about the lldb-commits mailing list