Skip to content

Commit b8d2944

Browse files
committed
Reland[lldb] Add count for errors of DWO files in statistics and combine DWO file count functions
1 parent d943efb commit b8d2944

File tree

8 files changed

+177
-27
lines changed

8 files changed

+177
-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: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,19 @@ 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) const {
133+
return DWOStats{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+
}
137+
};
138+
126139
/// A class that represents statistics for a since lldb_private::Module.
127140
struct ModuleStats {
128141
llvm::json::Value ToJSON() const;
@@ -153,8 +166,7 @@ struct ModuleStats {
153166
bool symtab_stripped = false;
154167
bool debug_info_had_variable_errors = false;
155168
bool debug_info_had_incomplete_types = false;
156-
uint32_t dwo_file_count = 0;
157-
uint32_t loaded_dwo_file_count = 0;
169+
DWOStats dwo_stats;
158170
};
159171

160172
struct ConstStringStats {

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4502,9 +4502,8 @@ void SymbolFileDWARF::GetCompileOptions(
45024502
}
45034503
}
45044504

4505-
std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
4506-
uint32_t total_dwo_count = 0;
4507-
uint32_t loaded_dwo_count = 0;
4505+
DWOStats SymbolFileDWARF::GetDwoStats() {
4506+
DWOStats stats;
45084507

45094508
DWARFDebugInfo &info = DebugInfo();
45104509
const size_t num_cus = info.GetNumUnits();
@@ -4517,16 +4516,21 @@ std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
45174516
if (!dwarf_cu->GetDWOId().has_value())
45184517
continue;
45194518

4520-
total_dwo_count++;
4519+
stats.dwo_file_count++;
45214520

45224521
// If we have a DWO symbol file, that means we were able to successfully
45234522
// load it.
45244523
SymbolFile *dwo_symfile =
45254524
dwarf_cu->GetDwoSymbolFile(/*load_all_debug_info=*/false);
45264525
if (dwo_symfile) {
4527-
loaded_dwo_count++;
4526+
stats.loaded_dwo_file_count++;
45284527
}
4528+
4529+
// Check if this unit has a DWO load error, false by default.
4530+
const Status &dwo_error = dwarf_cu->GetDwoError();
4531+
if (dwo_error.Fail())
4532+
stats.dwo_error_count++;
45294533
}
45304534

4531-
return {loaded_dwo_count, total_dwo_count};
4535+
return stats;
45324536
}

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 = module_stat.dwo_stats + current_dwo_stats;
362+
total_dwo_stats = 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: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import glob
12
import json
23
import os
34
import re
5+
import shutil
46

57
import lldb
68
from lldbsuite.test.decorators import *
@@ -179,6 +181,7 @@ def test_default_no_run(self):
179181
"totalDebugInfoParseTime",
180182
"totalDwoFileCount",
181183
"totalLoadedDwoFileCount",
184+
"totalDwoErrorCount",
182185
]
183186
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
184187
if self.getPlatform() != "windows":
@@ -291,6 +294,7 @@ def test_default_with_run(self):
291294
"totalDebugInfoParseTime",
292295
"totalDwoFileCount",
293296
"totalLoadedDwoFileCount",
297+
"totalDwoErrorCount",
294298
]
295299
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
296300
stats = debug_stats["targets"][0]
@@ -331,6 +335,7 @@ def test_memory(self):
331335
"totalDebugInfoByteSize",
332336
"totalDwoFileCount",
333337
"totalLoadedDwoFileCount",
338+
"totalDwoErrorCount",
334339
]
335340
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
336341

@@ -385,6 +390,7 @@ def test_modules(self):
385390
"totalDebugInfoByteSize",
386391
"totalDwoFileCount",
387392
"totalLoadedDwoFileCount",
393+
"totalDwoErrorCount",
388394
]
389395
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
390396
stats = debug_stats["targets"][0]
@@ -407,6 +413,7 @@ def test_modules(self):
407413
"symbolTableSavedToCache",
408414
"dwoFileCount",
409415
"loadedDwoFileCount",
416+
"dwoErrorCount",
410417
"triple",
411418
"uuid",
412419
]
@@ -497,6 +504,7 @@ def test_breakpoints(self):
497504
"totalDebugInfoByteSize",
498505
"totalDwoFileCount",
499506
"totalLoadedDwoFileCount",
507+
"totalDwoErrorCount",
500508
]
501509
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
502510
target_stats = debug_stats["targets"][0]
@@ -655,6 +663,113 @@ def test_dwp_dwo_file_count(self):
655663
self.assertEqual(debug_stats["totalDwoFileCount"], 2)
656664
self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)
657665

