Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions lldb/include/lldb/Symbol/SymbolFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,13 +488,16 @@ class SymbolFile : public PluginInterface {
return false;
};

/// Get number of loaded/parsed DWO files. This is emitted in "statistics
/// dump"
/// Retrieves statistics about DWO files associated with this symbol file.
/// This function returns a DWOStats struct containing:
/// - The number of successfully loaded/parsed DWO files.
/// - The total number of DWO files encountered.
/// - The number of DWO CUs that failed to load due to errors.
/// If this symbol file does not support DWO files, all counts will be zero.
///
/// \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}; }
/// A DWOStats struct with loaded, total, and error counts for DWO files.
virtual DWOStats GetDwoStats() { return {}; }

virtual lldb::TypeSP
MakeType(lldb::user_id_t uid, ConstString name,
Expand Down
22 changes: 20 additions & 2 deletions lldb/include/lldb/Target/Statistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,25 @@ struct StatsSuccessFail {
uint32_t failures = 0;
};

/// Holds statistics about DWO (Debug With Object) files.
struct DWOStats {
uint32_t loaded_dwo_file_count = 0;
uint32_t dwo_file_count = 0;
uint32_t dwo_error_count = 0;

DWOStats &operator+=(const DWOStats &rhs) {
loaded_dwo_file_count += rhs.loaded_dwo_file_count;
dwo_file_count += rhs.dwo_file_count;
dwo_error_count += rhs.dwo_error_count;
return *this;
}

friend DWOStats operator+(DWOStats lhs, const DWOStats &rhs) {
lhs += rhs;
return lhs;
}
};

/// A class that represents statistics for a since lldb_private::Module.
struct ModuleStats {
llvm::json::Value ToJSON() const;
Expand Down Expand Up @@ -153,8 +172,7 @@ 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;
DWOStats dwo_stats;
};

struct ConstStringStats {
Expand Down
16 changes: 10 additions & 6 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4502,9 +4502,8 @@ void SymbolFileDWARF::GetCompileOptions(
}
}

std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {
uint32_t total_dwo_count = 0;
uint32_t loaded_dwo_count = 0;
DWOStats SymbolFileDWARF::GetDwoStats() {
DWOStats stats;

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

total_dwo_count++;
stats.dwo_file_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++;
stats.loaded_dwo_file_count++;
}

// Check if this unit has a DWO load error, false by default.
const Status &dwo_error = dwarf_cu->GetDwoError();
if (dwo_error.Fail())
stats.dwo_error_count++;
}

return {loaded_dwo_count, total_dwo_count};
return stats;
}
9 changes: 5 additions & 4 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,11 @@ class SymbolFileDWARF : public SymbolFileCommon {
bool GetSeparateDebugInfo(StructuredData::Dictionary &d, bool errors_only,
bool load_all_debug_info = false) override;

// Gets a pair of loaded and total dwo file counts.
// For split-dwarf 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;
/// Gets statistics about dwo files associated with this symbol file.
/// For split-dwarf files, this reports the counts for successfully loaded DWO
/// CUs, total DWO CUs, and the number of DWO CUs with loading errors.
/// For non-split-dwarf files, this reports 0 for all.
DWOStats GetDwoStats() override;

DWARFContext &GetDWARFContext() { return m_context; }

Expand Down
20 changes: 10 additions & 10 deletions lldb/source/Target/Statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ 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);
module.try_emplace("dwoFileCount", dwo_stats.dwo_file_count);
module.try_emplace("loadedDwoFileCount", dwo_stats.loaded_dwo_file_count);
module.try_emplace("dwoErrorCount", dwo_stats.dwo_error_count);

if (!symbol_locator_time.map.empty()) {
json::Object obj;
Expand Down Expand Up @@ -324,8 +325,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;
uint32_t total_dwo_file_count = 0;
DWOStats total_dwo_stats;
for (size_t image_idx = 0; image_idx < num_modules; ++image_idx) {
Module *module = target != nullptr
? target->GetImages().GetModuleAtIndex(image_idx).get()
Expand Down Expand Up @@ -357,10 +357,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());
}
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;
DWOStats current_dwo_stats = sym_file->GetDwoStats();
module_stat.dwo_stats += current_dwo_stats;
total_dwo_stats += current_dwo_stats;
module_stat.debug_info_index_loaded_from_cache =
sym_file->GetDebugInfoIndexWasLoadedFromCache();
if (module_stat.debug_info_index_loaded_from_cache)
Expand Down Expand Up @@ -435,8 +434,9 @@ 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},
{"totalDwoFileCount", total_dwo_file_count},
{"totalLoadedDwoFileCount", total_dwo_stats.loaded_dwo_file_count},
{"totalDwoFileCount", total_dwo_stats.dwo_file_count},
{"totalDwoErrorCount", total_dwo_stats.dwo_error_count},
};

