xref: /freebsd/contrib/llvm-project/llvm/tools/llvm-dwarfdump/Statistics.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
2*0b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
3*0b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h"
4*0b57cec5SDimitry Andric #include "llvm/DebugInfo/DIContext.h"
5*0b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFContext.h"
6*0b57cec5SDimitry Andric #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
7*0b57cec5SDimitry Andric #include "llvm/Object/ObjectFile.h"
8*0b57cec5SDimitry Andric 
9*0b57cec5SDimitry Andric #define DEBUG_TYPE "dwarfdump"
10*0b57cec5SDimitry Andric using namespace llvm;
11*0b57cec5SDimitry Andric using namespace object;
12*0b57cec5SDimitry Andric 
13*0b57cec5SDimitry Andric /// Holds statistics for one function (or other entity that has a PC range and
14*0b57cec5SDimitry Andric /// contains variables, such as a compile unit).
15*0b57cec5SDimitry Andric struct PerFunctionStats {
16*0b57cec5SDimitry Andric   /// Number of inlined instances of this function.
17*0b57cec5SDimitry Andric   unsigned NumFnInlined = 0;
18*0b57cec5SDimitry Andric   /// Number of inlined instances that have abstract origins.
19*0b57cec5SDimitry Andric   unsigned NumAbstractOrigins = 0;
20*0b57cec5SDimitry Andric   /// Number of variables and parameters with location across all inlined
21*0b57cec5SDimitry Andric   /// instances.
22*0b57cec5SDimitry Andric   unsigned TotalVarWithLoc = 0;
23*0b57cec5SDimitry Andric   /// Number of constants with location across all inlined instances.
24*0b57cec5SDimitry Andric   unsigned ConstantMembers = 0;
25*0b57cec5SDimitry Andric   /// List of all Variables and parameters in this function.
26*0b57cec5SDimitry Andric   StringSet<> VarsInFunction;
27*0b57cec5SDimitry Andric   /// Compile units also cover a PC range, but have this flag set to false.
28*0b57cec5SDimitry Andric   bool IsFunction = false;
29*0b57cec5SDimitry Andric   /// Verify function definition has PC addresses (for detecting when
30*0b57cec5SDimitry Andric   /// a function has been inlined everywhere).
31*0b57cec5SDimitry Andric   bool HasPCAddresses = false;
32*0b57cec5SDimitry Andric   /// Function has source location information.
33*0b57cec5SDimitry Andric   bool HasSourceLocation = false;
34*0b57cec5SDimitry Andric   /// Number of function parameters.
35*0b57cec5SDimitry Andric   unsigned NumParams = 0;
36*0b57cec5SDimitry Andric   /// Number of function parameters with source location.
37*0b57cec5SDimitry Andric   unsigned NumParamSourceLocations = 0;
38*0b57cec5SDimitry Andric   /// Number of function parameters with type.
39*0b57cec5SDimitry Andric   unsigned NumParamTypes = 0;
40*0b57cec5SDimitry Andric   /// Number of function parameters with a DW_AT_location.
41*0b57cec5SDimitry Andric   unsigned NumParamLocations = 0;
42*0b57cec5SDimitry Andric   /// Number of variables.
43*0b57cec5SDimitry Andric   unsigned NumVars = 0;
44*0b57cec5SDimitry Andric   /// Number of variables with source location.
45*0b57cec5SDimitry Andric   unsigned NumVarSourceLocations = 0;
46*0b57cec5SDimitry Andric   /// Number of variables wtih type.
47*0b57cec5SDimitry Andric   unsigned NumVarTypes = 0;
48*0b57cec5SDimitry Andric   /// Number of variables wtih DW_AT_location.
49*0b57cec5SDimitry Andric   unsigned NumVarLocations = 0;
50*0b57cec5SDimitry Andric };
51*0b57cec5SDimitry Andric 
52*0b57cec5SDimitry Andric /// Holds accumulated global statistics about DIEs.
53*0b57cec5SDimitry Andric struct GlobalStats {
54*0b57cec5SDimitry Andric   /// Total number of PC range bytes covered by DW_AT_locations.
55*0b57cec5SDimitry Andric   unsigned ScopeBytesCovered = 0;
56*0b57cec5SDimitry Andric   /// Total number of PC range bytes in each variable's enclosing scope,
57*0b57cec5SDimitry Andric   /// starting from the first definition of the variable.
58*0b57cec5SDimitry Andric   unsigned ScopeBytesFromFirstDefinition = 0;
59*0b57cec5SDimitry Andric   /// Total number of call site entries (DW_TAG_call_site) or
60*0b57cec5SDimitry Andric   /// (DW_AT_call_file & DW_AT_call_line).
61*0b57cec5SDimitry Andric   unsigned CallSiteEntries = 0;
62*0b57cec5SDimitry Andric   /// Total byte size of concrete functions. This byte size includes
63*0b57cec5SDimitry Andric   /// inline functions contained in the concrete functions.
64*0b57cec5SDimitry Andric   uint64_t FunctionSize = 0;
65*0b57cec5SDimitry Andric   /// Total byte size of inlined functions. This is the total number of bytes
66*0b57cec5SDimitry Andric   /// for the top inline functions within concrete functions. This can help
67*0b57cec5SDimitry Andric   /// tune the inline settings when compiling to match user expectations.
68*0b57cec5SDimitry Andric   uint64_t InlineFunctionSize = 0;
69*0b57cec5SDimitry Andric };
70*0b57cec5SDimitry Andric 
71*0b57cec5SDimitry Andric /// Extract the low pc from a Die.
72*0b57cec5SDimitry Andric static uint64_t getLowPC(DWARFDie Die) {
73*0b57cec5SDimitry Andric   auto RangesOrError = Die.getAddressRanges();
74*0b57cec5SDimitry Andric   DWARFAddressRangesVector Ranges;
75*0b57cec5SDimitry Andric   if (RangesOrError)
76*0b57cec5SDimitry Andric     Ranges = RangesOrError.get();
77*0b57cec5SDimitry Andric   else
78*0b57cec5SDimitry Andric     llvm::consumeError(RangesOrError.takeError());
79*0b57cec5SDimitry Andric   if (Ranges.size())
80*0b57cec5SDimitry Andric     return Ranges[0].LowPC;
81*0b57cec5SDimitry Andric   return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
82*0b57cec5SDimitry Andric }
83*0b57cec5SDimitry Andric 
84*0b57cec5SDimitry Andric /// Collect debug info quality metrics for one DIE.
85*0b57cec5SDimitry Andric static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
86*0b57cec5SDimitry Andric                                std::string VarPrefix, uint64_t ScopeLowPC,
87*0b57cec5SDimitry Andric                                uint64_t BytesInScope, uint32_t InlineDepth,
88*0b57cec5SDimitry Andric                                StringMap<PerFunctionStats> &FnStatMap,
89*0b57cec5SDimitry Andric                                GlobalStats &GlobalStats) {
90*0b57cec5SDimitry Andric   bool HasLoc = false;
91*0b57cec5SDimitry Andric   bool HasSrcLoc = false;
92*0b57cec5SDimitry Andric   bool HasType = false;
93*0b57cec5SDimitry Andric   bool IsArtificial = false;
94*0b57cec5SDimitry Andric   uint64_t BytesCovered = 0;
95*0b57cec5SDimitry Andric   uint64_t OffsetToFirstDefinition = 0;
96*0b57cec5SDimitry Andric 
97*0b57cec5SDimitry Andric   if (Die.getTag() == dwarf::DW_TAG_call_site) {
98*0b57cec5SDimitry Andric     GlobalStats.CallSiteEntries++;
99*0b57cec5SDimitry Andric     return;
100*0b57cec5SDimitry Andric   }
101*0b57cec5SDimitry Andric 
102*0b57cec5SDimitry Andric   if (Die.getTag() != dwarf::DW_TAG_formal_parameter &&
103*0b57cec5SDimitry Andric       Die.getTag() != dwarf::DW_TAG_variable &&
104*0b57cec5SDimitry Andric       Die.getTag() != dwarf::DW_TAG_member) {
105*0b57cec5SDimitry Andric     // Not a variable or constant member.
106*0b57cec5SDimitry Andric     return;
107*0b57cec5SDimitry Andric   }
108*0b57cec5SDimitry Andric 
109*0b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
110*0b57cec5SDimitry Andric       Die.findRecursively(dwarf::DW_AT_decl_line))
111*0b57cec5SDimitry Andric     HasSrcLoc = true;
112*0b57cec5SDimitry Andric 
113*0b57cec5SDimitry Andric   if (Die.findRecursively(dwarf::DW_AT_type))
114*0b57cec5SDimitry Andric     HasType = true;
115*0b57cec5SDimitry Andric 
116*0b57cec5SDimitry Andric   if (Die.find(dwarf::DW_AT_artificial))
117*0b57cec5SDimitry Andric     IsArtificial = true;
118*0b57cec5SDimitry Andric 
119*0b57cec5SDimitry Andric   if (Die.find(dwarf::DW_AT_const_value)) {
120*0b57cec5SDimitry Andric     // This catches constant members *and* variables.
121*0b57cec5SDimitry Andric     HasLoc = true;
122*0b57cec5SDimitry Andric     BytesCovered = BytesInScope;
123*0b57cec5SDimitry Andric   } else {
124*0b57cec5SDimitry Andric     if (Die.getTag() == dwarf::DW_TAG_member) {
125*0b57cec5SDimitry Andric       // Non-const member.
126*0b57cec5SDimitry Andric       return;
127*0b57cec5SDimitry Andric     }
128*0b57cec5SDimitry Andric     // Handle variables and function arguments.
129*0b57cec5SDimitry Andric     auto FormValue = Die.find(dwarf::DW_AT_location);
130*0b57cec5SDimitry Andric     HasLoc = FormValue.hasValue();
131*0b57cec5SDimitry Andric     if (HasLoc) {
132*0b57cec5SDimitry Andric       // Get PC coverage.
133*0b57cec5SDimitry Andric       if (auto DebugLocOffset = FormValue->getAsSectionOffset()) {
134*0b57cec5SDimitry Andric         auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc();
135*0b57cec5SDimitry Andric         if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) {
136*0b57cec5SDimitry Andric           for (auto Entry : List->Entries)
137*0b57cec5SDimitry Andric             BytesCovered += Entry.End - Entry.Begin;
138*0b57cec5SDimitry Andric           if (List->Entries.size()) {
139*0b57cec5SDimitry Andric             uint64_t FirstDef = List->Entries[0].Begin;
140*0b57cec5SDimitry Andric             uint64_t UnitOfs = getLowPC(Die.getDwarfUnit()->getUnitDIE());
141*0b57cec5SDimitry Andric             // Ranges sometimes start before the lexical scope.
142*0b57cec5SDimitry Andric             if (UnitOfs + FirstDef >= ScopeLowPC)
143*0b57cec5SDimitry Andric               OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC;
144*0b57cec5SDimitry Andric             // Or even after it. Count that as a failure.
145*0b57cec5SDimitry Andric             if (OffsetToFirstDefinition > BytesInScope)
146*0b57cec5SDimitry Andric               OffsetToFirstDefinition = 0;
147*0b57cec5SDimitry Andric           }
148*0b57cec5SDimitry Andric         }
149*0b57cec5SDimitry Andric         assert(BytesInScope);
150*0b57cec5SDimitry Andric       } else {
151*0b57cec5SDimitry Andric         // Assume the entire range is covered by a single location.
152*0b57cec5SDimitry Andric         BytesCovered = BytesInScope;
153*0b57cec5SDimitry Andric       }
154*0b57cec5SDimitry Andric     }
155*0b57cec5SDimitry Andric   }
156*0b57cec5SDimitry Andric 
157*0b57cec5SDimitry Andric   // Collect PC range coverage data.
158*0b57cec5SDimitry Andric   auto &FnStats = FnStatMap[FnPrefix];
159*0b57cec5SDimitry Andric   if (DWARFDie D =
160*0b57cec5SDimitry Andric           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
161*0b57cec5SDimitry Andric     Die = D;
162*0b57cec5SDimitry Andric   // By using the variable name + the path through the lexical block tree, the
163*0b57cec5SDimitry Andric   // keys are consistent across duplicate abstract origins in different CUs.
164*0b57cec5SDimitry Andric   std::string VarName = StringRef(Die.getName(DINameKind::ShortName));
165*0b57cec5SDimitry Andric   FnStats.VarsInFunction.insert(VarPrefix + VarName);
166*0b57cec5SDimitry Andric   if (BytesInScope) {
167*0b57cec5SDimitry Andric     FnStats.TotalVarWithLoc += (unsigned)HasLoc;
168*0b57cec5SDimitry Andric     // Adjust for the fact the variables often start their lifetime in the
169*0b57cec5SDimitry Andric     // middle of the scope.
170*0b57cec5SDimitry Andric     BytesInScope -= OffsetToFirstDefinition;
171*0b57cec5SDimitry Andric     // Turns out we have a lot of ranges that extend past the lexical scope.
172*0b57cec5SDimitry Andric     GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
173*0b57cec5SDimitry Andric     GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope;
174*0b57cec5SDimitry Andric     assert(GlobalStats.ScopeBytesCovered <=
175*0b57cec5SDimitry Andric            GlobalStats.ScopeBytesFromFirstDefinition);
176*0b57cec5SDimitry Andric   } else if (Die.getTag() == dwarf::DW_TAG_member) {
177*0b57cec5SDimitry Andric     FnStats.ConstantMembers++;
178*0b57cec5SDimitry Andric   } else {
179*0b57cec5SDimitry Andric     FnStats.TotalVarWithLoc += (unsigned)HasLoc;
180*0b57cec5SDimitry Andric   }
181*0b57cec5SDimitry Andric   if (!IsArtificial) {
182*0b57cec5SDimitry Andric     if (Die.getTag() == dwarf::DW_TAG_formal_parameter) {
183*0b57cec5SDimitry Andric       FnStats.NumParams++;
184*0b57cec5SDimitry Andric       if (HasType)
185*0b57cec5SDimitry Andric         FnStats.NumParamTypes++;
186*0b57cec5SDimitry Andric       if (HasSrcLoc)
187*0b57cec5SDimitry Andric         FnStats.NumParamSourceLocations++;
188*0b57cec5SDimitry Andric       if (HasLoc)
189*0b57cec5SDimitry Andric         FnStats.NumParamLocations++;
190*0b57cec5SDimitry Andric     } else if (Die.getTag() == dwarf::DW_TAG_variable) {
191*0b57cec5SDimitry Andric       FnStats.NumVars++;
192*0b57cec5SDimitry Andric       if (HasType)
193*0b57cec5SDimitry Andric         FnStats.NumVarTypes++;
194*0b57cec5SDimitry Andric       if (HasSrcLoc)
195*0b57cec5SDimitry Andric         FnStats.NumVarSourceLocations++;
196*0b57cec5SDimitry Andric       if (HasLoc)
197*0b57cec5SDimitry Andric         FnStats.NumVarLocations++;
198*0b57cec5SDimitry Andric     }
199*0b57cec5SDimitry Andric   }
200*0b57cec5SDimitry Andric }
201*0b57cec5SDimitry Andric 
202*0b57cec5SDimitry Andric /// Recursively collect debug info quality metrics.
203*0b57cec5SDimitry Andric static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
204*0b57cec5SDimitry Andric                                   std::string VarPrefix, uint64_t ScopeLowPC,
205*0b57cec5SDimitry Andric                                   uint64_t BytesInScope, uint32_t InlineDepth,
206*0b57cec5SDimitry Andric                                   StringMap<PerFunctionStats> &FnStatMap,
207*0b57cec5SDimitry Andric                                   GlobalStats &GlobalStats) {
208*0b57cec5SDimitry Andric   // Handle any kind of lexical scope.
209*0b57cec5SDimitry Andric   const dwarf::Tag Tag = Die.getTag();
210*0b57cec5SDimitry Andric   const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
211*0b57cec5SDimitry Andric   const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
212*0b57cec5SDimitry Andric   const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
213*0b57cec5SDimitry Andric   if (IsFunction || IsInlinedFunction || IsBlock) {
214*0b57cec5SDimitry Andric 
215*0b57cec5SDimitry Andric     // Reset VarPrefix when entering a new function.
216*0b57cec5SDimitry Andric     if (Die.getTag() == dwarf::DW_TAG_subprogram ||
217*0b57cec5SDimitry Andric         Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
218*0b57cec5SDimitry Andric       VarPrefix = "v";
219*0b57cec5SDimitry Andric 
220*0b57cec5SDimitry Andric     // Ignore forward declarations.
221*0b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_declaration))
222*0b57cec5SDimitry Andric       return;
223*0b57cec5SDimitry Andric 
224*0b57cec5SDimitry Andric     // Check for call sites.
225*0b57cec5SDimitry Andric     if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
226*0b57cec5SDimitry Andric       GlobalStats.CallSiteEntries++;
227*0b57cec5SDimitry Andric 
228*0b57cec5SDimitry Andric     // PC Ranges.
229*0b57cec5SDimitry Andric     auto RangesOrError = Die.getAddressRanges();
230*0b57cec5SDimitry Andric     if (!RangesOrError) {
231*0b57cec5SDimitry Andric       llvm::consumeError(RangesOrError.takeError());
232*0b57cec5SDimitry Andric       return;
233*0b57cec5SDimitry Andric     }
234*0b57cec5SDimitry Andric 
235*0b57cec5SDimitry Andric     auto Ranges = RangesOrError.get();
236*0b57cec5SDimitry Andric     uint64_t BytesInThisScope = 0;
237*0b57cec5SDimitry Andric     for (auto Range : Ranges)
238*0b57cec5SDimitry Andric       BytesInThisScope += Range.HighPC - Range.LowPC;
239*0b57cec5SDimitry Andric     ScopeLowPC = getLowPC(Die);
240*0b57cec5SDimitry Andric 
241*0b57cec5SDimitry Andric     // Count the function.
242*0b57cec5SDimitry Andric     if (!IsBlock) {
243*0b57cec5SDimitry Andric       StringRef Name = Die.getName(DINameKind::LinkageName);
244*0b57cec5SDimitry Andric       if (Name.empty())
245*0b57cec5SDimitry Andric         Name = Die.getName(DINameKind::ShortName);
246*0b57cec5SDimitry Andric       FnPrefix = Name;
247*0b57cec5SDimitry Andric       // Skip over abstract origins.
248*0b57cec5SDimitry Andric       if (Die.find(dwarf::DW_AT_inline))
249*0b57cec5SDimitry Andric         return;
250*0b57cec5SDimitry Andric       // We've seen an (inlined) instance of this function.
251*0b57cec5SDimitry Andric       auto &FnStats = FnStatMap[Name];
252*0b57cec5SDimitry Andric       if (IsInlinedFunction) {
253*0b57cec5SDimitry Andric         FnStats.NumFnInlined++;
254*0b57cec5SDimitry Andric         if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
255*0b57cec5SDimitry Andric           FnStats.NumAbstractOrigins++;
256*0b57cec5SDimitry Andric       }
257*0b57cec5SDimitry Andric       FnStats.IsFunction = true;
258*0b57cec5SDimitry Andric       if (BytesInThisScope && !IsInlinedFunction)
259*0b57cec5SDimitry Andric         FnStats.HasPCAddresses = true;
260*0b57cec5SDimitry Andric       std::string FnName = StringRef(Die.getName(DINameKind::ShortName));
261*0b57cec5SDimitry Andric       if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
262*0b57cec5SDimitry Andric           Die.findRecursively(dwarf::DW_AT_decl_line))
263*0b57cec5SDimitry Andric         FnStats.HasSourceLocation = true;
264*0b57cec5SDimitry Andric     }
265*0b57cec5SDimitry Andric 
266*0b57cec5SDimitry Andric     if (BytesInThisScope) {
267*0b57cec5SDimitry Andric       BytesInScope = BytesInThisScope;
268*0b57cec5SDimitry Andric       if (IsFunction)
269*0b57cec5SDimitry Andric         GlobalStats.FunctionSize += BytesInThisScope;
270*0b57cec5SDimitry Andric       else if (IsInlinedFunction && InlineDepth == 0)
271*0b57cec5SDimitry Andric         GlobalStats.InlineFunctionSize += BytesInThisScope;
272*0b57cec5SDimitry Andric     }
273*0b57cec5SDimitry Andric   } else {
274*0b57cec5SDimitry Andric     // Not a scope, visit the Die itself. It could be a variable.
275*0b57cec5SDimitry Andric     collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope,
276*0b57cec5SDimitry Andric                        InlineDepth, FnStatMap, GlobalStats);
277*0b57cec5SDimitry Andric   }
278*0b57cec5SDimitry Andric 
279*0b57cec5SDimitry Andric   // Set InlineDepth correctly for child recursion
280*0b57cec5SDimitry Andric   if (IsFunction)
281*0b57cec5SDimitry Andric     InlineDepth = 0;
282*0b57cec5SDimitry Andric   else if (IsInlinedFunction)
283*0b57cec5SDimitry Andric     ++InlineDepth;
284*0b57cec5SDimitry Andric 
285*0b57cec5SDimitry Andric   // Traverse children.
286*0b57cec5SDimitry Andric   unsigned LexicalBlockIndex = 0;
287*0b57cec5SDimitry Andric   DWARFDie Child = Die.getFirstChild();
288*0b57cec5SDimitry Andric   while (Child) {
289*0b57cec5SDimitry Andric     std::string ChildVarPrefix = VarPrefix;
290*0b57cec5SDimitry Andric     if (Child.getTag() == dwarf::DW_TAG_lexical_block)
291*0b57cec5SDimitry Andric       ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
292*0b57cec5SDimitry Andric 
293*0b57cec5SDimitry Andric     collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC,
294*0b57cec5SDimitry Andric                           BytesInScope, InlineDepth, FnStatMap, GlobalStats);
295*0b57cec5SDimitry Andric     Child = Child.getSibling();
296*0b57cec5SDimitry Andric   }
297*0b57cec5SDimitry Andric }
298*0b57cec5SDimitry Andric 
299*0b57cec5SDimitry Andric /// Print machine-readable output.
300*0b57cec5SDimitry Andric /// The machine-readable format is single-line JSON output.
301*0b57cec5SDimitry Andric /// \{
302*0b57cec5SDimitry Andric static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) {
303*0b57cec5SDimitry Andric   OS << ",\"" << Key << "\":\"" << Value << '"';
304*0b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
305*0b57cec5SDimitry Andric }
306*0b57cec5SDimitry Andric static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) {
307*0b57cec5SDimitry Andric   OS << ",\"" << Key << "\":" << Value;
308*0b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
309*0b57cec5SDimitry Andric }
310*0b57cec5SDimitry Andric /// \}
311*0b57cec5SDimitry Andric 
312*0b57cec5SDimitry Andric /// Collect debug info quality metrics for an entire DIContext.
313*0b57cec5SDimitry Andric ///
314*0b57cec5SDimitry Andric /// Do the impossible and reduce the quality of the debug info down to a few
315*0b57cec5SDimitry Andric /// numbers. The idea is to condense the data into numbers that can be tracked
316*0b57cec5SDimitry Andric /// over time to identify trends in newer compiler versions and gauge the effect
317*0b57cec5SDimitry Andric /// of particular optimizations. The raw numbers themselves are not particularly
318*0b57cec5SDimitry Andric /// useful, only the delta between compiling the same program with different
319*0b57cec5SDimitry Andric /// compilers is.
320*0b57cec5SDimitry Andric bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
321*0b57cec5SDimitry Andric                                Twine Filename, raw_ostream &OS) {
322*0b57cec5SDimitry Andric   StringRef FormatName = Obj.getFileFormatName();
323*0b57cec5SDimitry Andric   GlobalStats GlobalStats;
324*0b57cec5SDimitry Andric   StringMap<PerFunctionStats> Statistics;
325*0b57cec5SDimitry Andric   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
326*0b57cec5SDimitry Andric     if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
327*0b57cec5SDimitry Andric       collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats);
328*0b57cec5SDimitry Andric 
329*0b57cec5SDimitry Andric   /// The version number should be increased every time the algorithm is changed
330*0b57cec5SDimitry Andric   /// (including bug fixes). New metrics may be added without increasing the
331*0b57cec5SDimitry Andric   /// version.
332*0b57cec5SDimitry Andric   unsigned Version = 3;
333*0b57cec5SDimitry Andric   unsigned VarParamTotal = 0;
334*0b57cec5SDimitry Andric   unsigned VarParamUnique = 0;
335*0b57cec5SDimitry Andric   unsigned VarParamWithLoc = 0;
336*0b57cec5SDimitry Andric   unsigned NumFunctions = 0;
337*0b57cec5SDimitry Andric   unsigned NumInlinedFunctions = 0;
338*0b57cec5SDimitry Andric   unsigned NumFuncsWithSrcLoc = 0;
339*0b57cec5SDimitry Andric   unsigned NumAbstractOrigins = 0;
340*0b57cec5SDimitry Andric   unsigned ParamTotal = 0;
341*0b57cec5SDimitry Andric   unsigned ParamWithType = 0;
342*0b57cec5SDimitry Andric   unsigned ParamWithLoc = 0;
343*0b57cec5SDimitry Andric   unsigned ParamWithSrcLoc = 0;
344*0b57cec5SDimitry Andric   unsigned VarTotal = 0;
345*0b57cec5SDimitry Andric   unsigned VarWithType = 0;
346*0b57cec5SDimitry Andric   unsigned VarWithSrcLoc = 0;
347*0b57cec5SDimitry Andric   unsigned VarWithLoc = 0;
348*0b57cec5SDimitry Andric   for (auto &Entry : Statistics) {
349*0b57cec5SDimitry Andric     PerFunctionStats &Stats = Entry.getValue();
350*0b57cec5SDimitry Andric     unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined;
351*0b57cec5SDimitry Andric     // Count variables in concrete out-of-line functions and in global scope.
352*0b57cec5SDimitry Andric     if (Stats.HasPCAddresses || !Stats.IsFunction)
353*0b57cec5SDimitry Andric       TotalVars += Stats.VarsInFunction.size();
354*0b57cec5SDimitry Andric     unsigned Constants = Stats.ConstantMembers;
355*0b57cec5SDimitry Andric     VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
356*0b57cec5SDimitry Andric     VarParamTotal += TotalVars;
357*0b57cec5SDimitry Andric     VarParamUnique += Stats.VarsInFunction.size();
358*0b57cec5SDimitry Andric     LLVM_DEBUG(for (auto &V
359*0b57cec5SDimitry Andric                     : Stats.VarsInFunction) llvm::dbgs()
360*0b57cec5SDimitry Andric                << Entry.getKey() << ": " << V.getKey() << "\n");
361*0b57cec5SDimitry Andric     NumFunctions += Stats.IsFunction;
362*0b57cec5SDimitry Andric     NumFuncsWithSrcLoc += Stats.HasSourceLocation;
363*0b57cec5SDimitry Andric     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
364*0b57cec5SDimitry Andric     NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
365*0b57cec5SDimitry Andric     ParamTotal += Stats.NumParams;
366*0b57cec5SDimitry Andric     ParamWithType += Stats.NumParamTypes;
367*0b57cec5SDimitry Andric     ParamWithLoc += Stats.NumParamLocations;
368*0b57cec5SDimitry Andric     ParamWithSrcLoc += Stats.NumParamSourceLocations;
369*0b57cec5SDimitry Andric     VarTotal += Stats.NumVars;
370*0b57cec5SDimitry Andric     VarWithType += Stats.NumVarTypes;
371*0b57cec5SDimitry Andric     VarWithLoc += Stats.NumVarLocations;
372*0b57cec5SDimitry Andric     VarWithSrcLoc += Stats.NumVarSourceLocations;
373*0b57cec5SDimitry Andric   }
374*0b57cec5SDimitry Andric 
375*0b57cec5SDimitry Andric   // Print summary.
376*0b57cec5SDimitry Andric   OS.SetBufferSize(1024);
377*0b57cec5SDimitry Andric   OS << "{\"version\":" << Version;
378*0b57cec5SDimitry Andric   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
379*0b57cec5SDimitry Andric              llvm::dbgs() << "---------------------------------\n");
380*0b57cec5SDimitry Andric   printDatum(OS, "file", Filename.str());
381*0b57cec5SDimitry Andric   printDatum(OS, "format", FormatName);
382*0b57cec5SDimitry Andric   printDatum(OS, "source functions", NumFunctions);
383*0b57cec5SDimitry Andric   printDatum(OS, "source functions with location", NumFuncsWithSrcLoc);
384*0b57cec5SDimitry Andric   printDatum(OS, "inlined functions", NumInlinedFunctions);
385*0b57cec5SDimitry Andric   printDatum(OS, "inlined funcs with abstract origins", NumAbstractOrigins);
386*0b57cec5SDimitry Andric   printDatum(OS, "unique source variables", VarParamUnique);
387*0b57cec5SDimitry Andric   printDatum(OS, "source variables", VarParamTotal);
388*0b57cec5SDimitry Andric   printDatum(OS, "variables with location", VarParamWithLoc);
389*0b57cec5SDimitry Andric   printDatum(OS, "call site entries", GlobalStats.CallSiteEntries);
390*0b57cec5SDimitry Andric   printDatum(OS, "scope bytes total",
391*0b57cec5SDimitry Andric              GlobalStats.ScopeBytesFromFirstDefinition);
392*0b57cec5SDimitry Andric   printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
393*0b57cec5SDimitry Andric   printDatum(OS, "total function size", GlobalStats.FunctionSize);
394*0b57cec5SDimitry Andric   printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize);
395*0b57cec5SDimitry Andric   printDatum(OS, "total formal params", ParamTotal);
396*0b57cec5SDimitry Andric   printDatum(OS, "formal params with source location", ParamWithSrcLoc);
397*0b57cec5SDimitry Andric   printDatum(OS, "formal params with type", ParamWithType);
398*0b57cec5SDimitry Andric   printDatum(OS, "formal params with binary location", ParamWithLoc);
399*0b57cec5SDimitry Andric   printDatum(OS, "total vars", VarTotal);
400*0b57cec5SDimitry Andric   printDatum(OS, "vars with source location", VarWithSrcLoc);
401*0b57cec5SDimitry Andric   printDatum(OS, "vars with type", VarWithType);
402*0b57cec5SDimitry Andric   printDatum(OS, "vars with binary location", VarWithLoc);
403*0b57cec5SDimitry Andric   OS << "}\n";
404*0b57cec5SDimitry Andric   LLVM_DEBUG(
405*0b57cec5SDimitry Andric       llvm::dbgs() << "Total Availability: "
406*0b57cec5SDimitry Andric                    << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
407*0b57cec5SDimitry Andric                    << "%\n";
408*0b57cec5SDimitry Andric       llvm::dbgs() << "PC Ranges covered: "
409*0b57cec5SDimitry Andric                    << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
410*0b57cec5SDimitry Andric                                       GlobalStats.ScopeBytesFromFirstDefinition)
411*0b57cec5SDimitry Andric                    << "%\n");
412*0b57cec5SDimitry Andric   return true;
413*0b57cec5SDimitry Andric }
414