Index: llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll =================================================================== --- llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll +++ llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll @@ -0,0 +1,211 @@ +; RUN: llc -debug-entry-values %s -o - -filetype=obj \ +; RUN: | llvm-dwarfdump -statistics - | FileCheck %s +; +; CHECK: "entry value scope bytes covered":5 +; CHECK: "formal params scope bytes total":20 +; CHECK: "formal params scope bytes covered":20 +; CHECK: "formal params entry value scope bytes covered":5 +; CHECK: "vars scope bytes total":78 +; CHECK: "vars scope bytes covered":60 +; CHECK: "vars entry value scope bytes covered":0 +; CHECK: "total variables procesed by location statistics":6 +; CHECK: "variables with 0% of its scope covered":1 +; CHECK: "variables with 1-9% of its scope covered":0 +; CHECK: "variables with 10-19% of its scope covered":0 +; CHECK: "variables with 20-29% of its scope covered":0 +; CHECK: "variables with 30-39% of its scope covered":0 +; CHECK: "variables with 40-49% of its scope covered":0 +; CHECK: "variables with 50-59% of its scope covered":1 +; CHECK: "variables with 60-69% of its scope covered":0 +; CHECK: "variables with 70-79% of its scope covered":0 +; CHECK: "variables with 80-89% of its scope covered":1 +; CHECK: "variables with 90-99% of its scope covered":0 +; CHECK: "variables with 100% of its scope covered":3 +; CHECK: "variables (excluding the debug entry values) with 0% of its scope covered":1 +; CHECK: "variables (excluding the debug entry values) with 1-9% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 10-19% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 20-29% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 30-39% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 40-49% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 50-59% of its scope covered":2 +; CHECK: "variables (excluding the debug entry values) with 60-69% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 70-79% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 80-89% of its scope covered":1 +; CHECK: "variables (excluding the debug entry values) with 90-99% of its scope covered":0 +; CHECK: "variables (excluding the debug entry values) with 100% of its scope covered":2 +; CHECK: "total params procesed by location statistics":2 +; CHECK: "params with 0% of its scope covered":0 +; CHECK: "params with 1-9% of its scope covered":0 +; CHECK: "params with 10-19% of its scope covered":0 +; CHECK: "params with 20-29% of its scope covered":0 +; CHECK: "params with 30-39% of its scope covered":0 +; CHECK: "params with 40-49% of its scope covered":0 +; CHECK: "params with 50-59% of its scope covered":0 +; CHECK: "params with 60-69% of its scope covered":0 +; CHECK: "params with 70-79% of its scope covered":0 +; CHECK: "params with 80-89% of its scope covered":0 +; CHECK: "params with 90-99% of its scope covered":0 +; CHECK: "params with 100% of its scope covered":2 +; CHECK: "params (excluding the debug entry values) with 0% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 1-9% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 10-19% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 20-29% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 30-39% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 40-49% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 50-59% of its scope covered":1 +; CHECK: "params (excluding the debug entry values) with 60-69% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 70-79% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 80-89% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 90-99% of its scope covered":0 +; CHECK: "params (excluding the debug entry values) with 100% of its scope covered":1 +; CHECK: "total vars procesed by location statistics":4 +; CHECK: "vars with 0% of its scope covered":1 +; CHECK: "vars with 1-9% of its scope covered":0 +; CHECK: "vars with 10-19% of its scope covered":0 +; CHECK: "vars with 20-29% of its scope covered":0 +; CHECK: "vars with 30-39% of its scope covered":0 +; CHECK: "vars with 40-49% of its scope covered":0 +; CHECK: "vars with 50-59% of its scope covered":1 +; CHECK: "vars with 60-69% of its scope covered":0 +; CHECK: "vars with 70-79% of its scope covered":0 +; CHECK: "vars with 80-89% of its scope covered":1 +; CHECK: "vars with 90-99% of its scope covered":0 +; CHECK: "vars with 100% of its scope covered":1 +; CHECK: "vars (excluding the debug entry values) with 0% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 1-9% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 10-19% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 20-29% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 30-39% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 40-49% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 50-59% of its scope covered":1 +; CHECK: "vars (excluding the debug entry values) with 60-69% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 70-79% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 80-89% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 90-99% of its scope covered":0 +; CHECK: "vars (excluding the debug entry values) with 100% of its scope covered":1} +; +; The source code of the test case: +; extern void fn3(int *); +; extern void fn2 (int); +; __attribute__((noinline)) +; void +; fn1 (int x, int y) +; { +; int u = x + y; +; if (x > 1) +; u += 1; +; else +; u += 2; +; if (y > 4) +; u += x; +; int a = 7; +; fn2 (a); +; u --; +; } +; +; __attribute__((noinline)) +; int f() +; { +; int l, k; +; fn3(&l); +; fn3(&k); +; fn1 (l, k); +; return 0; +; } +; +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: noinline nounwind uwtable +define dso_local void @fn1(i32 %x, i32 %y) local_unnamed_addr !dbg !16 { +entry: + call void @llvm.dbg.value(metadata i32 %x, metadata !20, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata i32 %y, metadata !21, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata i32 undef, metadata !22, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata i32 undef, metadata !22, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata i32 7, metadata !23, metadata !DIExpression()), !dbg !24 + tail call void @fn2(i32 7), !dbg !25 + call void @llvm.dbg.value(metadata i32 undef, metadata !22, metadata !DIExpression(DW_OP_constu, 1, DW_OP_minus, DW_OP_stack_value)), !dbg !24 + ret void, !dbg !26 +} + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) + +declare !dbg !4 dso_local void @fn2(i32) local_unnamed_addr + +; Function Attrs: argmemonly nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) + +; Function Attrs: noinline nounwind uwtable +define dso_local i32 @f() local_unnamed_addr !dbg !27 { +entry: + %l = alloca i32, align 4 + %k = alloca i32, align 4 + %0 = bitcast i32* %l to i8*, !dbg !33 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0), !dbg !33 + %1 = bitcast i32* %k to i8*, !dbg !33 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %1), !dbg !33 + call void @llvm.dbg.value(metadata i32* %l, metadata !31, metadata !DIExpression(DW_OP_deref)), !dbg !34 + call void @fn3(i32* nonnull %l), !dbg !35 + call void @llvm.dbg.value(metadata i32* %k, metadata !32, metadata !DIExpression(DW_OP_deref)), !dbg !34 + call void @fn3(i32* nonnull %k), !dbg !36 + %2 = load i32, i32* %l, align 4, !dbg !37 + call void @llvm.dbg.value(metadata i32 %2, metadata !31, metadata !DIExpression()), !dbg !34 + %3 = load i32, i32* %k, align 4, !dbg !37 + call void @llvm.dbg.value(metadata i32 %3, metadata !32, metadata !DIExpression()), !dbg !34 + call void @fn1(i32 %2, i32 %3), !dbg !37 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %1), !dbg !37 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0), !dbg !37 + ret i32 0, !dbg !37 +} + +declare !dbg !8 dso_local void @fn3(i32*) local_unnamed_addr + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!12, !13, !14} +!llvm.ident = !{!15} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{!4, !8} +!4 = !DISubprogram(name: "fn2", scope: !1, file: !1, line: 2, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{null, !7} +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !DISubprogram(name: "fn3", scope: !1, file: !1, line: 1, type: !9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11} +!11 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!12 = !{i32 2, !"Dwarf Version", i32 4} +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{!"clang version 10.0.0"} +!16 = distinct !DISubprogram(name: "fn1", scope: !1, file: !1, line: 6, type: !17, scopeLine: 7, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19) +!17 = !DISubroutineType(types: !18) +!18 = !{null, !7, !7} +!19 = !{!20, !21, !22, !23} +!20 = !DILocalVariable(name: "x", arg: 1, scope: !16, file: !1, line: 6, type: !7, flags: DIFlagArgumentNotModified) +!21 = !DILocalVariable(name: "y", arg: 2, scope: !16, file: !1, line: 6, type: !7, flags: DIFlagArgumentNotModified) +!22 = !DILocalVariable(name: "u", scope: !16, file: !1, line: 8, type: !7) +!23 = !DILocalVariable(name: "a", scope: !16, file: !1, line: 18, type: !7) +!24 = !DILocation(line: 0, scope: !16) +!25 = !DILocation(line: 20, column: 3, scope: !16) +!26 = !DILocation(line: 22, column: 1, scope: !16) +!27 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 25, type: !28, scopeLine: 26, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !30) +!28 = !DISubroutineType(types: !29) +!29 = !{!7} +!30 = !{!31, !32} +!31 = !DILocalVariable(name: "l", scope: !27, file: !1, line: 27, type: !7) +!32 = !DILocalVariable(name: "k", scope: !27, file: !1, line: 27, type: !7) +!33 = !DILocation(line: 27, column: 3, scope: !27) +!34 = !DILocation(line: 0, scope: !27) +!35 = !DILocation(line: 29, column: 3, scope: !27) +!36 = !DILocation(line: 30, column: 3, scope: !27) +!37 = !DILocation(line: 32, column: 8, scope: !27) Index: llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp =================================================================== --- llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp +++ llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp @@ -10,6 +10,12 @@ using namespace llvm; using namespace object; +/// This represents the number of categories of debug location coverage being +/// calculated. The first category is the number of variables with 0% location +/// coverage, but the last category is the number of variables with 100% +/// location coverage. +constexpr int NumOfCoverageCategories = 12; + /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { @@ -56,6 +62,28 @@ /// Total number of PC range bytes in each variable's enclosing scope, /// starting from the first definition of the variable. unsigned ScopeBytesFromFirstDefinition = 0; + /// Total number of PC range bytes covered by DW_AT_locations with + /// the debug entry values (DW_OP_entry_value). + unsigned ScopeEntryValueBytesCovered = 0; + /// Total number of PC range bytes covered by DW_AT_locations of + /// formal parameters. + unsigned ParamScopeBytesCovered = 0; + /// Total number of PC range bytes in each variable's enclosing scope, + /// starting from the first definition of the variable (only for parameters). + unsigned ParamScopeBytesFromFirstDefinition = 0; + /// Total number of PC range bytes covered by DW_AT_locations with + /// the debug entry values (DW_OP_entry_value) (only for parameters). + unsigned ParamScopeEntryValueBytesCovered = 0; + /// Total number of PC range bytes covered by DW_AT_locations (only for local + /// variables). + unsigned VarScopeBytesCovered = 0; + /// Total number of PC range bytes in each variable's enclosing scope, + /// starting from the first definition of the variable (only for local + /// variables). + unsigned VarScopeBytesFromFirstDefinition = 0; + /// Total number of PC range bytes covered by DW_AT_locations with + /// the debug entry values (DW_OP_entry_value) (only for local variables). + unsigned VarScopeEntryValueBytesCovered = 0; /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line). unsigned CallSiteEntries = 0; /// Total number of call site DIEs (DW_TAG_call_site). @@ -71,6 +99,39 @@ uint64_t InlineFunctionSize = 0; }; +/// Holds accumulated debug location statistics about local variables and +/// formal parameters. +struct LocationStats { + /// Map the scope coverage decile to the number of variables in the decile. + /// The first element of the array (at the index zero) represents the number + /// of variables with the no debug location at all, but the last element + /// in the vector represents the number of fully covered variables within + /// its scope. + std::vector VarParamLocStats{ + std::vector(NumOfCoverageCategories, 0)}; + /// Map non debug entry values coverage. + std::vector VarParamNonEntryValLocStats{ + std::vector(NumOfCoverageCategories, 0)}; + /// The debug location statistics for formal parameters. + std::vector ParamLocStats{ + std::vector(NumOfCoverageCategories, 0)}; + /// Map non debug entry values coverage for formal parameters. + std::vector ParamNonEntryValLocStats{ + std::vector(NumOfCoverageCategories, 0)}; + /// The debug location statistics for local variables. + std::vector VarLocStats{ + std::vector(NumOfCoverageCategories, 0)}; + /// Map non debug entry values coverage for local variables. + std::vector VarNonEntryValLocStats{ + std::vector(NumOfCoverageCategories, 0)}; + /// Total number of local variables and function parameters processed. + unsigned NumVarParam = 0; + /// Total number of formal parameters processed. + unsigned NumParam = 0; + /// Total number of local variables processed. + unsigned NumVar = 0; +}; + /// Extract the low pc from a Die. static uint64_t getLowPC(DWARFDie Die) { auto RangesOrError = Die.getAddressRanges(); @@ -84,18 +145,52 @@ return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0); } +/// Collect debug location statistics for one DIE. +static void collectLocStats(uint64_t BytesCovered, uint64_t BytesInScope, + std::vector &VarParamLocStats, + std::vector &ParamLocStats, + std::vector &VarLocStats, bool IsParam, + bool IsLocalVar) { + auto getCoverageBucket = [BytesCovered, BytesInScope]() -> unsigned { + unsigned LocBucket = 100 * (double)BytesCovered / BytesInScope; + if (LocBucket == 0) { + // No debug location at all for the variable. + return 0; + } else if (LocBucket == 100 || BytesCovered > BytesInScope) { + // Fully covered variable within its scope. + return NumOfCoverageCategories - 1; + } else { + // Get covered range (e.g. 20%-29%). + LocBucket /= 10; + return LocBucket + 1; + } + }; + + unsigned CoverageBucket = getCoverageBucket(); + VarParamLocStats[CoverageBucket]++; + if (IsParam) + ParamLocStats[CoverageBucket]++; + else if (IsLocalVar) + VarLocStats[CoverageBucket]++; +} + /// Collect debug info quality metrics for one DIE. static void collectStatsForDie(DWARFDie Die, uint64_t UnitLowPC, std::string FnPrefix, std::string VarPrefix, uint64_t ScopeLowPC, uint64_t BytesInScope, uint32_t InlineDepth, StringMap &FnStatMap, - GlobalStats &GlobalStats) { + GlobalStats &GlobalStats, + LocationStats &LocStats) { bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; bool IsArtificial = false; uint64_t BytesCovered = 0; + uint64_t BytesEntryValuesCovered = 0; uint64_t OffsetToFirstDefinition = 0; + auto &FnStats = FnStatMap[FnPrefix]; + bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter; + bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable; if (Die.getTag() == dwarf::DW_TAG_call_site || Die.getTag() == dwarf::DW_TAG_GNU_call_site) { @@ -109,9 +204,7 @@ return; } - if (Die.getTag() != dwarf::DW_TAG_formal_parameter && - Die.getTag() != dwarf::DW_TAG_variable && - Die.getTag() != dwarf::DW_TAG_member) { + if (!IsParam && !IsLocalVar && Die.getTag() != dwarf::DW_TAG_member) { // Not a variable or constant member. return; } @@ -126,6 +219,19 @@ if (Die.find(dwarf::DW_AT_artificial)) IsArtificial = true; + auto IsEntryValue = [&](ArrayRef D) -> bool { + DWARFUnit *U = Die.getDwarfUnit(); + DataExtractor Data(toStringRef(D), + Die.getDwarfUnit()->getContext().isLittleEndian(), 0); + DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); + // Consider the expression containing the DW_OP_entry_value as + // an entry value. + return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { + return Op.getCode() == dwarf::DW_OP_entry_value || + Op.getCode() == dwarf::DW_OP_GNU_entry_value; + }); + }; + if (Die.find(dwarf::DW_AT_const_value)) { // This catches constant members *and* variables. HasLoc = true; @@ -143,8 +249,12 @@ if (auto DebugLocOffset = FormValue->getAsSectionOffset()) { auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc(); if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) { - for (auto Entry : List->Entries) - BytesCovered += Entry.End - Entry.Begin; + for (auto Entry : List->Entries) { + uint64_t BytesEntryCovered = Entry.End - Entry.Begin; + BytesCovered += BytesEntryCovered; + if (IsEntryValue(Entry.Loc)) + BytesEntryValuesCovered += BytesEntryCovered; + } if (List->Entries.size()) { uint64_t FirstDef = List->Entries[0].Begin; uint64_t UnitOfs = UnitLowPC; @@ -164,8 +274,25 @@ } } + // Calculate the debug location statistics. + if (BytesInScope) { + LocStats.NumVarParam++; + if (IsParam) + LocStats.NumParam++; + else if (IsLocalVar) + LocStats.NumVar++; + + collectLocStats(BytesCovered, BytesInScope, LocStats.VarParamLocStats, + LocStats.ParamLocStats, LocStats.VarLocStats, IsParam, + IsLocalVar); + // Non debug entry values coverage statistics. + collectLocStats(BytesCovered - BytesEntryValuesCovered, BytesInScope, + LocStats.VarParamNonEntryValLocStats, + LocStats.ParamNonEntryValLocStats, + LocStats.VarNonEntryValLocStats, IsParam, IsLocalVar); + } + // Collect PC range coverage data. - auto &FnStats = FnStatMap[FnPrefix]; if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; @@ -181,6 +308,17 @@ // Turns out we have a lot of ranges that extend past the lexical scope. GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered); GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope; + GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered; + if (IsParam) { + GlobalStats.ParamScopeBytesCovered += + std::min(BytesInScope, BytesCovered); + GlobalStats.ParamScopeBytesFromFirstDefinition += BytesInScope; + GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered; + } else if (IsLocalVar) { + GlobalStats.VarScopeBytesCovered += std::min(BytesInScope, BytesCovered); + GlobalStats.VarScopeBytesFromFirstDefinition += BytesInScope; + GlobalStats.VarScopeEntryValueBytesCovered += BytesEntryValuesCovered; + } assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytesFromFirstDefinition); } else if (Die.getTag() == dwarf::DW_TAG_member) { @@ -189,7 +327,7 @@ FnStats.TotalVarWithLoc += (unsigned)HasLoc; } if (!IsArtificial) { - if (Die.getTag() == dwarf::DW_TAG_formal_parameter) { + if (IsParam) { FnStats.NumParams++; if (HasType) FnStats.NumParamTypes++; @@ -197,7 +335,7 @@ FnStats.NumParamSourceLocations++; if (HasLoc) FnStats.NumParamLocations++; - } else if (Die.getTag() == dwarf::DW_TAG_variable) { + } else if (IsLocalVar) { FnStats.NumVars++; if (HasType) FnStats.NumVarTypes++; @@ -214,7 +352,8 @@ std::string VarPrefix, uint64_t ScopeLowPC, uint64_t BytesInScope, uint32_t InlineDepth, StringMap &FnStatMap, - GlobalStats &GlobalStats) { + GlobalStats &GlobalStats, + LocationStats &LocStats) { // Handle any kind of lexical scope. const dwarf::Tag Tag = Die.getTag(); const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; @@ -283,7 +422,7 @@ } else { // Not a scope, visit the Die itself. It could be a variable. collectStatsForDie(Die, UnitLowPC, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope, - InlineDepth, FnStatMap, GlobalStats); + InlineDepth, FnStatMap, GlobalStats, LocStats); } // Set InlineDepth correctly for child recursion @@ -301,7 +440,8 @@ ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; collectStatsRecursive(Child, UnitLowPC, FnPrefix, ChildVarPrefix, ScopeLowPC, - BytesInScope, InlineDepth, FnStatMap, GlobalStats); + BytesInScope, InlineDepth, FnStatMap, GlobalStats, + LocStats); Child = Child.getSibling(); } } @@ -317,6 +457,29 @@ OS << ",\"" << Key << "\":" << Value; LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n'); } +static void printLocationStats(raw_ostream &OS, + const char *Key, + std::vector &LocationStats) { + OS << ",\"" << Key << " with 0% of its scope covered\":" + << LocationStats[0]; + LLVM_DEBUG(llvm::dbgs() << Key << " with 0% of its scope covered: " + << LocationStats[0] << '\n'); + OS << ",\"" << Key << " with 1-9% of its scope covered\":" + << LocationStats[1]; + LLVM_DEBUG(llvm::dbgs() << Key << " with 1-9% of its scope covered: " + << LocationStats[1] << '\n'); + for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) { + OS << ",\"" << Key << " with " << (i - 1) * 10 << "-" << i * 10 - 1 + << "% of its scope covered\":" << LocationStats[i]; + LLVM_DEBUG(llvm::dbgs() + << Key << " with " << (i - 1) * 10 << "-" << i * 10 - 1 + << "% of its scope covered: " << LocationStats[i]); + } + OS << ",\"" << Key << " with 100% of its scope covered\":" + << LocationStats[NumOfCoverageCategories - 1]; + LLVM_DEBUG(llvm::dbgs() << Key << " with 100% of its scope covered: " + << LocationStats[NumOfCoverageCategories - 1]); +} /// \} /// Collect debug info quality metrics for an entire DIContext. @@ -331,10 +494,12 @@ Twine Filename, raw_ostream &OS) { StringRef FormatName = Obj.getFileFormatName(); GlobalStats GlobalStats; + LocationStats LocStats; StringMap Statistics; for (const auto &CU : static_cast(&DICtx)->compile_units()) if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) - collectStatsRecursive(CUDie, getLowPC(CUDie), "/", "g", 0, 0, 0, Statistics, GlobalStats); + collectStatsRecursive(CUDie, getLowPC(CUDie), "/", "g", 0, 0, 0, + Statistics, GlobalStats, LocStats); /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the @@ -402,6 +567,19 @@ printDatum(OS, "scope bytes total", GlobalStats.ScopeBytesFromFirstDefinition); printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); + printDatum(OS, "entry value scope bytes covered", + GlobalStats.ScopeEntryValueBytesCovered); + printDatum(OS, "formal params scope bytes total", + GlobalStats.ParamScopeBytesFromFirstDefinition); + printDatum(OS, "formal params scope bytes covered", + GlobalStats.ParamScopeBytesCovered); + printDatum(OS, "formal params entry value scope bytes covered", + GlobalStats.ParamScopeEntryValueBytesCovered); + printDatum(OS, "vars scope bytes total", + GlobalStats.VarScopeBytesFromFirstDefinition); + printDatum(OS, "vars scope bytes covered", GlobalStats.VarScopeBytesCovered); + printDatum(OS, "vars entry value scope bytes covered", + GlobalStats.VarScopeEntryValueBytesCovered); printDatum(OS, "total function size", GlobalStats.FunctionSize); printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); printDatum(OS, "total formal params", ParamTotal); @@ -412,6 +590,20 @@ printDatum(OS, "vars with source location", VarWithSrcLoc); printDatum(OS, "vars with type", VarWithType); printDatum(OS, "vars with binary location", VarWithLoc); + printDatum(OS, "total variables procesed by location statistics", + LocStats.NumVarParam); + printLocationStats(OS, "variables", LocStats.VarParamLocStats); + printLocationStats(OS, "variables (excluding the debug entry values)", + LocStats.VarParamNonEntryValLocStats); + printDatum(OS, "total params procesed by location statistics", + LocStats.NumParam); + printLocationStats(OS, "params", LocStats.ParamLocStats); + printLocationStats(OS, "params (excluding the debug entry values)", + LocStats.ParamNonEntryValLocStats); + printDatum(OS, "total vars procesed by location statistics", LocStats.NumVar); + printLocationStats(OS, "vars", LocStats.VarLocStats); + printLocationStats(OS, "vars (excluding the debug entry values)", + LocStats.ParamNonEntryValLocStats); OS << "}\n"; LLVM_DEBUG( llvm::dbgs() << "Total Availability: "