Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d978cb5
[lldb] Add count for errors of DWO files in statistics
zw3917 Aug 22, 2025
ed0530f
Merge branch 'main' of https://github.com/llvm/llvm-project
zw3917 Aug 22, 2025
04a0e16
Merge branch 'main' of https://github.com/llvm/llvm-project
zw3917 Aug 22, 2025
4416c22
Merge branch 'main' of https://github.com/llvm/llvm-project
zw3917 Aug 22, 2025
38cf0ea
Merge branch 'main' of https://github.com/llvm/llvm-project
zw3917 Aug 22, 2025
5370425
Merge remote-tracking branch 'zw3917/main'
zw3917 Aug 22, 2025
c9184c7
Merge branch 'main' into main
zw3917 Aug 22, 2025
6649b3d
Merge branch 'main' into main
zw3917 Aug 22, 2025
d526eac
Merge branch 'main' of https://github.com/llvm/llvm-project
zw3917 Aug 22, 2025
698bc9e
Combine DWO counting statistics into a single DWOStats struct
zw3917 Aug 24, 2025
f65f68d
Merge branch 'main' of https://github.com/llvm/llvm-project
zw3917 Aug 24, 2025
aa223d3
Merge branch 'llvm:main' into main
zw3917 Aug 24, 2025
460fcae
Merge branch 'main' of https://github.com/zw3917/llvm-project
zw3917 Aug 24, 2025
1e50bec
Merge branch 'llvm:main' into main
zw3917 Aug 28, 2025
ba2c528
Merge branch 'llvm:main' into main
zw3917 Aug 28, 2025
3881633
Reuse DWOStats in Statistics
zw3917 Aug 28, 2025
ec5dabd
[lldb]Reuse DWOStats in statistics
zw3917 Aug 28, 2025
76850ec
[lldb]Reuse DWOStats in statistics
zw3917 Aug 28, 2025
3f866ad
[format]Revert for auto-formatter
zw3917 Sep 2, 2025
1a703e7
Merge branch 'main' into main
zw3917 Sep 3, 2025
90d01ec
Merge branch 'main' into main
zw3917 Sep 3, 2025
033551d
[lldb]Update TestStas to include correct dwo stats
zw3917 Sep 3, 2025
3c6f745
Merge branch 'main' of https://github.com/zw3917/llvm-project
zw3917 Sep 3, 2025
805129f
Merge branch 'main' into main
zw3917 Sep 3, 2025
c4e4694
Merge branch 'main' into main
zw3917 Sep 3, 2025
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
4 changes: 4 additions & 0 deletions lldb/include/lldb/Symbol/SymbolFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ class SymbolFile : public PluginInterface {
/// symbol file doesn't support DWO files, both counts will be 0.
virtual std::pair<uint32_t, uint32_t> GetDwoFileCounts() { return {0, 0}; }

/// Calculates the count of dwo load error, return the number of dwo file with
/// errors, 0 by default.
virtual uint32_t CountDwoLoadErrors() { return 0; }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be best to modify the GetDwoFileCounts function to return a simple struct and rename it:

struct DWOStats {
  uint32_t loaded_dwo_file_count = 0;
  uint32_t dwo_file_count = 0;
  uint32_t dwo_error_count = 0;
};

virtual DWOStats GetDwoStats() { return {}; }

This will replace both GetDwoFileCounts() and CountDwoLoadErrors()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out! Just updated the implementation to use the new structure.

virtual lldb::TypeSP
MakeType(lldb::user_id_t uid, ConstString name,
std::optional<uint64_t> byte_size, SymbolContextScope *context,
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/Target/Statistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ struct ModuleStats {
bool debug_info_had_incomplete_types = false;
uint32_t dwo_file_count = 0;
uint32_t loaded_dwo_file_count = 0;
uint32_t dwo_load_error_count = 0;
};

struct ConstStringStats {
Expand Down
18 changes: 18 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4530,3 +4530,21 @@ std::pair<uint32_t, uint32_t> SymbolFileDWARF::GetDwoFileCounts() {

return {loaded_dwo_count, total_dwo_count};
}

uint32_t SymbolFileDWARF::CountDwoLoadErrors() {
uint32_t dwo_load_error_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 unit has dwo error (False by default).
const Status &dwo_error = dwarf_cu->GetDwoError();
if (dwo_error.Fail())
dwo_load_error_count++;
}
return dwo_load_error_count;
}
3 changes: 3 additions & 0 deletions lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,9 @@ class SymbolFileDWARF : public SymbolFileCommon {
// CUs and total DWO CUs. For non-split-dwarf files, this reports 0 for both.
std::pair<uint32_t, uint32_t> GetDwoFileCounts() override;

/// Count the number of dwo load errors happened.
uint32_t CountDwoLoadErrors() override;

DWARFContext &GetDWARFContext() { return m_context; }

const std::shared_ptr<SymbolFileDWARFDwo> &GetDwpSymbolFile();
Expand Down
6 changes: 6 additions & 0 deletions lldb/source/Target/Statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ json::Value ModuleStats::ToJSON() const {
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("dwoLoadErrorCount", dwo_load_error_count);

if (!symbol_locator_time.map.empty()) {
json::Object obj;
Expand Down Expand Up @@ -326,6 +327,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
uint32_t symtab_symbol_count = 0;
uint32_t total_loaded_dwo_file_count = 0;
uint32_t total_dwo_file_count = 0;
uint32_t total_dwo_load_error_count = 0;
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 @@ -361,6 +363,9 @@ llvm::json::Value DebuggerStats::ReportStatistics(
sym_file->GetDwoFileCounts();
total_dwo_file_count += module_stat.dwo_file_count;
total_loaded_dwo_file_count += module_stat.loaded_dwo_file_count;
uint32_t current_dwo_errors = sym_file->CountDwoLoadErrors();
module_stat.dwo_load_error_count += current_dwo_errors;
total_dwo_load_error_count += current_dwo_errors;
module_stat.debug_info_index_loaded_from_cache =
sym_file->GetDebugInfoIndexWasLoadedFromCache();
if (module_stat.debug_info_index_loaded_from_cache)
Expand Down Expand Up @@ -437,6 +442,7 @@ llvm::json::Value DebuggerStats::ReportStatistics(
{"totalSymbolTableSymbolCount", symtab_symbol_count},
{"totalLoadedDwoFileCount", total_loaded_dwo_file_count},
{"totalDwoFileCount", total_dwo_file_count},
{"totalDwoLoadErrorCount", total_dwo_load_error_count},
};

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

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

Expand Down Expand Up @@ -385,6 +390,7 @@ def test_modules(self):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoLoadErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
stats = debug_stats["targets"][0]
Expand All @@ -407,6 +413,7 @@ def test_modules(self):
"symbolTableSavedToCache",
"dwoFileCount",
"loadedDwoFileCount",
"dwoLoadErrorCount",
"triple",
"uuid",
]
Expand Down Expand Up @@ -497,6 +504,7 @@ def test_breakpoints(self):
"totalDebugInfoByteSize",
"totalDwoFileCount",
"totalLoadedDwoFileCount",
"totalDwoLoadErrorCount",
]
self.verify_keys(debug_stats, '"debug_stats"', debug_stat_keys, None)
target_stats = debug_stats["targets"][0]
Expand Down Expand Up @@ -655,6 +663,113 @@ 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 all two .dwo files
3) Verify that 2 DWO load errors are 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")
self.addTearDownCleanup(dictionary=da)
exe = self.getBuildArtifact("a.out")

# Remove the two .dwo files to trigger a DWO load error
dwo_files = glob.glob(self.getBuildArtifact("*.dwo"))
for dwo_file in dwo_files:
os.rename(dwo_file, dwo_file + ".bak")

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

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

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

# Restore the original .dwo file
for dwo_file in dwo_files:
os.rename(dwo_file + ".bak", dwo_file)

@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) Change one of the source file content and rebuild
3) Replace the new .dwo file with the original one to create a DWO ID mismatch
4) Verifies that a DWO load error is reported in statistics
5) Restores the original source file
"""
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")
self.addTearDownCleanup(dictionary=da)
exe = self.getBuildArtifact("a.out")

# Find and make a backup of the original .dwo file
dwo_files = glob.glob(self.getBuildArtifact("*.dwo"))

original_dwo_file = dwo_files[1]
original_dwo_backup = original_dwo_file + ".bak"
shutil.copy2(original_dwo_file, original_dwo_backup)

target = self.createTestTarget(file_path=exe)
initial_stats = self.get_stats()
self.assertIn("totalDwoLoadErrorCount", initial_stats)
self.assertEqual(initial_stats["totalDwoLoadErrorCount"], 0)
self.dbg.DeleteTarget(target)

# Get the original file size before modification
source_file_path = self.getSourcePath("dwo_error_foo.cpp")
original_size = os.path.getsize(source_file_path)

try:
# Modify the source code and rebuild
with open(source_file_path, "a") as f:
f.write("\n void additional_foo(){}\n")

# Rebuild and replace the new .dwo file with the original one
self.build(dictionary=da, debug_info="dwo")
shutil.copy2(original_dwo_backup, original_dwo_file)

# Create a new target and run to a breakpoint to force DWO file loading
target = self.createTestTarget(file_path=exe)
debug_stats = self.get_stats()

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

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

finally:
# Remove the appended content
with open(source_file_path, "a") as f:
f.truncate(original_size)

# Restore the original .dwo file
if os.path.exists(original_dwo_backup):
os.unlink(original_dwo_backup)

@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;
}