Skip to content

Commit 8774de8

Browse files
authored
Reland"[lldb] Add count for errors of DWO files in statistics and combine DWO file count functions" (#156980)
This relands changes in #155023 for adding a count of dwo errors and combine all the dwo related stats into one struct. The previous PR was reverted in #156777 as the newly added unit test `test_dwo_id_mismatch_error_stats` sometimes fails due to inappropriate use of `glob.glob`. This change modified the tests created in the former PR to collect and modify the dwo files by there names instead of using index after `glob.glob`. This will avoid the possible failure in these tests if the order of dwo files changes. [Original PR: https://github.com/llvm/llvm-project/pull/155023](https://github.com/llvm/llvm-project/pull/155023) ## Testing Ran unit tests ``` $ ./bin/llvm-lit /data/users/ziyiww/llvm-project/lldb/test/API/commands/statistics/basic/TestStats.py ./bin/llvm-lit /data/users/ziyiww/llvm-project/lldb/test/API/commands/statistics/basic/TestStats.py -v -- Testing: 1 tests, 1 workers -- PASS: lldb-api :: commands/statistics/basic/TestStats.py (1 of 1) Testing Time: 388.52s Total Discovered Tests: 1 Passed: 1 (100.00%) $ bin/lldb-dotest -p TestStats.py /data/users/ziyiww/llvm-project/lldb/test/API/commands/statistics/basic/ ---------------------------------------------------------------------- Ran 27 tests in 386.302s OK (skipped=3) ```
1 parent 4e30d78 commit 8774de8

File tree

8 files changed

+168
-27
lines changed

8 files changed

+168
-27
lines changed

lldb/include/lldb/Symbol/SymbolFile.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,13 +488,16 @@ class SymbolFile : public PluginInterface {
488488
return false;
489489
};
490490

491-
/// Get number of loaded/parsed DWO files. This is emitted in "statistics
492-
/// dump"
491+
/// Retrieves statistics about DWO files associated with this symbol file.
492+
/// This function returns a DWOStats struct containing:
493+
/// - The number of successfully loaded/parsed DWO files.
494+
/// - The total number of DWO files encountered.
495+
/// - The number of DWO CUs that failed to load due to errors.
496+
/// If this symbol file does not support DWO files, all counts will be zero.
493497
///
494498
/// \returns
495-
/// A pair containing (loaded_dwo_count, total_dwo_count). If this
496-
/// symbol file doesn't support DWO files, both counts will be 0.
497-
virtual std::pair<uint32_t, uint32_t> GetDwoFileCounts() { return {0, 0}; }
499+
/// A DWOStats struct with loaded, total, and error counts for DWO files.
500+
virtual DWOStats GetDwoStats() { return {}; }
498501

499502
virtual lldb::TypeSP
500503
MakeType(lldb::user_id_t uid, ConstString name,

lldb/include/lldb/Target/Statistics.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,25 @@ struct StatsSuccessFail {
123123
uint32_t failures = 0;
124124
};
125125

126+
/// Holds statistics about DWO (Debug With Object) files.
127+
struct DWOStats {
128+
uint32_t loaded_dwo_file_count = 0;
129+
uint32_t dwo_file_count = 0;
130+
uint32_t dwo_error_count = 0;
131+
132+
DWOStats &operator+=(const DWOStats &rhs) {
133+
loaded_dwo_file_count += rhs.loaded_dwo_file_count;
134+
dwo_file_count += rhs.dwo_file_count;
135+
dwo_error_count += rhs.dwo_error_count;
136+
return *this;
137+
}
138+
139+
friend DWOStats operator+(DWOStats lhs, const DWOStats &rhs) {
140+
lhs += rhs;
141+
return lhs;
142+
}
143+
};
144+
126145
/// A class that represents statistics for a since lldb_private::Module.
127146
struct ModuleStats {
128147
llvm::json::Value ToJSON() const;
@@ -153,8 +172,7 @@ struct ModuleStats {
153172
bool symtab_stripped = false;
154173
bool debug_info_had_variable_errors = false;
155174
bool debug_info_had_incomplete_types = false;
156-
uint32_t dwo_file_count = 0;
157-
uint32_t loaded_dwo_file_count = 0;
175+
DWOStats dwo_stats;
158176
};
159177

160178
struct ConstStringStats {

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4623,9 +4623,8 @@ void SymbolFileDWARF::GetCompileOptions(
46234623
}
46244624
}
46254625

4626-
std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
4627-
uint32_t total_dwo_count = 0;
4628-
uint32_t loaded_dwo_count = 0;
4626+
DWOStats SymbolFileDWARF::GetDwoStats() {
4627+
DWOStats stats;
46294628

46304629
DWARFDebugInfo &info = DebugInfo();
46314630
const size_t num_cus = info.GetNumUnits();
@@ -4638,16 +4637,21 @@ std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
46384637
if (!dwarf_cu->GetDWOId().has_value())
46394638
continue;
46404639

4641-
total_dwo_count++;
4640+
stats.dwo_file_count++;
46424641

46434642
// If we have a DWO symbol file, that means we were able to successfully
46444643
// load it.
46454644
SymbolFile *dwo_symfile =
46464645
dwarf_cu->GetDwoSymbolFile(/*load_all_debug_info=*/false);
46474646
if (dwo_symfile) {
4648-
loaded_dwo_count++;
4647+
stats.loaded_dwo_file_count++;
46494648
}
4649+
4650+
// Check if this unit has a DWO load error, false by default.
4651+
const Status &dwo_error = dwarf_cu->GetDwoError();
4652+
if (dwo_error.Fail())
4653+
stats.dwo_error_count++;
46504654
}
46514655

4652-
return {loaded_dwo_count, total_dwo_count};
4656+
return stats;
46534657
}

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,11 @@ class SymbolFileDWARF : public SymbolFileCommon {
283283
bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
284284
bool load_all_debug_info = false) override;
285285

286-
// Gets a pair of loaded and total dwo file counts.
287-
// For split-dwarf files, this reports the counts for successfully loaded DWO
288-
// CUs and total DWO CUs. For non-split-dwarf files, this reports 0 for both.
289-
std::pair<uint32_t, uint32_t> GetDwoFileCounts() override;
286+
/// Gets statistics about dwo files associated with this symbol file.
287+
/// For split-dwarf files, this reports the counts for successfully loaded DWO
288+
/// CUs, total DWO CUs, and the number of DWO CUs with loading errors.
289+
/// For non-split-dwarf files, this reports 0 for all.
290+
DWOStats GetDwoStats() override;
290291

291292
DWARFContext &GetDWARFContext() { return m_context; }
292293

lldb/source/Target/Statistics.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ json::Value ModuleStats::ToJSON() const {
7373
debug_info_had_incomplete_types);
7474
module.try_emplace("symbolTableStripped", symtab_stripped);
7575
module.try_emplace("symbolTableSymbolCount", symtab_symbol_count);
76-
module.try_emplace("dwoFileCount", dwo_file_count);
77-
module.try_emplace("loadedDwoFileCount", loaded_dwo_file_count);
76+
module.try_emplace("dwoFileCount", dwo_stats.dwo_file_count);
77+
module.try_emplace("loadedDwoFileCount", dwo_stats.loaded_dwo_file_count);
78+
module.try_emplace("dwoErrorCount", dwo_stats.dwo_error_count);
7879

7980
if (!symbol_locator_time.map.empty()) {
8081
json::Object obj;
@@ -324,8 +325,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
324325
uint32_t num_modules_with_incomplete_types = 0;
325326
uint32_t num_stripped_modules = 0;
326327
uint32_t symtab_symbol_count = 0;
327-
uint32_t total_loaded_dwo_file_count = 0;
328-
uint32_t total_dwo_file_count = 0;
328+
DWOStats total_dwo_stats;
329329
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
330330
Module *module = target != nullptr
331331
? target->GetImages().GetModuleAtIndex(image_idx).get()
@@ -357,10 +357,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
357357
for (const auto &symbol_module : symbol_modules.Modules())
358358
module_stat.symfile_modules.push_back((intptr_t)symbol_module.get());
359359
}
360-
std::tie(module_stat.loaded_dwo_file_count, module_stat.dwo_file_count) =
361-
sym_file->GetDwoFileCounts();
362-
total_dwo_file_count += module_stat.dwo_file_count;
363-
total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
360+
DWOStats current_dwo_stats = sym_file->GetDwoStats();
361+
module_stat.dwo_stats += current_dwo_stats;
362+
total_dwo_stats += current_dwo_stats;
364363
module_stat.debug_info_index_loaded_from_cache =
365364
sym_file->GetDebugInfoIndexWasLoadedFromCache();
366365
if (module_stat.debug_info_index_loaded_from_cache)
@@ -435,8 +434,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
435434
{"totalDebugInfoEnabled", num_debug_info_enabled_modules},
436435
{"totalSymbolTableStripped", num_stripped_modules},
437436
{"totalSymbolTableSymbolCount", symtab_symbol_count},
438-
{"totalLoadedDwoFileCount", total_loaded_dwo_file_count},
439-
{"totalDwoFileCount", total_dwo_file_count},
437+
{"totalLoadedDwoFileCount", total_dwo_stats.loaded_dwo_file_count},
438+
{"totalDwoFileCount", total_dwo_stats.dwo_file_count},
439+
{"totalDwoErrorCount", total_dwo_stats.dwo_error_count},
440440
};
441441

442442
if (include_targets) {

lldb/test/API/commands/statistics/basic/TestStats.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import os
33
import re
4+
import shutil
45

56
import lldb
67
from lldbsuite.test.decorators import *
@@ -179,6 +180,7 @@ def test_default_no_run(self):
179180
"totalDebugInfoParseTime",
180181
"totalDwoFileCount",
181182
"totalLoadedDwoFileCount",
183+
"totalDwoErrorCount",
182184
]
183185
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
184186
if self.getPlatform() != "windows":
@@ -291,6 +293,7 @@ def test_default_with_run(self):
291293
"totalDebugInfoParseTime",
292294
"totalDwoFileCount",
293295
"totalLoadedDwoFileCount",
296+
"totalDwoErrorCount",
294297
]
295298
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
296299
stats = debug_stats["targets"][0]
@@ -331,6 +334,7 @@ def test_memory(self):
331334
"totalDebugInfoByteSize",
332335
"totalDwoFileCount",
333336
"totalLoadedDwoFileCount",
337+
"totalDwoErrorCount",
334338
]
335339
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
336340

