[llvm] r371506 - [llvm-dwarfdump] Add additional stats fields

Djordje Todorovic via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 10 03:37:28 PDT 2019


Author: djtodoro
Date: Tue Sep 10 03:37:28 2019
New Revision: 371506

URL: http://llvm.org/viewvc/llvm-project?rev=371506&view=rev
Log:
[llvm-dwarfdump] Add additional stats fields

The additional fields will be parsed by the llvm-locstats tool in order to
produce more human readable output of the DWARF debug location quality
generated.

Differential Revision: https://reviews.llvm.org/D66525

Added:
    llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll
Modified:
    llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp

Added: llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll?rev=371506&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll (added)
+++ llvm/trunk/test/tools/llvm-dwarfdump/X86/locstats.ll Tue Sep 10 03:37:28 2019
@@ -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)

Modified: llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp?rev=371506&r1=371505&r2=371506&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp (original)
+++ llvm/trunk/tools/llvm-dwarfdump/Statistics.cpp Tue Sep 10 03:37:28 2019
@@ -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 @@ struct GlobalStats {
   /// 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 @@ struct GlobalStats {
   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<unsigned> VarParamLocStats{
+      std::vector<unsigned>(NumOfCoverageCategories, 0)};
+  /// Map non debug entry values coverage.
+  std::vector<unsigned> VarParamNonEntryValLocStats{
+      std::vector<unsigned>(NumOfCoverageCategories, 0)};
+  /// The debug location statistics for formal parameters.
+  std::vector<unsigned> ParamLocStats{
+      std::vector<unsigned>(NumOfCoverageCategories, 0)};
+  /// Map non debug entry values coverage for formal parameters.
+  std::vector<unsigned> ParamNonEntryValLocStats{
+      std::vector<unsigned>(NumOfCoverageCategories, 0)};
+  /// The debug location statistics for local variables.
+  std::vector<unsigned> VarLocStats{
+      std::vector<unsigned>(NumOfCoverageCategories, 0)};
+  /// Map non debug entry values coverage for local variables.
+  std::vector<unsigned> VarNonEntryValLocStats{
+      std::vector<unsigned>(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 @@ static uint64_t getLowPC(DWARFDie Die) {
   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<unsigned> &VarParamLocStats,
+                            std::vector<unsigned> &ParamLocStats,
+                            std::vector<unsigned> &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<PerFunctionStats> &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 @@ static void collectStatsForDie(DWARFDie
     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 @@ static void collectStatsForDie(DWARFDie
   if (Die.find(dwarf::DW_AT_artificial))
     IsArtificial = true;
 
+  auto IsEntryValue = [&](ArrayRef<uint8_t> 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 @@ static void collectStatsForDie(DWARFDie
       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 @@ static void collectStatsForDie(DWARFDie
     }
   }
 
+  // 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 @@ static void collectStatsForDie(DWARFDie
     // 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 @@ static void collectStatsForDie(DWARFDie
     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 @@ static void collectStatsForDie(DWARFDie
         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 @@ static void collectStatsRecursive(DWARFD
                                   std::string VarPrefix, uint64_t ScopeLowPC,
                                   uint64_t BytesInScope, uint32_t InlineDepth,
                                   StringMap<PerFunctionStats> &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 @@ static void collectStatsRecursive(DWARFD
   } 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 @@ static void collectStatsRecursive(DWARFD
       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 @@ static void printDatum(raw_ostream &OS,
   OS << ",\"" << Key << "\":" << Value;
   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
 }
+static void printLocationStats(raw_ostream &OS,
+                               const char *Key,
+                               std::vector<unsigned> &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 @@ bool collectStatsForObjectFile(ObjectFil
                                Twine Filename, raw_ostream &OS) {
   StringRef FormatName = Obj.getFileFormatName();
   GlobalStats GlobalStats;
+  LocationStats LocStats;
   StringMap<PerFunctionStats> Statistics;
   for (const auto &CU : static_cast<DWARFContext *>(&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 @@ bool collectStatsForObjectFile(ObjectFil
   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 @@ bool collectStatsForObjectFile(ObjectFil
   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: "




More information about the llvm-commits mailing list