666+
@add_test_categories(["dwo"])
667+
def test_dwo_missing_error_stats(self):
668+
"""
669+
Test that DWO missing errors are reported correctly in statistics.
670+
This test:
671+
1) Builds a program with split DWARF (.dwo files)
672+
2) Delete all two .dwo files
673+
3) Verify that 2 DWO errors are reported in statistics
674+
"""
675+
da = {
676+
"CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
677+
"EXE": self.getBuildArtifact("a.out"),
678+
}
679+
# -gsplit-dwarf creates separate .dwo files,
680+
# Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
681+
self.build(dictionary=da, debug_info="dwo")
682+
self.addTearDownCleanup(dictionary=da)
683+
exe = self.getBuildArtifact("a.out")
684+
685+
# Remove the two .dwo files to trigger a DWO load error
686+
dwo_files = glob.glob(self.getBuildArtifact("*.dwo"))
687+
for dwo_file in dwo_files:
688+
os.rename(dwo_file, dwo_file + ".bak")
689+
690+
target = self.createTestTarget(file_path=exe)
691+
debug_stats = self.get_stats()
692+
693+
# Check DWO load error statistics are reported
694+
self.assertIn("totalDwoErrorCount", debug_stats)
695+
self.assertEqual(debug_stats["totalDwoErrorCount"], 2)
696+
697+
# Since there's only one module, module stats should have the same count as total count
698+
self.assertIn("dwoErrorCount", debug_stats["modules"][0])
699+
self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 2)
700+
701+
# Restore the original .dwo file
702+
for dwo_file in dwo_files:
703+
os.rename(dwo_file + ".bak", dwo_file)
704+
705+
@add_test_categories(["dwo"])
706+
def test_dwo_id_mismatch_error_stats(self):
707+
"""
708+
Test that DWO ID mismatch errors are reported correctly in statistics.
709+
This test:
710+
1) Builds a program with split DWARF (.dwo files)
711+
2) Change one of the source file content and rebuild
712+
3) Replace the new .dwo file with the original one to create a DWO ID mismatch
713+
4) Verifies that a DWO error is reported in statistics
714+
5) Restores the original source file
715+
"""
716+
da = {
717+
"CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
718+
"EXE": self.getBuildArtifact("a.out"),
719+
}
720+
# -gsplit-dwarf creates separate .dwo files,
721+
# Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
722+
self.build(dictionary=da, debug_info="dwo")
723+
self.addTearDownCleanup(dictionary=da)
724+
exe = self.getBuildArtifact("a.out")
725+
726+
# Find and make a backup of the original .dwo file
727+
dwo_files = glob.glob(self.getBuildArtifact("*.dwo"))
728+
729+
original_dwo_file = dwo_files[1]
730+
original_dwo_backup = original_dwo_file + ".bak"
731+
shutil.copy2(original_dwo_file, original_dwo_backup)
732+
733+
target = self.createTestTarget(file_path=exe)
734+
initial_stats = self.get_stats()
735+
self.assertIn("totalDwoErrorCount", initial_stats)
736+
self.assertEqual(initial_stats["totalDwoErrorCount"], 0)
737+
self.dbg.DeleteTarget(target)
738+
739+
# Get the original file size before modification
740+
source_file_path = self.getSourcePath("dwo_error_foo.cpp")
741+
original_size = os.path.getsize(source_file_path)
742+
743+
try:
744+
# Modify the source code and rebuild
745+
with open(source_file_path, "a") as f:
746+
f.write("\n void additional_foo(){}\n")
747+
748+
# Rebuild and replace the new .dwo file with the original one
749+
self.build(dictionary=da, debug_info="dwo")
750+
shutil.copy2(original_dwo_backup, original_dwo_file)
751+
752+
# Create a new target and run to a breakpoint to force DWO file loading
753+
target = self.createTestTarget(file_path=exe)
754+
debug_stats = self.get_stats()
755+
756+
# Check that DWO load error statistics are reported
757+
self.assertIn("totalDwoErrorCount", debug_stats)
758+
self.assertEqual(debug_stats["totalDwoErrorCount"], 1)
759+
760+
# Since there's only one module, module stats should have the same count as total count
761+
self.assertIn("dwoErrorCount", debug_stats["modules"][0])
762+
self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)
763+
764+
finally:
765+
# Remove the appended content
766+
with open(source_file_path, "a") as f:
767+
f.truncate(original_size)
768+
769+
# Restore the original .dwo file
770+
if os.path.exists(original_dwo_backup):
771+
os.unlink(original_dwo_backup)
772+
658773
@skipUnlessDarwin
659774
@no_debug_info_test
660775
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)