@@ -385,6 +389,7 @@ def test_modules(self):
385389
"totalDebugInfoByteSize",
386390
"totalDwoFileCount",
387391
"totalLoadedDwoFileCount",
392+
"totalDwoErrorCount",
388393
]
389394
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
390395
stats = debug_stats["targets"][0]
@@ -407,6 +412,7 @@ def test_modules(self):
407412
"symbolTableSavedToCache",
408413
"dwoFileCount",
409414
"loadedDwoFileCount",
415+
"dwoErrorCount",
410416
"triple",
411417
"uuid",
412418
]
@@ -497,6 +503,7 @@ def test_breakpoints(self):
497503
"totalDebugInfoByteSize",
498504
"totalDwoFileCount",
499505
"totalLoadedDwoFileCount",
506+
"totalDwoErrorCount",
500507
]
501508
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
502509
target_stats = debug_stats["targets"][0]
@@ -655,6 +662,99 @@ def test_dwp_dwo_file_count(self):
655662
self.assertEqual(debug_stats["totalDwoFileCount"], 2)
656663
self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
657664

665+
@add_test_categories(["dwo"])
666+
def test_dwo_missing_error_stats(self):
667+
"""
668+
Test that DWO missing errors are reported correctly in statistics.
669+
This test:
670+
1) Builds a program with split DWARF (.dwo files)
671+
2) Delete one of the two .dwo files
672+
3) Verify that 1 DWO error is reported in statistics
673+
"""
674+
da = {
675+
"CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
676+
"EXE": self.getBuildArtifact("a.out"),
677+
}
678+
# -gsplit-dwarf creates separate .dwo files,
679+
# Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
680+
self.build(dictionary=da, debug_info="dwo")
681+
exe = self.getBuildArtifact("a.out")
682+
683+
expected_dwo_files = [
684+
self.getBuildArtifact("dwo_error_main.dwo"),
685+
self.getBuildArtifact("dwo_error_foo.dwo"),
686+
]
687+
688+
# Verify expected files exist
689+
for dwo_file in expected_dwo_files:
690+
self.assertTrue(
691+
os.path.exists(dwo_file),
692+
f"Expected .dwo file does not exist: {dwo_file}",
693+
)
694+
695+
# Remove one of .dwo files to trigger DWO load error
696+
dwo_main_file = self.getBuildArtifact("dwo_error_main.dwo")
697+
os.remove(dwo_main_file)
698+
699+
target = self.createTestTarget(file_path=exe)
700+
debug_stats = self.get_stats()
701+
702+
# Check DWO load error statistics are reported
703+
self.assertIn("totalDwoErrorCount", debug_stats)
704+
self.assertEqual(debug_stats["totalDwoErrorCount"], 1)
705+
706+
# Since there's only one module, module stats should have the same count as total count
707+
self.assertIn("dwoErrorCount", debug_stats["modules"][0])
708+
self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)
709+
710+
@add_test_categories(["dwo"])
711+
def test_dwo_id_mismatch_error_stats(self):
712+
"""
713+
Test that DWO ID mismatch errors are reported correctly in statistics.
714+
This test:
715+
1) Builds a program with split DWARF (.dwo files)
716+
2) Replace one of the .dwo files with a mismatched one to cause a DWO ID mismatch error
717+
3) Verifies that a DWO error is reported in statistics
718+
"""
719+
da = {
720+
"CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
721+
"EXE": self.getBuildArtifact("a.out"),
722+
}
723+
# -gsplit-dwarf creates separate .dwo files,
724+
# Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
725+
self.build(dictionary=da, debug_info="dwo")
726+
exe = self.getBuildArtifact("a.out")
727+
728+
expected_dwo_files = [
729+
self.getBuildArtifact("dwo_error_main.dwo"),
730+
self.getBuildArtifact("dwo_error_foo.dwo"),
731+
]
732+
733+
# Verify expected files exist
734+
for dwo_file in expected_dwo_files:
735+
self.assertTrue(
736+
os.path.exists(dwo_file),
737+
f"Expected .dwo file does not exist: {dwo_file}",
738+
)
739+
740+
# Replace one of the original .dwo file content with another one to trigger DWO ID mismatch error
741+
dwo_foo_file = self.getBuildArtifact("dwo_error_foo.dwo")
742+
dwo_main_file = self.getBuildArtifact("dwo_error_main.dwo")
743+
744+
shutil.copy(dwo_main_file, dwo_foo_file)
745+
746+
# Create a new target and get stats
747+
target = self.createTestTarget(file_path=exe)
748+
debug_stats = self.get_stats()
749+
750+
# Check that DWO load error statistics are reported
751+
self.assertIn("totalDwoErrorCount", debug_stats)
752+
self.assertEqual(debug_stats["totalDwoErrorCount"], 1)
753+
754+
# Since there's only one module, module stats should have the same count as total count
755+
self.assertIn("dwoErrorCount", debug_stats["modules"][0])
756+
self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)
757+
658758
@skipUnlessDarwin
659759
@no_debug_info_test
660760
def test_dsym_binary_has_symfile_in_stats(self):
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
struct foo {
2+
int x;
3+
bool y;
4+
};
5+
6+
void dwo_error_foo() {
7+
foo f;
8+
f.x = 1;
9+
f.y = true;
10+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
void dwo_error_foo();
2+
int main() {
3+
dwo_error_foo();
4+
return 0;
5+
}

0 commit comments

Comments
 (0)