1097a140dSpatrick //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
2097a140dSpatrick //
3097a140dSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4097a140dSpatrick // See https://llvm.org/LICENSE.txt for license information.
5097a140dSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6097a140dSpatrick //
7097a140dSpatrick //===----------------------------------------------------------------------===//
8097a140dSpatrick
9097a140dSpatrick #include "llvm-dwarfdump.h"
1009467b48Spatrick #include "llvm/ADT/DenseMap.h"
1109467b48Spatrick #include "llvm/ADT/StringSet.h"
1209467b48Spatrick #include "llvm/DebugInfo/DWARF/DWARFContext.h"
1309467b48Spatrick #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
14*d415bd75Srobert #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
1509467b48Spatrick #include "llvm/Object/ObjectFile.h"
1609467b48Spatrick #include "llvm/Support/JSON.h"
1709467b48Spatrick
1809467b48Spatrick #define DEBUG_TYPE "dwarfdump"
1909467b48Spatrick using namespace llvm;
20097a140dSpatrick using namespace llvm::dwarfdump;
21097a140dSpatrick using namespace llvm::object;
2209467b48Spatrick
2373471bf0Spatrick namespace {
2409467b48Spatrick /// This represents the number of categories of debug location coverage being
2509467b48Spatrick /// calculated. The first category is the number of variables with 0% location
2609467b48Spatrick /// coverage, but the last category is the number of variables with 100%
2709467b48Spatrick /// location coverage.
2809467b48Spatrick constexpr int NumOfCoverageCategories = 12;
2909467b48Spatrick
3073471bf0Spatrick /// This is used for zero location coverage bucket.
3173471bf0Spatrick constexpr unsigned ZeroCoverageBucket = 0;
3273471bf0Spatrick
33*d415bd75Srobert /// The UINT64_MAX is used as an indication of the overflow.
34*d415bd75Srobert constexpr uint64_t OverflowValue = std::numeric_limits<uint64_t>::max();
35*d415bd75Srobert
3673471bf0Spatrick /// This represents variables DIE offsets.
3773471bf0Spatrick using AbstractOriginVarsTy = llvm::SmallVector<uint64_t>;
3873471bf0Spatrick /// This maps function DIE offset to its variables.
3973471bf0Spatrick using AbstractOriginVarsTyMap = llvm::DenseMap<uint64_t, AbstractOriginVarsTy>;
4073471bf0Spatrick /// This represents function DIE offsets containing an abstract_origin.
4173471bf0Spatrick using FunctionsWithAbstractOriginTy = llvm::SmallVector<uint64_t>;
4273471bf0Spatrick
43*d415bd75Srobert /// This represents a data type for the stats and it helps us to
44*d415bd75Srobert /// detect an overflow.
45*d415bd75Srobert /// NOTE: This can be implemented as a template if there is an another type
46*d415bd75Srobert /// needing this.
47*d415bd75Srobert struct SaturatingUINT64 {
48*d415bd75Srobert /// Number that represents the stats.
49*d415bd75Srobert uint64_t Value;
50*d415bd75Srobert
SaturatingUINT64__anon696fdd2d0111::SaturatingUINT6451*d415bd75Srobert SaturatingUINT64(uint64_t Value_) : Value(Value_) {}
52*d415bd75Srobert
operator ++__anon696fdd2d0111::SaturatingUINT6453*d415bd75Srobert void operator++(int) { return *this += 1; }
operator +=__anon696fdd2d0111::SaturatingUINT6454*d415bd75Srobert void operator+=(uint64_t Value_) {
55*d415bd75Srobert if (Value != OverflowValue) {
56*d415bd75Srobert if (Value < OverflowValue - Value_)
57*d415bd75Srobert Value += Value_;
58*d415bd75Srobert else
59*d415bd75Srobert Value = OverflowValue;
60*d415bd75Srobert }
61*d415bd75Srobert }
62*d415bd75Srobert };
63*d415bd75Srobert
64*d415bd75Srobert /// Utility struct to store the full location of a DIE - its CU and offset.
65*d415bd75Srobert struct DIELocation {
66*d415bd75Srobert DWARFUnit *DwUnit;
67*d415bd75Srobert uint64_t DIEOffset;
DIELocation__anon696fdd2d0111::DIELocation68*d415bd75Srobert DIELocation(DWARFUnit *_DwUnit, uint64_t _DIEOffset)
69*d415bd75Srobert : DwUnit(_DwUnit), DIEOffset(_DIEOffset) {}
70*d415bd75Srobert };
71*d415bd75Srobert /// This represents DWARF locations of CrossCU referencing DIEs.
72*d415bd75Srobert using CrossCUReferencingDIELocationTy = llvm::SmallVector<DIELocation>;
73*d415bd75Srobert
74*d415bd75Srobert /// This maps function DIE offset to its DWARF CU.
75*d415bd75Srobert using FunctionDIECUTyMap = llvm::DenseMap<uint64_t, DWARFUnit *>;
76*d415bd75Srobert
7709467b48Spatrick /// Holds statistics for one function (or other entity that has a PC range and
7809467b48Spatrick /// contains variables, such as a compile unit).
7909467b48Spatrick struct PerFunctionStats {
8009467b48Spatrick /// Number of inlined instances of this function.
81*d415bd75Srobert uint64_t NumFnInlined = 0;
82097a140dSpatrick /// Number of out-of-line instances of this function.
83*d415bd75Srobert uint64_t NumFnOutOfLine = 0;
8409467b48Spatrick /// Number of inlined instances that have abstract origins.
85*d415bd75Srobert uint64_t NumAbstractOrigins = 0;
8609467b48Spatrick /// Number of variables and parameters with location across all inlined
8709467b48Spatrick /// instances.
88*d415bd75Srobert uint64_t TotalVarWithLoc = 0;
8909467b48Spatrick /// Number of constants with location across all inlined instances.
90*d415bd75Srobert uint64_t ConstantMembers = 0;
91097a140dSpatrick /// Number of arificial variables, parameters or members across all instances.
92*d415bd75Srobert uint64_t NumArtificial = 0;
9309467b48Spatrick /// List of all Variables and parameters in this function.
9409467b48Spatrick StringSet<> VarsInFunction;
9509467b48Spatrick /// Compile units also cover a PC range, but have this flag set to false.
9609467b48Spatrick bool IsFunction = false;
9709467b48Spatrick /// Function has source location information.
9809467b48Spatrick bool HasSourceLocation = false;
9909467b48Spatrick /// Number of function parameters.
100*d415bd75Srobert uint64_t NumParams = 0;
10109467b48Spatrick /// Number of function parameters with source location.
102*d415bd75Srobert uint64_t NumParamSourceLocations = 0;
10309467b48Spatrick /// Number of function parameters with type.
104*d415bd75Srobert uint64_t NumParamTypes = 0;
10509467b48Spatrick /// Number of function parameters with a DW_AT_location.
106*d415bd75Srobert uint64_t NumParamLocations = 0;
107097a140dSpatrick /// Number of local variables.
108*d415bd75Srobert uint64_t NumLocalVars = 0;
109097a140dSpatrick /// Number of local variables with source location.
110*d415bd75Srobert uint64_t NumLocalVarSourceLocations = 0;
111097a140dSpatrick /// Number of local variables with type.
112*d415bd75Srobert uint64_t NumLocalVarTypes = 0;
113097a140dSpatrick /// Number of local variables with DW_AT_location.
114*d415bd75Srobert uint64_t NumLocalVarLocations = 0;
11509467b48Spatrick };
11609467b48Spatrick
11709467b48Spatrick /// Holds accumulated global statistics about DIEs.
11809467b48Spatrick struct GlobalStats {
11909467b48Spatrick /// Total number of PC range bytes covered by DW_AT_locations.
120*d415bd75Srobert SaturatingUINT64 TotalBytesCovered = 0;
12173471bf0Spatrick /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
122*d415bd75Srobert SaturatingUINT64 ScopeBytesCovered = 0;
12309467b48Spatrick /// Total number of PC range bytes in each variable's enclosing scope.
124*d415bd75Srobert SaturatingUINT64 ScopeBytes = 0;
12509467b48Spatrick /// Total number of PC range bytes covered by DW_AT_locations with
12609467b48Spatrick /// the debug entry values (DW_OP_entry_value).
127*d415bd75Srobert SaturatingUINT64 ScopeEntryValueBytesCovered = 0;
12809467b48Spatrick /// Total number of PC range bytes covered by DW_AT_locations of
12909467b48Spatrick /// formal parameters.
130*d415bd75Srobert SaturatingUINT64 ParamScopeBytesCovered = 0;
131097a140dSpatrick /// Total number of PC range bytes in each parameter's enclosing scope.
132*d415bd75Srobert SaturatingUINT64 ParamScopeBytes = 0;
13309467b48Spatrick /// Total number of PC range bytes covered by DW_AT_locations with
13409467b48Spatrick /// the debug entry values (DW_OP_entry_value) (only for parameters).
135*d415bd75Srobert SaturatingUINT64 ParamScopeEntryValueBytesCovered = 0;
13609467b48Spatrick /// Total number of PC range bytes covered by DW_AT_locations (only for local
13709467b48Spatrick /// variables).
138*d415bd75Srobert SaturatingUINT64 LocalVarScopeBytesCovered = 0;
139097a140dSpatrick /// Total number of PC range bytes in each local variable's enclosing scope.
140*d415bd75Srobert SaturatingUINT64 LocalVarScopeBytes = 0;
14109467b48Spatrick /// Total number of PC range bytes covered by DW_AT_locations with
14209467b48Spatrick /// the debug entry values (DW_OP_entry_value) (only for local variables).
143*d415bd75Srobert SaturatingUINT64 LocalVarScopeEntryValueBytesCovered = 0;
14409467b48Spatrick /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
145*d415bd75Srobert SaturatingUINT64 CallSiteEntries = 0;
14609467b48Spatrick /// Total number of call site DIEs (DW_TAG_call_site).
147*d415bd75Srobert SaturatingUINT64 CallSiteDIEs = 0;
14809467b48Spatrick /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
149*d415bd75Srobert SaturatingUINT64 CallSiteParamDIEs = 0;
15009467b48Spatrick /// Total byte size of concrete functions. This byte size includes
15109467b48Spatrick /// inline functions contained in the concrete functions.
152*d415bd75Srobert SaturatingUINT64 FunctionSize = 0;
15309467b48Spatrick /// Total byte size of inlined functions. This is the total number of bytes
15409467b48Spatrick /// for the top inline functions within concrete functions. This can help
15509467b48Spatrick /// tune the inline settings when compiling to match user expectations.
156*d415bd75Srobert SaturatingUINT64 InlineFunctionSize = 0;
15709467b48Spatrick };
15809467b48Spatrick
15909467b48Spatrick /// Holds accumulated debug location statistics about local variables and
16009467b48Spatrick /// formal parameters.
16109467b48Spatrick struct LocationStats {
16209467b48Spatrick /// Map the scope coverage decile to the number of variables in the decile.
16309467b48Spatrick /// The first element of the array (at the index zero) represents the number
16409467b48Spatrick /// of variables with the no debug location at all, but the last element
16509467b48Spatrick /// in the vector represents the number of fully covered variables within
16609467b48Spatrick /// its scope.
167*d415bd75Srobert std::vector<SaturatingUINT64> VarParamLocStats{
168*d415bd75Srobert std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
16909467b48Spatrick /// Map non debug entry values coverage.
170*d415bd75Srobert std::vector<SaturatingUINT64> VarParamNonEntryValLocStats{
171*d415bd75Srobert std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
17209467b48Spatrick /// The debug location statistics for formal parameters.
173*d415bd75Srobert std::vector<SaturatingUINT64> ParamLocStats{
174*d415bd75Srobert std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
17509467b48Spatrick /// Map non debug entry values coverage for formal parameters.
176*d415bd75Srobert std::vector<SaturatingUINT64> ParamNonEntryValLocStats{
177*d415bd75Srobert std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
17809467b48Spatrick /// The debug location statistics for local variables.
179*d415bd75Srobert std::vector<SaturatingUINT64> LocalVarLocStats{
180*d415bd75Srobert std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
18109467b48Spatrick /// Map non debug entry values coverage for local variables.
182*d415bd75Srobert std::vector<SaturatingUINT64> LocalVarNonEntryValLocStats{
183*d415bd75Srobert std::vector<SaturatingUINT64>(NumOfCoverageCategories, 0)};
18409467b48Spatrick /// Total number of local variables and function parameters processed.
185*d415bd75Srobert SaturatingUINT64 NumVarParam = 0;
18609467b48Spatrick /// Total number of formal parameters processed.
187*d415bd75Srobert SaturatingUINT64 NumParam = 0;
18809467b48Spatrick /// Total number of local variables processed.
189*d415bd75Srobert SaturatingUINT64 NumVar = 0;
19009467b48Spatrick };
191097a140dSpatrick } // namespace
19209467b48Spatrick
19309467b48Spatrick /// Collect debug location statistics for one DIE.
collectLocStats(uint64_t ScopeBytesCovered,uint64_t BytesInScope,std::vector<SaturatingUINT64> & VarParamLocStats,std::vector<SaturatingUINT64> & ParamLocStats,std::vector<SaturatingUINT64> & LocalVarLocStats,bool IsParam,bool IsLocalVar)19473471bf0Spatrick static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
195*d415bd75Srobert std::vector<SaturatingUINT64> &VarParamLocStats,
196*d415bd75Srobert std::vector<SaturatingUINT64> &ParamLocStats,
197*d415bd75Srobert std::vector<SaturatingUINT64> &LocalVarLocStats,
198097a140dSpatrick bool IsParam, bool IsLocalVar) {
19973471bf0Spatrick auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
20009467b48Spatrick // No debug location at all for the variable.
20173471bf0Spatrick if (ScopeBytesCovered == 0)
20209467b48Spatrick return 0;
20309467b48Spatrick // Fully covered variable within its scope.
20473471bf0Spatrick if (ScopeBytesCovered >= BytesInScope)
20509467b48Spatrick return NumOfCoverageCategories - 1;
20609467b48Spatrick // Get covered range (e.g. 20%-29%).
20773471bf0Spatrick unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
20809467b48Spatrick LocBucket /= 10;
20909467b48Spatrick return LocBucket + 1;
21009467b48Spatrick };
21109467b48Spatrick
21209467b48Spatrick unsigned CoverageBucket = getCoverageBucket();
21373471bf0Spatrick
214*d415bd75Srobert VarParamLocStats[CoverageBucket].Value++;
21509467b48Spatrick if (IsParam)
216*d415bd75Srobert ParamLocStats[CoverageBucket].Value++;
21709467b48Spatrick else if (IsLocalVar)
218*d415bd75Srobert LocalVarLocStats[CoverageBucket].Value++;
219097a140dSpatrick }
22073471bf0Spatrick
221097a140dSpatrick /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
222097a140dSpatrick /// and DeclLine. The identifier aims to be unique for any unique entities,
223097a140dSpatrick /// but keeping the same among different instances of the same entity.
constructDieID(DWARFDie Die,StringRef Prefix=StringRef ())224097a140dSpatrick static std::string constructDieID(DWARFDie Die,
225097a140dSpatrick StringRef Prefix = StringRef()) {
226097a140dSpatrick std::string IDStr;
227097a140dSpatrick llvm::raw_string_ostream ID(IDStr);
228097a140dSpatrick ID << Prefix
229097a140dSpatrick << Die.getName(DINameKind::LinkageName);
230097a140dSpatrick
231097a140dSpatrick // Prefix + Name is enough for local variables and parameters.
232097a140dSpatrick if (!Prefix.empty() && !Prefix.equals("g"))
233097a140dSpatrick return ID.str();
234097a140dSpatrick
235097a140dSpatrick auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
236097a140dSpatrick std::string File;
237097a140dSpatrick if (DeclFile) {
238097a140dSpatrick DWARFUnit *U = Die.getDwarfUnit();
239097a140dSpatrick if (const auto *LT = U->getContext().getLineTableForUnit(U))
240097a140dSpatrick if (LT->getFileNameByIndex(
241097a140dSpatrick dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
242097a140dSpatrick DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
243097a140dSpatrick File = std::string(sys::path::filename(File));
244097a140dSpatrick }
245097a140dSpatrick ID << ":" << (File.empty() ? "/" : File);
246097a140dSpatrick ID << ":"
247097a140dSpatrick << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
248097a140dSpatrick return ID.str();
24909467b48Spatrick }
25009467b48Spatrick
25173471bf0Spatrick /// Return the number of bytes in the overlap of ranges A and B.
calculateOverlap(DWARFAddressRange A,DWARFAddressRange B)25273471bf0Spatrick static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
25373471bf0Spatrick uint64_t Lower = std::max(A.LowPC, B.LowPC);
25473471bf0Spatrick uint64_t Upper = std::min(A.HighPC, B.HighPC);
25573471bf0Spatrick if (Lower >= Upper)
25673471bf0Spatrick return 0;
25773471bf0Spatrick return Upper - Lower;
25873471bf0Spatrick }
25973471bf0Spatrick
26009467b48Spatrick /// Collect debug info quality metrics for one DIE.
collectStatsForDie(DWARFDie Die,const std::string & FnPrefix,const std::string & VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTy * AbstractOriginVariables)26173471bf0Spatrick static void collectStatsForDie(DWARFDie Die, const std::string &FnPrefix,
26273471bf0Spatrick const std::string &VarPrefix,
26373471bf0Spatrick uint64_t BytesInScope, uint32_t InlineDepth,
26409467b48Spatrick StringMap<PerFunctionStats> &FnStatMap,
26509467b48Spatrick GlobalStats &GlobalStats,
26673471bf0Spatrick LocationStats &LocStats,
26773471bf0Spatrick AbstractOriginVarsTy *AbstractOriginVariables) {
26873471bf0Spatrick const dwarf::Tag Tag = Die.getTag();
26973471bf0Spatrick // Skip CU node.
27073471bf0Spatrick if (Tag == dwarf::DW_TAG_compile_unit)
27173471bf0Spatrick return;
27273471bf0Spatrick
27309467b48Spatrick bool HasLoc = false;
27409467b48Spatrick bool HasSrcLoc = false;
27509467b48Spatrick bool HasType = false;
27673471bf0Spatrick uint64_t TotalBytesCovered = 0;
27773471bf0Spatrick uint64_t ScopeBytesCovered = 0;
27809467b48Spatrick uint64_t BytesEntryValuesCovered = 0;
27909467b48Spatrick auto &FnStats = FnStatMap[FnPrefix];
28073471bf0Spatrick bool IsParam = Tag == dwarf::DW_TAG_formal_parameter;
28173471bf0Spatrick bool IsLocalVar = Tag == dwarf::DW_TAG_variable;
28273471bf0Spatrick bool IsConstantMember = Tag == dwarf::DW_TAG_member &&
283097a140dSpatrick Die.find(dwarf::DW_AT_const_value);
28409467b48Spatrick
28573471bf0Spatrick // For zero covered inlined variables the locstats will be
28673471bf0Spatrick // calculated later.
28773471bf0Spatrick bool DeferLocStats = false;
28873471bf0Spatrick
28973471bf0Spatrick if (Tag == dwarf::DW_TAG_call_site || Tag == dwarf::DW_TAG_GNU_call_site) {
29009467b48Spatrick GlobalStats.CallSiteDIEs++;
29109467b48Spatrick return;
29209467b48Spatrick }
29309467b48Spatrick
29473471bf0Spatrick if (Tag == dwarf::DW_TAG_call_site_parameter ||
29573471bf0Spatrick Tag == dwarf::DW_TAG_GNU_call_site_parameter) {
29609467b48Spatrick GlobalStats.CallSiteParamDIEs++;
29709467b48Spatrick return;
29809467b48Spatrick }
29909467b48Spatrick
300097a140dSpatrick if (!IsParam && !IsLocalVar && !IsConstantMember) {
30109467b48Spatrick // Not a variable or constant member.
30209467b48Spatrick return;
30309467b48Spatrick }
30409467b48Spatrick
305097a140dSpatrick // Ignore declarations of global variables.
306097a140dSpatrick if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
307097a140dSpatrick return;
308097a140dSpatrick
30909467b48Spatrick if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
31009467b48Spatrick Die.findRecursively(dwarf::DW_AT_decl_line))
31109467b48Spatrick HasSrcLoc = true;
31209467b48Spatrick
31309467b48Spatrick if (Die.findRecursively(dwarf::DW_AT_type))
31409467b48Spatrick HasType = true;
31509467b48Spatrick
31673471bf0Spatrick if (Die.find(dwarf::DW_AT_abstract_origin)) {
31773471bf0Spatrick if (Die.find(dwarf::DW_AT_location) || Die.find(dwarf::DW_AT_const_value)) {
31873471bf0Spatrick if (AbstractOriginVariables) {
31973471bf0Spatrick auto Offset = Die.find(dwarf::DW_AT_abstract_origin);
32073471bf0Spatrick // Do not track this variable any more, since it has location
32173471bf0Spatrick // coverage.
32273471bf0Spatrick llvm::erase_value(*AbstractOriginVariables, (*Offset).getRawUValue());
32373471bf0Spatrick }
32473471bf0Spatrick } else {
32573471bf0Spatrick // The locstats will be handled at the end of
32673471bf0Spatrick // the collectStatsRecursive().
32773471bf0Spatrick DeferLocStats = true;
32873471bf0Spatrick }
32973471bf0Spatrick }
33073471bf0Spatrick
33109467b48Spatrick auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
33209467b48Spatrick DWARFUnit *U = Die.getDwarfUnit();
33309467b48Spatrick DataExtractor Data(toStringRef(D),
33409467b48Spatrick Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
335097a140dSpatrick DWARFExpression Expression(Data, U->getAddressByteSize(),
336097a140dSpatrick U->getFormParams().Format);
33709467b48Spatrick // Consider the expression containing the DW_OP_entry_value as
33809467b48Spatrick // an entry value.
339*d415bd75Srobert return llvm::any_of(Expression, [](const DWARFExpression::Operation &Op) {
34009467b48Spatrick return Op.getCode() == dwarf::DW_OP_entry_value ||
34109467b48Spatrick Op.getCode() == dwarf::DW_OP_GNU_entry_value;
34209467b48Spatrick });
34309467b48Spatrick };
34409467b48Spatrick
34509467b48Spatrick if (Die.find(dwarf::DW_AT_const_value)) {
34609467b48Spatrick // This catches constant members *and* variables.
34709467b48Spatrick HasLoc = true;
34873471bf0Spatrick ScopeBytesCovered = BytesInScope;
34973471bf0Spatrick TotalBytesCovered = BytesInScope;
35009467b48Spatrick } else {
35109467b48Spatrick // Handle variables and function arguments.
35209467b48Spatrick Expected<std::vector<DWARFLocationExpression>> Loc =
35309467b48Spatrick Die.getLocations(dwarf::DW_AT_location);
35409467b48Spatrick if (!Loc) {
35509467b48Spatrick consumeError(Loc.takeError());
35609467b48Spatrick } else {
35709467b48Spatrick HasLoc = true;
35809467b48Spatrick // Get PC coverage.
35909467b48Spatrick auto Default = find_if(
36009467b48Spatrick *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
36109467b48Spatrick if (Default != Loc->end()) {
36209467b48Spatrick // Assume the entire range is covered by a single location.
36373471bf0Spatrick ScopeBytesCovered = BytesInScope;
36473471bf0Spatrick TotalBytesCovered = BytesInScope;
36509467b48Spatrick } else {
36673471bf0Spatrick // Caller checks this Expected result already, it cannot fail.
36773471bf0Spatrick auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
36809467b48Spatrick for (auto Entry : *Loc) {
36973471bf0Spatrick TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
37073471bf0Spatrick uint64_t ScopeBytesCoveredByEntry = 0;
37173471bf0Spatrick // Calculate how many bytes of the parent scope this entry covers.
37273471bf0Spatrick // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
37373471bf0Spatrick // address ranges defined by the bounded location descriptions of a
37473471bf0Spatrick // location list may overlap". So in theory a variable can have
37573471bf0Spatrick // multiple simultaneous locations, which would make this calculation
37673471bf0Spatrick // misleading because we will count the overlapped areas
37773471bf0Spatrick // twice. However, clang does not currently emit DWARF like this.
37873471bf0Spatrick for (DWARFAddressRange R : ScopeRanges) {
37973471bf0Spatrick ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
38073471bf0Spatrick }
38173471bf0Spatrick ScopeBytesCovered += ScopeBytesCoveredByEntry;
38209467b48Spatrick if (IsEntryValue(Entry.Expr))
38373471bf0Spatrick BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
38409467b48Spatrick }
38509467b48Spatrick }
38609467b48Spatrick }
38709467b48Spatrick }
38809467b48Spatrick
38909467b48Spatrick // Calculate the debug location statistics.
39073471bf0Spatrick if (BytesInScope && !DeferLocStats) {
391*d415bd75Srobert LocStats.NumVarParam.Value++;
39209467b48Spatrick if (IsParam)
393*d415bd75Srobert LocStats.NumParam.Value++;
39409467b48Spatrick else if (IsLocalVar)
395*d415bd75Srobert LocStats.NumVar.Value++;
39609467b48Spatrick
39773471bf0Spatrick collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
398097a140dSpatrick LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
39909467b48Spatrick IsLocalVar);
40009467b48Spatrick // Non debug entry values coverage statistics.
40173471bf0Spatrick collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
40209467b48Spatrick LocStats.VarParamNonEntryValLocStats,
40309467b48Spatrick LocStats.ParamNonEntryValLocStats,
404097a140dSpatrick LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
40509467b48Spatrick }
40609467b48Spatrick
40709467b48Spatrick // Collect PC range coverage data.
40809467b48Spatrick if (DWARFDie D =
40909467b48Spatrick Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
41009467b48Spatrick Die = D;
411097a140dSpatrick
412097a140dSpatrick std::string VarID = constructDieID(Die, VarPrefix);
413097a140dSpatrick FnStats.VarsInFunction.insert(VarID);
414097a140dSpatrick
41573471bf0Spatrick GlobalStats.TotalBytesCovered += TotalBytesCovered;
41609467b48Spatrick if (BytesInScope) {
41773471bf0Spatrick GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
41809467b48Spatrick GlobalStats.ScopeBytes += BytesInScope;
41909467b48Spatrick GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
42009467b48Spatrick if (IsParam) {
42173471bf0Spatrick GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
42209467b48Spatrick GlobalStats.ParamScopeBytes += BytesInScope;
42309467b48Spatrick GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
42409467b48Spatrick } else if (IsLocalVar) {
42573471bf0Spatrick GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
426097a140dSpatrick GlobalStats.LocalVarScopeBytes += BytesInScope;
427097a140dSpatrick GlobalStats.LocalVarScopeEntryValueBytesCovered +=
428097a140dSpatrick BytesEntryValuesCovered;
42909467b48Spatrick }
430*d415bd75Srobert assert(GlobalStats.ScopeBytesCovered.Value <= GlobalStats.ScopeBytes.Value);
43109467b48Spatrick }
432097a140dSpatrick
433097a140dSpatrick if (IsConstantMember) {
434097a140dSpatrick FnStats.ConstantMembers++;
435097a140dSpatrick return;
436097a140dSpatrick }
437097a140dSpatrick
438097a140dSpatrick FnStats.TotalVarWithLoc += (unsigned)HasLoc;
439097a140dSpatrick
440097a140dSpatrick if (Die.find(dwarf::DW_AT_artificial)) {
441097a140dSpatrick FnStats.NumArtificial++;
442097a140dSpatrick return;
443097a140dSpatrick }
444097a140dSpatrick
44509467b48Spatrick if (IsParam) {
44609467b48Spatrick FnStats.NumParams++;
44709467b48Spatrick if (HasType)
44809467b48Spatrick FnStats.NumParamTypes++;
44909467b48Spatrick if (HasSrcLoc)
45009467b48Spatrick FnStats.NumParamSourceLocations++;
45109467b48Spatrick if (HasLoc)
45209467b48Spatrick FnStats.NumParamLocations++;
45309467b48Spatrick } else if (IsLocalVar) {
454097a140dSpatrick FnStats.NumLocalVars++;
45509467b48Spatrick if (HasType)
456097a140dSpatrick FnStats.NumLocalVarTypes++;
45709467b48Spatrick if (HasSrcLoc)
458097a140dSpatrick FnStats.NumLocalVarSourceLocations++;
45909467b48Spatrick if (HasLoc)
460097a140dSpatrick FnStats.NumLocalVarLocations++;
46109467b48Spatrick }
46209467b48Spatrick }
46309467b48Spatrick
46473471bf0Spatrick /// Recursively collect variables from subprogram with DW_AT_inline attribute.
collectAbstractOriginFnInfo(DWARFDie Die,uint64_t SPOffset,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo)46573471bf0Spatrick static void collectAbstractOriginFnInfo(
46673471bf0Spatrick DWARFDie Die, uint64_t SPOffset,
467*d415bd75Srobert AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
468*d415bd75Srobert AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo) {
46973471bf0Spatrick DWARFDie Child = Die.getFirstChild();
47073471bf0Spatrick while (Child) {
47173471bf0Spatrick const dwarf::Tag ChildTag = Child.getTag();
47273471bf0Spatrick if (ChildTag == dwarf::DW_TAG_formal_parameter ||
473*d415bd75Srobert ChildTag == dwarf::DW_TAG_variable) {
47473471bf0Spatrick GlobalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
475*d415bd75Srobert LocalAbstractOriginFnInfo[SPOffset].push_back(Child.getOffset());
476*d415bd75Srobert } else if (ChildTag == dwarf::DW_TAG_lexical_block)
477*d415bd75Srobert collectAbstractOriginFnInfo(Child, SPOffset, GlobalAbstractOriginFnInfo,
478*d415bd75Srobert LocalAbstractOriginFnInfo);
47973471bf0Spatrick Child = Child.getSibling();
48073471bf0Spatrick }
48173471bf0Spatrick }
48273471bf0Spatrick
48309467b48Spatrick /// Recursively collect debug info quality metrics.
collectStatsRecursive(DWARFDie Die,std::string FnPrefix,std::string VarPrefix,uint64_t BytesInScope,uint32_t InlineDepth,StringMap<PerFunctionStats> & FnStatMap,GlobalStats & GlobalStats,LocationStats & LocStats,FunctionDIECUTyMap & AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed,AbstractOriginVarsTy * AbstractOriginVarsPtr=nullptr)48473471bf0Spatrick static void collectStatsRecursive(
48573471bf0Spatrick DWARFDie Die, std::string FnPrefix, std::string VarPrefix,
48673471bf0Spatrick uint64_t BytesInScope, uint32_t InlineDepth,
48773471bf0Spatrick StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats,
488*d415bd75Srobert LocationStats &LocStats, FunctionDIECUTyMap &AbstractOriginFnCUs,
48973471bf0Spatrick AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
490*d415bd75Srobert AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
49173471bf0Spatrick FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed,
49273471bf0Spatrick AbstractOriginVarsTy *AbstractOriginVarsPtr = nullptr) {
49373471bf0Spatrick // Skip NULL nodes.
49473471bf0Spatrick if (Die.isNULL())
49573471bf0Spatrick return;
49673471bf0Spatrick
49709467b48Spatrick const dwarf::Tag Tag = Die.getTag();
498097a140dSpatrick // Skip function types.
499097a140dSpatrick if (Tag == dwarf::DW_TAG_subroutine_type)
500097a140dSpatrick return;
501097a140dSpatrick
502097a140dSpatrick // Handle any kind of lexical scope.
503*d415bd75Srobert const bool HasAbstractOrigin =
504*d415bd75Srobert Die.find(dwarf::DW_AT_abstract_origin) != std::nullopt;
50509467b48Spatrick const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
50609467b48Spatrick const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
50709467b48Spatrick const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
50873471bf0Spatrick // We want to know how many variables (with abstract_origin) don't have
50973471bf0Spatrick // location info.
51073471bf0Spatrick const bool IsCandidateForZeroLocCovTracking =
51173471bf0Spatrick (IsInlinedFunction || (IsFunction && HasAbstractOrigin));
51209467b48Spatrick
51373471bf0Spatrick AbstractOriginVarsTy AbstractOriginVars;
51473471bf0Spatrick
51573471bf0Spatrick // Get the vars of the inlined fn, so the locstats
51673471bf0Spatrick // reports the missing vars (with coverage 0%).
51773471bf0Spatrick if (IsCandidateForZeroLocCovTracking) {
51873471bf0Spatrick auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin);
51973471bf0Spatrick if (OffsetFn) {
52073471bf0Spatrick uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue();
521*d415bd75Srobert if (LocalAbstractOriginFnInfo.count(OffsetOfInlineFnCopy)) {
522*d415bd75Srobert AbstractOriginVars = LocalAbstractOriginFnInfo[OffsetOfInlineFnCopy];
52373471bf0Spatrick AbstractOriginVarsPtr = &AbstractOriginVars;
52473471bf0Spatrick } else {
525*d415bd75Srobert // This means that the DW_AT_inline fn copy is out of order
526*d415bd75Srobert // or that the abstract_origin references another CU,
52773471bf0Spatrick // so this abstract origin instance will be processed later.
52873471bf0Spatrick FnsWithAbstractOriginToBeProcessed.push_back(Die.getOffset());
52973471bf0Spatrick AbstractOriginVarsPtr = nullptr;
53073471bf0Spatrick }
53173471bf0Spatrick }
53273471bf0Spatrick }
53373471bf0Spatrick
53473471bf0Spatrick if (IsFunction || IsInlinedFunction || IsBlock) {
53509467b48Spatrick // Reset VarPrefix when entering a new function.
53673471bf0Spatrick if (IsFunction || IsInlinedFunction)
53709467b48Spatrick VarPrefix = "v";
53809467b48Spatrick
53909467b48Spatrick // Ignore forward declarations.
54009467b48Spatrick if (Die.find(dwarf::DW_AT_declaration))
54109467b48Spatrick return;
54209467b48Spatrick
54309467b48Spatrick // Check for call sites.
54409467b48Spatrick if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
54509467b48Spatrick GlobalStats.CallSiteEntries++;
54609467b48Spatrick
54709467b48Spatrick // PC Ranges.
54809467b48Spatrick auto RangesOrError = Die.getAddressRanges();
54909467b48Spatrick if (!RangesOrError) {
55009467b48Spatrick llvm::consumeError(RangesOrError.takeError());
55109467b48Spatrick return;
55209467b48Spatrick }
55309467b48Spatrick
55409467b48Spatrick auto Ranges = RangesOrError.get();
55509467b48Spatrick uint64_t BytesInThisScope = 0;
55609467b48Spatrick for (auto Range : Ranges)
55709467b48Spatrick BytesInThisScope += Range.HighPC - Range.LowPC;
55809467b48Spatrick
55909467b48Spatrick // Count the function.
56009467b48Spatrick if (!IsBlock) {
56173471bf0Spatrick // Skip over abstract origins, but collect variables
56273471bf0Spatrick // from it so it can be used for location statistics
56373471bf0Spatrick // for inlined instancies.
56473471bf0Spatrick if (Die.find(dwarf::DW_AT_inline)) {
56573471bf0Spatrick uint64_t SPOffset = Die.getOffset();
566*d415bd75Srobert AbstractOriginFnCUs[SPOffset] = Die.getDwarfUnit();
567*d415bd75Srobert collectAbstractOriginFnInfo(Die, SPOffset, GlobalAbstractOriginFnInfo,
568*d415bd75Srobert LocalAbstractOriginFnInfo);
56909467b48Spatrick return;
57073471bf0Spatrick }
57173471bf0Spatrick
572097a140dSpatrick std::string FnID = constructDieID(Die);
573097a140dSpatrick // We've seen an instance of this function.
574097a140dSpatrick auto &FnStats = FnStatMap[FnID];
575097a140dSpatrick FnStats.IsFunction = true;
57609467b48Spatrick if (IsInlinedFunction) {
57709467b48Spatrick FnStats.NumFnInlined++;
57809467b48Spatrick if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
57909467b48Spatrick FnStats.NumAbstractOrigins++;
580097a140dSpatrick } else {
581097a140dSpatrick FnStats.NumFnOutOfLine++;
58209467b48Spatrick }
58309467b48Spatrick if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
58409467b48Spatrick Die.findRecursively(dwarf::DW_AT_decl_line))
58509467b48Spatrick FnStats.HasSourceLocation = true;
586097a140dSpatrick // Update function prefix.
587097a140dSpatrick FnPrefix = FnID;
58809467b48Spatrick }
58909467b48Spatrick
59009467b48Spatrick if (BytesInThisScope) {
59109467b48Spatrick BytesInScope = BytesInThisScope;
59209467b48Spatrick if (IsFunction)
59309467b48Spatrick GlobalStats.FunctionSize += BytesInThisScope;
59409467b48Spatrick else if (IsInlinedFunction && InlineDepth == 0)
59509467b48Spatrick GlobalStats.InlineFunctionSize += BytesInThisScope;
59609467b48Spatrick }
59709467b48Spatrick } else {
59809467b48Spatrick // Not a scope, visit the Die itself. It could be a variable.
59909467b48Spatrick collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
60073471bf0Spatrick FnStatMap, GlobalStats, LocStats, AbstractOriginVarsPtr);
60109467b48Spatrick }
60209467b48Spatrick
60309467b48Spatrick // Set InlineDepth correctly for child recursion
60409467b48Spatrick if (IsFunction)
60509467b48Spatrick InlineDepth = 0;
60609467b48Spatrick else if (IsInlinedFunction)
60709467b48Spatrick ++InlineDepth;
60809467b48Spatrick
60909467b48Spatrick // Traverse children.
61009467b48Spatrick unsigned LexicalBlockIndex = 0;
611097a140dSpatrick unsigned FormalParameterIndex = 0;
61209467b48Spatrick DWARFDie Child = Die.getFirstChild();
61309467b48Spatrick while (Child) {
61409467b48Spatrick std::string ChildVarPrefix = VarPrefix;
61509467b48Spatrick if (Child.getTag() == dwarf::DW_TAG_lexical_block)
61609467b48Spatrick ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
617097a140dSpatrick if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
618097a140dSpatrick ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
61909467b48Spatrick
62073471bf0Spatrick collectStatsRecursive(
62173471bf0Spatrick Child, FnPrefix, ChildVarPrefix, BytesInScope, InlineDepth, FnStatMap,
622*d415bd75Srobert GlobalStats, LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
623*d415bd75Srobert LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed,
624*d415bd75Srobert AbstractOriginVarsPtr);
62509467b48Spatrick Child = Child.getSibling();
62609467b48Spatrick }
62773471bf0Spatrick
62873471bf0Spatrick if (!IsCandidateForZeroLocCovTracking)
62973471bf0Spatrick return;
63073471bf0Spatrick
63173471bf0Spatrick // After we have processed all vars of the inlined function (or function with
63273471bf0Spatrick // an abstract_origin), we want to know how many variables have no location.
63373471bf0Spatrick for (auto Offset : AbstractOriginVars) {
63473471bf0Spatrick LocStats.NumVarParam++;
63573471bf0Spatrick LocStats.VarParamLocStats[ZeroCoverageBucket]++;
63673471bf0Spatrick auto FnDie = Die.getDwarfUnit()->getDIEForOffset(Offset);
63773471bf0Spatrick if (!FnDie)
63873471bf0Spatrick continue;
63973471bf0Spatrick auto Tag = FnDie.getTag();
64073471bf0Spatrick if (Tag == dwarf::DW_TAG_formal_parameter) {
64173471bf0Spatrick LocStats.NumParam++;
64273471bf0Spatrick LocStats.ParamLocStats[ZeroCoverageBucket]++;
64373471bf0Spatrick } else if (Tag == dwarf::DW_TAG_variable) {
64473471bf0Spatrick LocStats.NumVar++;
64573471bf0Spatrick LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
64673471bf0Spatrick }
64773471bf0Spatrick }
64809467b48Spatrick }
64909467b48Spatrick
65073471bf0Spatrick /// Print human-readable output.
65109467b48Spatrick /// \{
printDatum(json::OStream & J,const char * Key,json::Value Value)65273471bf0Spatrick static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
653*d415bd75Srobert if (Value == OverflowValue)
654*d415bd75Srobert J.attribute(Key, "overflowed");
655*d415bd75Srobert else
65673471bf0Spatrick J.attribute(Key, Value);
657*d415bd75Srobert
65809467b48Spatrick LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
65909467b48Spatrick }
660097a140dSpatrick
printLocationStats(json::OStream & J,const char * Key,std::vector<SaturatingUINT64> & LocationStats)66173471bf0Spatrick static void printLocationStats(json::OStream &J, const char *Key,
662*d415bd75Srobert std::vector<SaturatingUINT64> &LocationStats) {
663*d415bd75Srobert if (LocationStats[0].Value == OverflowValue)
664*d415bd75Srobert J.attribute((Twine(Key) +
665*d415bd75Srobert " with (0%,10%) of parent scope covered by DW_AT_location")
666*d415bd75Srobert .str(),
667*d415bd75Srobert "overflowed");
668*d415bd75Srobert else
66973471bf0Spatrick J.attribute(
670*d415bd75Srobert (Twine(Key) + " with 0% of parent scope covered by DW_AT_location")
671*d415bd75Srobert .str(),
672*d415bd75Srobert LocationStats[0].Value);
673097a140dSpatrick LLVM_DEBUG(
674097a140dSpatrick llvm::dbgs() << Key
675097a140dSpatrick << " with 0% of parent scope covered by DW_AT_location: \\"
676*d415bd75Srobert << LocationStats[0].Value << '\n');
677*d415bd75Srobert
678*d415bd75Srobert if (LocationStats[1].Value == OverflowValue)
679*d415bd75Srobert J.attribute((Twine(Key) +
680*d415bd75Srobert " with (0%,10%) of parent scope covered by DW_AT_location")
68173471bf0Spatrick .str(),
682*d415bd75Srobert "overflowed");
683*d415bd75Srobert else
684*d415bd75Srobert J.attribute((Twine(Key) +
685*d415bd75Srobert " with (0%,10%) of parent scope covered by DW_AT_location")
686*d415bd75Srobert .str(),
687*d415bd75Srobert LocationStats[1].Value);
688097a140dSpatrick LLVM_DEBUG(llvm::dbgs()
689097a140dSpatrick << Key
690097a140dSpatrick << " with (0%,10%) of parent scope covered by DW_AT_location: "
691*d415bd75Srobert << LocationStats[1].Value << '\n');
692*d415bd75Srobert
69309467b48Spatrick for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
694*d415bd75Srobert if (LocationStats[i].Value == OverflowValue)
69573471bf0Spatrick J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
696*d415bd75Srobert Twine(i * 10) +
697*d415bd75Srobert "%) of parent scope covered by DW_AT_location")
69873471bf0Spatrick .str(),
699*d415bd75Srobert "overflowed");
700*d415bd75Srobert else
701*d415bd75Srobert J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
702*d415bd75Srobert Twine(i * 10) +
703*d415bd75Srobert "%) of parent scope covered by DW_AT_location")
704*d415bd75Srobert .str(),
705*d415bd75Srobert LocationStats[i].Value);
70609467b48Spatrick LLVM_DEBUG(llvm::dbgs()
70709467b48Spatrick << Key << " with [" << (i - 1) * 10 << "%," << i * 10
708097a140dSpatrick << "%) of parent scope covered by DW_AT_location: "
709*d415bd75Srobert << LocationStats[i].Value);
71009467b48Spatrick }
711*d415bd75Srobert if (LocationStats[NumOfCoverageCategories - 1].Value == OverflowValue)
71273471bf0Spatrick J.attribute(
71373471bf0Spatrick (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
71473471bf0Spatrick .str(),
715*d415bd75Srobert "overflowed");
716*d415bd75Srobert else
717*d415bd75Srobert J.attribute(
718*d415bd75Srobert (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
719*d415bd75Srobert .str(),
720*d415bd75Srobert LocationStats[NumOfCoverageCategories - 1].Value);
721097a140dSpatrick LLVM_DEBUG(
722097a140dSpatrick llvm::dbgs() << Key
723097a140dSpatrick << " with 100% of parent scope covered by DW_AT_location: "
724*d415bd75Srobert << LocationStats[NumOfCoverageCategories - 1].Value);
72509467b48Spatrick }
726097a140dSpatrick
printSectionSizes(json::OStream & J,const SectionSizes & Sizes)72773471bf0Spatrick static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
72873471bf0Spatrick for (const auto &It : Sizes.DebugSectionSizes)
72973471bf0Spatrick J.attribute((Twine("#bytes in ") + It.first).str(), int64_t(It.second));
73073471bf0Spatrick }
73173471bf0Spatrick
73273471bf0Spatrick /// Stop tracking variables that contain abstract_origin with a location.
73373471bf0Spatrick /// This is used for out-of-order DW_AT_inline subprograms only.
updateVarsWithAbstractOriginLocCovInfo(DWARFDie FnDieWithAbstractOrigin,AbstractOriginVarsTy & AbstractOriginVars)73473471bf0Spatrick static void updateVarsWithAbstractOriginLocCovInfo(
73573471bf0Spatrick DWARFDie FnDieWithAbstractOrigin,
73673471bf0Spatrick AbstractOriginVarsTy &AbstractOriginVars) {
73773471bf0Spatrick DWARFDie Child = FnDieWithAbstractOrigin.getFirstChild();
73873471bf0Spatrick while (Child) {
73973471bf0Spatrick const dwarf::Tag ChildTag = Child.getTag();
74073471bf0Spatrick if ((ChildTag == dwarf::DW_TAG_formal_parameter ||
74173471bf0Spatrick ChildTag == dwarf::DW_TAG_variable) &&
74273471bf0Spatrick (Child.find(dwarf::DW_AT_location) ||
74373471bf0Spatrick Child.find(dwarf::DW_AT_const_value))) {
74473471bf0Spatrick auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin);
74573471bf0Spatrick if (OffsetVar)
74673471bf0Spatrick llvm::erase_value(AbstractOriginVars, (*OffsetVar).getRawUValue());
74773471bf0Spatrick } else if (ChildTag == dwarf::DW_TAG_lexical_block)
74873471bf0Spatrick updateVarsWithAbstractOriginLocCovInfo(Child, AbstractOriginVars);
74973471bf0Spatrick Child = Child.getSibling();
75073471bf0Spatrick }
75173471bf0Spatrick }
75273471bf0Spatrick
75373471bf0Spatrick /// Collect zero location coverage for inlined variables which refer to
75473471bf0Spatrick /// a DW_AT_inline copy of subprogram that is out of order in the DWARF.
75573471bf0Spatrick /// Also cover the variables of a concrete function (represented with
75673471bf0Spatrick /// the DW_TAG_subprogram) with an abstract_origin attribute.
collectZeroLocCovForVarsWithAbstractOrigin(DWARFUnit * DwUnit,GlobalStats & GlobalStats,LocationStats & LocStats,AbstractOriginVarsTyMap & LocalAbstractOriginFnInfo,FunctionsWithAbstractOriginTy & FnsWithAbstractOriginToBeProcessed)75773471bf0Spatrick static void collectZeroLocCovForVarsWithAbstractOrigin(
75873471bf0Spatrick DWARFUnit *DwUnit, GlobalStats &GlobalStats, LocationStats &LocStats,
759*d415bd75Srobert AbstractOriginVarsTyMap &LocalAbstractOriginFnInfo,
76073471bf0Spatrick FunctionsWithAbstractOriginTy &FnsWithAbstractOriginToBeProcessed) {
761*d415bd75Srobert // The next variable is used to filter out functions that have been processed,
762*d415bd75Srobert // leaving FnsWithAbstractOriginToBeProcessed with just CrossCU references.
763*d415bd75Srobert FunctionsWithAbstractOriginTy ProcessedFns;
76473471bf0Spatrick for (auto FnOffset : FnsWithAbstractOriginToBeProcessed) {
76573471bf0Spatrick DWARFDie FnDieWithAbstractOrigin = DwUnit->getDIEForOffset(FnOffset);
76673471bf0Spatrick auto FnCopy = FnDieWithAbstractOrigin.find(dwarf::DW_AT_abstract_origin);
76773471bf0Spatrick AbstractOriginVarsTy AbstractOriginVars;
76873471bf0Spatrick if (!FnCopy)
76973471bf0Spatrick continue;
770*d415bd75Srobert uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
771*d415bd75Srobert // If there is no entry within LocalAbstractOriginFnInfo for the given
772*d415bd75Srobert // FnCopyRawUValue, function isn't out-of-order in DWARF. Rather, we have
773*d415bd75Srobert // CrossCU referencing.
774*d415bd75Srobert if (!LocalAbstractOriginFnInfo.count(FnCopyRawUValue))
775*d415bd75Srobert continue;
776*d415bd75Srobert AbstractOriginVars = LocalAbstractOriginFnInfo[FnCopyRawUValue];
77773471bf0Spatrick updateVarsWithAbstractOriginLocCovInfo(FnDieWithAbstractOrigin,
77873471bf0Spatrick AbstractOriginVars);
77973471bf0Spatrick
78073471bf0Spatrick for (auto Offset : AbstractOriginVars) {
78173471bf0Spatrick LocStats.NumVarParam++;
78273471bf0Spatrick LocStats.VarParamLocStats[ZeroCoverageBucket]++;
78373471bf0Spatrick auto Tag = DwUnit->getDIEForOffset(Offset).getTag();
78473471bf0Spatrick if (Tag == dwarf::DW_TAG_formal_parameter) {
78573471bf0Spatrick LocStats.NumParam++;
78673471bf0Spatrick LocStats.ParamLocStats[ZeroCoverageBucket]++;
78773471bf0Spatrick } else if (Tag == dwarf::DW_TAG_variable) {
78873471bf0Spatrick LocStats.NumVar++;
78973471bf0Spatrick LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
79073471bf0Spatrick }
79173471bf0Spatrick }
792*d415bd75Srobert ProcessedFns.push_back(FnOffset);
793*d415bd75Srobert }
794*d415bd75Srobert for (auto ProcessedFn : ProcessedFns)
795*d415bd75Srobert llvm::erase_value(FnsWithAbstractOriginToBeProcessed, ProcessedFn);
796*d415bd75Srobert }
797*d415bd75Srobert
798*d415bd75Srobert /// Collect zero location coverage for inlined variables which refer to
799*d415bd75Srobert /// a DW_AT_inline copy of subprogram that is in a different CU.
collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(LocationStats & LocStats,FunctionDIECUTyMap AbstractOriginFnCUs,AbstractOriginVarsTyMap & GlobalAbstractOriginFnInfo,CrossCUReferencingDIELocationTy & CrossCUReferencesToBeResolved)800*d415bd75Srobert static void collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
801*d415bd75Srobert LocationStats &LocStats, FunctionDIECUTyMap AbstractOriginFnCUs,
802*d415bd75Srobert AbstractOriginVarsTyMap &GlobalAbstractOriginFnInfo,
803*d415bd75Srobert CrossCUReferencingDIELocationTy &CrossCUReferencesToBeResolved) {
804*d415bd75Srobert for (const auto &CrossCUReferenceToBeResolved :
805*d415bd75Srobert CrossCUReferencesToBeResolved) {
806*d415bd75Srobert DWARFUnit *DwUnit = CrossCUReferenceToBeResolved.DwUnit;
807*d415bd75Srobert DWARFDie FnDIEWithCrossCUReferencing =
808*d415bd75Srobert DwUnit->getDIEForOffset(CrossCUReferenceToBeResolved.DIEOffset);
809*d415bd75Srobert auto FnCopy =
810*d415bd75Srobert FnDIEWithCrossCUReferencing.find(dwarf::DW_AT_abstract_origin);
811*d415bd75Srobert if (!FnCopy)
812*d415bd75Srobert continue;
813*d415bd75Srobert uint64_t FnCopyRawUValue = (*FnCopy).getRawUValue();
814*d415bd75Srobert AbstractOriginVarsTy AbstractOriginVars =
815*d415bd75Srobert GlobalAbstractOriginFnInfo[FnCopyRawUValue];
816*d415bd75Srobert updateVarsWithAbstractOriginLocCovInfo(FnDIEWithCrossCUReferencing,
817*d415bd75Srobert AbstractOriginVars);
818*d415bd75Srobert for (auto Offset : AbstractOriginVars) {
819*d415bd75Srobert LocStats.NumVarParam++;
820*d415bd75Srobert LocStats.VarParamLocStats[ZeroCoverageBucket]++;
821*d415bd75Srobert auto Tag = (AbstractOriginFnCUs[FnCopyRawUValue])
822*d415bd75Srobert ->getDIEForOffset(Offset)
823*d415bd75Srobert .getTag();
824*d415bd75Srobert if (Tag == dwarf::DW_TAG_formal_parameter) {
825*d415bd75Srobert LocStats.NumParam++;
826*d415bd75Srobert LocStats.ParamLocStats[ZeroCoverageBucket]++;
827*d415bd75Srobert } else if (Tag == dwarf::DW_TAG_variable) {
828*d415bd75Srobert LocStats.NumVar++;
829*d415bd75Srobert LocStats.LocalVarLocStats[ZeroCoverageBucket]++;
830*d415bd75Srobert }
831*d415bd75Srobert }
83273471bf0Spatrick }
833097a140dSpatrick }
834097a140dSpatrick
83509467b48Spatrick /// \}
83609467b48Spatrick
83709467b48Spatrick /// Collect debug info quality metrics for an entire DIContext.
83809467b48Spatrick ///
83909467b48Spatrick /// Do the impossible and reduce the quality of the debug info down to a few
84009467b48Spatrick /// numbers. The idea is to condense the data into numbers that can be tracked
84109467b48Spatrick /// over time to identify trends in newer compiler versions and gauge the effect
84209467b48Spatrick /// of particular optimizations. The raw numbers themselves are not particularly
84309467b48Spatrick /// useful, only the delta between compiling the same program with different
84409467b48Spatrick /// compilers is.
collectStatsForObjectFile(ObjectFile & Obj,DWARFContext & DICtx,const Twine & Filename,raw_ostream & OS)845097a140dSpatrick bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
846097a140dSpatrick const Twine &Filename,
847097a140dSpatrick raw_ostream &OS) {
84809467b48Spatrick StringRef FormatName = Obj.getFileFormatName();
84909467b48Spatrick GlobalStats GlobalStats;
85009467b48Spatrick LocationStats LocStats;
85109467b48Spatrick StringMap<PerFunctionStats> Statistics;
852*d415bd75Srobert // This variable holds variable information for functions with
853*d415bd75Srobert // abstract_origin globally, across all CUs.
854*d415bd75Srobert AbstractOriginVarsTyMap GlobalAbstractOriginFnInfo;
855*d415bd75Srobert // This variable holds information about the CU of a function with
856*d415bd75Srobert // abstract_origin.
857*d415bd75Srobert FunctionDIECUTyMap AbstractOriginFnCUs;
858*d415bd75Srobert CrossCUReferencingDIELocationTy CrossCUReferencesToBeResolved;
85973471bf0Spatrick for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) {
86073471bf0Spatrick if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) {
861*d415bd75Srobert // This variable holds variable information for functions with
862*d415bd75Srobert // abstract_origin, but just for the current CU.
863*d415bd75Srobert AbstractOriginVarsTyMap LocalAbstractOriginFnInfo;
86473471bf0Spatrick FunctionsWithAbstractOriginTy FnsWithAbstractOriginToBeProcessed;
86573471bf0Spatrick
866*d415bd75Srobert collectStatsRecursive(
867*d415bd75Srobert CUDie, "/", "g", 0, 0, Statistics, GlobalStats, LocStats,
868*d415bd75Srobert AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
869*d415bd75Srobert LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
87073471bf0Spatrick
871*d415bd75Srobert // collectZeroLocCovForVarsWithAbstractOrigin will filter out all
872*d415bd75Srobert // out-of-order DWARF functions that have been processed within it,
873*d415bd75Srobert // leaving FnsWithAbstractOriginToBeProcessed with only CrossCU
874*d415bd75Srobert // references.
87573471bf0Spatrick collectZeroLocCovForVarsWithAbstractOrigin(
87673471bf0Spatrick CUDie.getDwarfUnit(), GlobalStats, LocStats,
877*d415bd75Srobert LocalAbstractOriginFnInfo, FnsWithAbstractOriginToBeProcessed);
878*d415bd75Srobert
879*d415bd75Srobert // Collect all CrossCU references into CrossCUReferencesToBeResolved.
880*d415bd75Srobert for (auto CrossCUReferencingDIEOffset :
881*d415bd75Srobert FnsWithAbstractOriginToBeProcessed)
882*d415bd75Srobert CrossCUReferencesToBeResolved.push_back(
883*d415bd75Srobert DIELocation(CUDie.getDwarfUnit(), CrossCUReferencingDIEOffset));
88473471bf0Spatrick }
88573471bf0Spatrick }
88609467b48Spatrick
887*d415bd75Srobert /// Resolve CrossCU references.
888*d415bd75Srobert collectZeroLocCovForVarsWithCrossCUReferencingAbstractOrigin(
889*d415bd75Srobert LocStats, AbstractOriginFnCUs, GlobalAbstractOriginFnInfo,
890*d415bd75Srobert CrossCUReferencesToBeResolved);
891*d415bd75Srobert
892097a140dSpatrick /// Collect the sizes of debug sections.
893097a140dSpatrick SectionSizes Sizes;
894097a140dSpatrick calculateSectionSizes(Obj, Sizes, Filename);
895097a140dSpatrick
89609467b48Spatrick /// The version number should be increased every time the algorithm is changed
89709467b48Spatrick /// (including bug fixes). New metrics may be added without increasing the
89809467b48Spatrick /// version.
899*d415bd75Srobert unsigned Version = 9;
900*d415bd75Srobert SaturatingUINT64 VarParamTotal = 0;
901*d415bd75Srobert SaturatingUINT64 VarParamUnique = 0;
902*d415bd75Srobert SaturatingUINT64 VarParamWithLoc = 0;
903*d415bd75Srobert SaturatingUINT64 NumFunctions = 0;
904*d415bd75Srobert SaturatingUINT64 NumInlinedFunctions = 0;
905*d415bd75Srobert SaturatingUINT64 NumFuncsWithSrcLoc = 0;
906*d415bd75Srobert SaturatingUINT64 NumAbstractOrigins = 0;
907*d415bd75Srobert SaturatingUINT64 ParamTotal = 0;
908*d415bd75Srobert SaturatingUINT64 ParamWithType = 0;
909*d415bd75Srobert SaturatingUINT64 ParamWithLoc = 0;
910*d415bd75Srobert SaturatingUINT64 ParamWithSrcLoc = 0;
911*d415bd75Srobert SaturatingUINT64 LocalVarTotal = 0;
912*d415bd75Srobert SaturatingUINT64 LocalVarWithType = 0;
913*d415bd75Srobert SaturatingUINT64 LocalVarWithSrcLoc = 0;
914*d415bd75Srobert SaturatingUINT64 LocalVarWithLoc = 0;
91509467b48Spatrick for (auto &Entry : Statistics) {
91609467b48Spatrick PerFunctionStats &Stats = Entry.getValue();
917*d415bd75Srobert uint64_t TotalVars = Stats.VarsInFunction.size() *
918097a140dSpatrick (Stats.NumFnInlined + Stats.NumFnOutOfLine);
919097a140dSpatrick // Count variables in global scope.
920097a140dSpatrick if (!Stats.IsFunction)
921097a140dSpatrick TotalVars =
922097a140dSpatrick Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
923*d415bd75Srobert uint64_t Constants = Stats.ConstantMembers;
92409467b48Spatrick VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
92509467b48Spatrick VarParamTotal += TotalVars;
92609467b48Spatrick VarParamUnique += Stats.VarsInFunction.size();
92709467b48Spatrick LLVM_DEBUG(for (auto &V
92809467b48Spatrick : Stats.VarsInFunction) llvm::dbgs()
92909467b48Spatrick << Entry.getKey() << ": " << V.getKey() << "\n");
93009467b48Spatrick NumFunctions += Stats.IsFunction;
93109467b48Spatrick NumFuncsWithSrcLoc += Stats.HasSourceLocation;
93209467b48Spatrick NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
93309467b48Spatrick NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
93409467b48Spatrick ParamTotal += Stats.NumParams;
93509467b48Spatrick ParamWithType += Stats.NumParamTypes;
93609467b48Spatrick ParamWithLoc += Stats.NumParamLocations;
93709467b48Spatrick ParamWithSrcLoc += Stats.NumParamSourceLocations;
938097a140dSpatrick LocalVarTotal += Stats.NumLocalVars;
939097a140dSpatrick LocalVarWithType += Stats.NumLocalVarTypes;
940097a140dSpatrick LocalVarWithLoc += Stats.NumLocalVarLocations;
941097a140dSpatrick LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
94209467b48Spatrick }
94309467b48Spatrick
94409467b48Spatrick // Print summary.
94509467b48Spatrick OS.SetBufferSize(1024);
94673471bf0Spatrick json::OStream J(OS, 2);
94773471bf0Spatrick J.objectBegin();
94873471bf0Spatrick J.attribute("version", Version);
94909467b48Spatrick LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
95009467b48Spatrick llvm::dbgs() << "---------------------------------\n");
951097a140dSpatrick
95273471bf0Spatrick printDatum(J, "file", Filename.str());
95373471bf0Spatrick printDatum(J, "format", FormatName);
954097a140dSpatrick
955*d415bd75Srobert printDatum(J, "#functions", NumFunctions.Value);
956*d415bd75Srobert printDatum(J, "#functions with location", NumFuncsWithSrcLoc.Value);
957*d415bd75Srobert printDatum(J, "#inlined functions", NumInlinedFunctions.Value);
958*d415bd75Srobert printDatum(J, "#inlined functions with abstract origins",
959*d415bd75Srobert NumAbstractOrigins.Value);
960097a140dSpatrick
961097a140dSpatrick // This includes local variables and formal parameters.
962*d415bd75Srobert printDatum(J, "#unique source variables", VarParamUnique.Value);
963*d415bd75Srobert printDatum(J, "#source variables", VarParamTotal.Value);
964*d415bd75Srobert printDatum(J, "#source variables with location", VarParamWithLoc.Value);
965097a140dSpatrick
966*d415bd75Srobert printDatum(J, "#call site entries", GlobalStats.CallSiteEntries.Value);
967*d415bd75Srobert printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs.Value);
968*d415bd75Srobert printDatum(J, "#call site parameter DIEs",
969*d415bd75Srobert GlobalStats.CallSiteParamDIEs.Value);
970097a140dSpatrick
97173471bf0Spatrick printDatum(J, "sum_all_variables(#bytes in parent scope)",
972*d415bd75Srobert GlobalStats.ScopeBytes.Value);
97373471bf0Spatrick printDatum(J,
97473471bf0Spatrick "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
975*d415bd75Srobert GlobalStats.TotalBytesCovered.Value);
97673471bf0Spatrick printDatum(J,
977097a140dSpatrick "sum_all_variables(#bytes in parent scope covered by "
978097a140dSpatrick "DW_AT_location)",
979*d415bd75Srobert GlobalStats.ScopeBytesCovered.Value);
98073471bf0Spatrick printDatum(J,
981097a140dSpatrick "sum_all_variables(#bytes in parent scope covered by "
982097a140dSpatrick "DW_OP_entry_value)",
983*d415bd75Srobert GlobalStats.ScopeEntryValueBytesCovered.Value);
984097a140dSpatrick
98573471bf0Spatrick printDatum(J, "sum_all_params(#bytes in parent scope)",
986*d415bd75Srobert GlobalStats.ParamScopeBytes.Value);
98773471bf0Spatrick printDatum(J,
988097a140dSpatrick "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
989*d415bd75Srobert GlobalStats.ParamScopeBytesCovered.Value);
99073471bf0Spatrick printDatum(J,
991097a140dSpatrick "sum_all_params(#bytes in parent scope covered by "
992097a140dSpatrick "DW_OP_entry_value)",
993*d415bd75Srobert GlobalStats.ParamScopeEntryValueBytesCovered.Value);
994097a140dSpatrick
99573471bf0Spatrick printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
996*d415bd75Srobert GlobalStats.LocalVarScopeBytes.Value);
99773471bf0Spatrick printDatum(J,
998097a140dSpatrick "sum_all_local_vars(#bytes in parent scope covered by "
999097a140dSpatrick "DW_AT_location)",
1000*d415bd75Srobert GlobalStats.LocalVarScopeBytesCovered.Value);
100173471bf0Spatrick printDatum(J,
1002097a140dSpatrick "sum_all_local_vars(#bytes in parent scope covered by "
1003097a140dSpatrick "DW_OP_entry_value)",
1004*d415bd75Srobert GlobalStats.LocalVarScopeEntryValueBytesCovered.Value);
1005097a140dSpatrick
1006*d415bd75Srobert printDatum(J, "#bytes within functions", GlobalStats.FunctionSize.Value);
100773471bf0Spatrick printDatum(J, "#bytes within inlined functions",
1008*d415bd75Srobert GlobalStats.InlineFunctionSize.Value);
1009097a140dSpatrick
1010097a140dSpatrick // Print the summary for formal parameters.
1011*d415bd75Srobert printDatum(J, "#params", ParamTotal.Value);
1012*d415bd75Srobert printDatum(J, "#params with source location", ParamWithSrcLoc.Value);
1013*d415bd75Srobert printDatum(J, "#params with type", ParamWithType.Value);
1014*d415bd75Srobert printDatum(J, "#params with binary location", ParamWithLoc.Value);
1015097a140dSpatrick
1016097a140dSpatrick // Print the summary for local variables.
1017*d415bd75Srobert printDatum(J, "#local vars", LocalVarTotal.Value);
1018*d415bd75Srobert printDatum(J, "#local vars with source location", LocalVarWithSrcLoc.Value);
1019*d415bd75Srobert printDatum(J, "#local vars with type", LocalVarWithType.Value);
1020*d415bd75Srobert printDatum(J, "#local vars with binary location", LocalVarWithLoc.Value);
1021097a140dSpatrick
1022097a140dSpatrick // Print the debug section sizes.
102373471bf0Spatrick printSectionSizes(J, Sizes);
1024097a140dSpatrick
1025097a140dSpatrick // Print the location statistics for variables (includes local variables
1026097a140dSpatrick // and formal parameters).
102773471bf0Spatrick printDatum(J, "#variables processed by location statistics",
1028*d415bd75Srobert LocStats.NumVarParam.Value);
102973471bf0Spatrick printLocationStats(J, "#variables", LocStats.VarParamLocStats);
103073471bf0Spatrick printLocationStats(J, "#variables - entry values",
103109467b48Spatrick LocStats.VarParamNonEntryValLocStats);
1032097a140dSpatrick
1033097a140dSpatrick // Print the location statistics for formal parameters.
1034*d415bd75Srobert printDatum(J, "#params processed by location statistics",
1035*d415bd75Srobert LocStats.NumParam.Value);
103673471bf0Spatrick printLocationStats(J, "#params", LocStats.ParamLocStats);
103773471bf0Spatrick printLocationStats(J, "#params - entry values",
103809467b48Spatrick LocStats.ParamNonEntryValLocStats);
1039097a140dSpatrick
1040097a140dSpatrick // Print the location statistics for local variables.
104173471bf0Spatrick printDatum(J, "#local vars processed by location statistics",
1042*d415bd75Srobert LocStats.NumVar.Value);
104373471bf0Spatrick printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
104473471bf0Spatrick printLocationStats(J, "#local vars - entry values",
1045097a140dSpatrick LocStats.LocalVarNonEntryValLocStats);
104673471bf0Spatrick J.objectEnd();
104773471bf0Spatrick OS << '\n';
104809467b48Spatrick LLVM_DEBUG(
104909467b48Spatrick llvm::dbgs() << "Total Availability: "
1050*d415bd75Srobert << (VarParamTotal.Value
1051*d415bd75Srobert ? (int)std::round((VarParamWithLoc.Value * 100.0) /
1052*d415bd75Srobert VarParamTotal.Value)
1053*d415bd75Srobert : 0)
105409467b48Spatrick << "%\n";
105509467b48Spatrick llvm::dbgs() << "PC Ranges covered: "
1056*d415bd75Srobert << (GlobalStats.ScopeBytes.Value
1057*d415bd75Srobert ? (int)std::round(
1058*d415bd75Srobert (GlobalStats.ScopeBytesCovered.Value * 100.0) /
1059*d415bd75Srobert GlobalStats.ScopeBytes.Value)
1060*d415bd75Srobert : 0)
106109467b48Spatrick << "%\n");
106209467b48Spatrick return true;
106309467b48Spatrick }
1064