diff --git a/llvm/test/tools/llvm-dwarfdump/X86/locstats.ll b/llvm/test/tools/llvm-dwarfdump/X86/locstats.ll index f850119acb000..cb5908976fb6c 100644 --- a/llvm/test/tools/llvm-dwarfdump/X86/locstats.ll +++ b/llvm/test/tools/llvm-dwarfdump/X86/locstats.ll @@ -1,5 +1,11 @@ ; RUN: llc -debug-entry-values %s -o - -filetype=obj \ ; RUN: | llvm-dwarfdump -statistics - | FileCheck %s +; RUN: llc -debug-entry-values --dwarf-version=4 %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s +; RUN: llc -debug-entry-values --dwarf-version=3 %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s +; RUN: llc -debug-entry-values --dwarf-version=2 %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s ; CHECK: "sum_all_variables(#bytes in parent scope covered by DW_OP_entry_value)": 5, ; CHECK-NEXT: "sum_all_params(#bytes in parent scope)": 20, @@ -89,6 +95,12 @@ ; CHECK-NEXT: "#local vars - entry values with [80%,90%) of parent scope covered by DW_AT_location": 1, ; CHECK-NEXT: "#local vars - entry values with [90%,100%) of parent scope covered by DW_AT_location": 0, ; CHECK-NEXT: "#local vars - entry values with 100% of parent scope covered by DW_AT_location": 1 +; CHECK-NEXT: "#bytes with line information": 51, +; CHECK-NEXT: "#bytes with line-0 locations": 3, +; CHECK-NEXT: "#line entries": 7, +; CHECK-NEXT: "#line entries (is_stmt)": 5, +; CHECK-NEXT: "#line entries (unique)": 6, +; CHECK-NEXT: "#line entries (unique non-0)": 5 ; The source code of the test case: ; extern void fn3(int *); diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp index 96841c3c387bd..10ce6093909b6 100644 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -8,6 +8,7 @@ #include "llvm-dwarfdump.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" @@ -188,6 +189,16 @@ struct LocationStats { /// Total number of local variables processed. SaturatingUINT64 NumVar = 0; }; + +/// Holds accumulated debug line statistics across all CUs. +struct LineStats { + SaturatingUINT64 NumBytes = 0; + SaturatingUINT64 NumLineZeroBytes = 0; + SaturatingUINT64 NumEntries = 0; + SaturatingUINT64 NumIsStmtEntries = 0; + SaturatingUINT64 NumUniqueEntries = 0; + SaturatingUINT64 NumUniqueNonZeroEntries = 0; +}; } // namespace /// Collect debug location statistics for one DIE. @@ -848,6 +859,7 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, StringRef FormatName = Obj.getFileFormatName(); GlobalStats GlobalStats; LocationStats LocStats; + LineStats LnStats; StringMap Statistics; // This variable holds variable information for functions with // abstract_origin globally, across all CUs. @@ -856,6 +868,14 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, // abstract_origin. FunctionDIECUTyMap AbstractOriginFnCUs; CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved; + // Tuple representing a single source code position in the line table. Fields + // are respectively: Line, Col, File, where 'File' is an index into the Files + // vector below. + using LineTuple = std::tuple; + SmallVector Files; + DenseSet UniqueLines; + DenseSet UniqueNonZeroLines; + for (const auto &CU : static_cast(&DICtx)->compile_units()) { if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { // This variable holds variable information for functions with @@ -882,8 +902,58 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, CrossCUReferencesToBeResolved.push_back( DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset)); } + const auto *LineTable = DICtx.getLineTableForUnit(CU.get()); + std::optional LastFileIdxOpt; + if (LineTable) + LastFileIdxOpt = LineTable->getLastValidFileIndex(); + if (LastFileIdxOpt) { + // Each CU has its own file index; in order to track unique line entries + // across CUs, we therefore need to map each CU file index to a global + // file index, which we store here. + DenseMap CUFileMapping; + for (uint64_t FileIdx = 0; FileIdx <= *LastFileIdxOpt; ++FileIdx) { + std::string File; + if (LineTable->getFileNameByIndex( + FileIdx, CU->getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + File)) { + auto ExistingFile = llvm::find(Files, File); + if (ExistingFile != Files.end()) { + CUFileMapping[FileIdx] = std::distance(Files.begin(), ExistingFile); + } else { + CUFileMapping[FileIdx] = Files.size(); + Files.push_back(File); + } + } + } + for (const auto &Seq : LineTable->Sequences) { + LnStats.NumBytes += Seq.HighPC - Seq.LowPC; + // Ignore the `end_sequence` entry, since it's not interesting for us. + LnStats.NumEntries += Seq.LastRowIndex - Seq.FirstRowIndex - 1; + for (size_t RowIdx = Seq.FirstRowIndex; RowIdx < Seq.LastRowIndex - 1; + ++RowIdx) { + auto Entry = LineTable->Rows[RowIdx]; + if (Entry.IsStmt) + LnStats.NumIsStmtEntries += 1; + assert(CUFileMapping.contains(Entry.File) && + "Should have been collected earlier!"); + uint16_t MappedFile = CUFileMapping[Entry.File]; + UniqueLines.insert({Entry.Line, Entry.Column, MappedFile}); + if (Entry.Line != 0) { + UniqueNonZeroLines.insert({Entry.Line, Entry.Column, MappedFile}); + } else { + auto EntryStartAddress = Entry.Address.Address; + auto EntryEndAddress = LineTable->Rows[RowIdx + 1].Address.Address; + LnStats.NumLineZeroBytes += EntryEndAddress - EntryStartAddress; + } + } + } + } } + LnStats.NumUniqueEntries = UniqueLines.size(); + LnStats.NumUniqueNonZeroEntries = UniqueNonZeroLines.size(); + /// Resolve CrossCU references. collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin( LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo, @@ -1043,6 +1113,16 @@ bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, printLocationStats(J, "#local vars", LocStats.LocalVarLocStats); printLocationStats(J, "#local vars - entry values", LocStats.LocalVarNonEntryValLocStats); + + // Print line statistics for the object file. + printDatum(J, "#bytes with line information", LnStats.NumBytes.Value); + printDatum(J, "#bytes with line-0 locations", LnStats.NumLineZeroBytes.Value); + printDatum(J, "#line entries", LnStats.NumEntries.Value); + printDatum(J, "#line entries (is_stmt)", LnStats.NumIsStmtEntries.Value); + printDatum(J, "#line entries (unique)", LnStats.NumUniqueEntries.Value); + printDatum(J, "#line entries (unique non-0)", + LnStats.NumUniqueNonZeroEntries.Value); + J.objectEnd(); OS << '\n'; LLVM_DEBUG(