if (include_targets) {
Expand Down
100 changes: 100 additions & 0 deletions lldb/test/API/commands/statistics/basic/TestStats.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import os
import re
import shutil

import lldb
from lldbsuite.test.decorators import *
Expand Down Expand Up @@ -179,6 +180,7 @@ def test_default_no_run(self):
"totalDebugInfoParseTime",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
if self.getPlatform() != "windows":
Expand Down Expand Up @@ -291,6 +293,7 @@ def test_default_with_run(self):
"totalDebugInfoParseTime",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
stats = debug_stats["targets"][0]
Expand Down Expand Up @@ -331,6 +334,7 @@ def test_memory(self):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)

Expand Down Expand Up @@ -385,6 +389,7 @@ def test_modules(self):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
stats = debug_stats["targets"][0]
Expand All @@ -407,6 +412,7 @@ def test_modules(self):
"symbolTableSavedToCache",
"dwoFileCount",
"loadedDwoFileCount",
"dwoErrorCount",
"triple",
"uuid",
]
Expand Down Expand Up @@ -497,6 +503,7 @@ def test_breakpoints(self):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
target_stats = debug_stats["targets"][0]
Expand Down Expand Up @@ -655,6 +662,99 @@ def test_dwp_dwo_file_count(self):
self.assertEqual(debug_stats["totalDwoFileCount"], 2)
self.assertEqual(debug_stats["totalLoadedDwoFileCount"], 2)

@add_test_categories(["dwo"])
def test_dwo_missing_error_stats(self):
"""
Test that DWO missing errors are reported correctly in statistics.
This test:
1) Builds a program with split DWARF (.dwo files)
2) Delete one of the two .dwo files
3) Verify that 1 DWO error is reported in statistics
"""
da = {
"CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
"EXE": self.getBuildArtifact("a.out"),
}
# -gsplit-dwarf creates separate .dwo files,
# Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
self.build(dictionary=da, debug_info="dwo")
exe = self.getBuildArtifact("a.out")

expected_dwo_files = [
self.getBuildArtifact("dwo_error_main.dwo"),
self.getBuildArtifact("dwo_error_foo.dwo"),
]

# Verify expected files exist
for dwo_file in expected_dwo_files:
self.assertTrue(
os.path.exists(dwo_file),
f"Expected .dwo file does not exist: {dwo_file}",
)

# Remove one of .dwo files to trigger DWO load error
dwo_main_file = self.getBuildArtifact("dwo_error_main.dwo")
os.remove(dwo_main_file)

target = self.createTestTarget(file_path=exe)
debug_stats = self.get_stats()

# Check DWO load error statistics are reported
self.assertIn("totalDwoErrorCount", debug_stats)
self.assertEqual(debug_stats["totalDwoErrorCount"], 1)

# Since there's only one module, module stats should have the same count as total count
self.assertIn("dwoErrorCount", debug_stats["modules"][0])
self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)

@add_test_categories(["dwo"])
def test_dwo_id_mismatch_error_stats(self):
"""
Test that DWO ID mismatch errors are reported correctly in statistics.
This test:
1) Builds a program with split DWARF (.dwo files)
2) Replace one of the .dwo files with a mismatched one to cause a DWO ID mismatch error
3) Verifies that a DWO error is reported in statistics
"""
da = {
"CXX_SOURCES": "dwo_error_main.cpp dwo_error_foo.cpp",
"EXE": self.getBuildArtifact("a.out"),
}
# -gsplit-dwarf creates separate .dwo files,
# Expected output: dwo_error_main.dwo (contains main) and dwo_error_foo.dwo (contains foo struct/function)
self.build(dictionary=da, debug_info="dwo")
exe = self.getBuildArtifact("a.out")

expected_dwo_files = [
self.getBuildArtifact("dwo_error_main.dwo"),
self.getBuildArtifact("dwo_error_foo.dwo"),
]

# Verify expected files exist
for dwo_file in expected_dwo_files:
self.assertTrue(
os.path.exists(dwo_file),
f"Expected .dwo file does not exist: {dwo_file}",
)

# Replace one of the original .dwo file content with another one to trigger DWO ID mismatch error
dwo_foo_file = self.getBuildArtifact("dwo_error_foo.dwo")
dwo_main_file = self.getBuildArtifact("dwo_error_main.dwo")

shutil.copy(dwo_main_file, dwo_foo_file)

# Create a new target and get stats
target = self.createTestTarget(file_path=exe)
debug_stats = self.get_stats()

# Check that DWO load error statistics are reported
self.assertIn("totalDwoErrorCount", debug_stats)
self.assertEqual(debug_stats["totalDwoErrorCount"], 1)

# Since there's only one module, module stats should have the same count as total count
self.assertIn("dwoErrorCount", debug_stats["modules"][0])
self.assertEqual(debug_stats["modules"][0]["dwoErrorCount"], 1)

@skipUnlessDarwin
@no_debug_info_test
def test_dsym_binary_has_symfile_in_stats(self):
Expand Down
10 changes: 10 additions & 0 deletions lldb/test/API/commands/statistics/basic/dwo_error_foo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
struct foo {
int x;
bool y;
};

void dwo_error_foo() {
foo f;
f.x = 1;
f.y = true;
}
5 changes: 5 additions & 0 deletions lldb/test/API/commands/statistics/basic/dwo_error_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void dwo_error_foo();
int main() {
dwo_error_foo();
return 0;
}
Loading