Commit r331416 breaks the big-endian PPC bot. On the big endian build, we
[llvm-core.git] / tools / llvm-dwarfdump / Statistics.cpp
blob9a7454a526245d673515b6bdf4aabd60d21c915d
1 #include "llvm/ADT/DenseMap.h"
2 #include "llvm/DebugInfo/DIContext.h"
3 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
4 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
5 #include "llvm/Object/ObjectFile.h"
7 #define DEBUG_TYPE "dwarfdump"
8 using namespace llvm;
9 using namespace object;
11 /// Holds statistics for one function (or other entity that has a PC range and
12 /// contains variables, such as a compile unit).
13 struct PerFunctionStats {
14 /// Number of inlined instances of this function.
15 unsigned NumFnInlined = 0;
16 /// Number of variables with location across all inlined instances.
17 unsigned TotalVarWithLoc = 0;
18 /// Number of constants with location across all inlined instances.
19 unsigned ConstantMembers = 0;
20 /// List of all Variables in this function.
21 SmallDenseSet<uint32_t, 4> VarsInFunction;
22 /// Compile units also cover a PC range, but have this flag set to false.
23 bool IsFunction = false;
26 /// Holds accumulated global statistics about local variables.
27 struct GlobalStats {
28 /// Total number of PC range bytes covered by DW_AT_locations.
29 unsigned ScopeBytesCovered = 0;
30 /// Total number of PC range bytes in each variable's enclosing scope,
31 /// starting from the first definition of the variable.
32 unsigned ScopeBytesFromFirstDefinition = 0;
35 /// Extract the low pc from a Die.
36 static uint64_t getLowPC(DWARFDie Die) {
37 if (Die.getAddressRanges().size())
38 return Die.getAddressRanges()[0].LowPC;
39 return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
42 /// Collect debug info quality metrics for one DIE.
43 static void collectStatsForDie(DWARFDie Die, std::string Prefix,
44 uint64_t ScopeLowPC, uint64_t BytesInScope,
45 StringMap<PerFunctionStats> &FnStatMap,
46 GlobalStats &GlobalStats) {
47 bool HasLoc = false;
48 uint64_t BytesCovered = 0;
49 uint64_t OffsetToFirstDefinition = 0;
50 if (Die.find(dwarf::DW_AT_const_value)) {
51 // This catches constant members *and* variables.
52 HasLoc = true;
53 BytesCovered = BytesInScope;
54 } else if (Die.getTag() == dwarf::DW_TAG_variable ||
55 Die.getTag() == dwarf::DW_TAG_formal_parameter) {
56 // Handle variables and function arguments.
57 auto FormValue = Die.find(dwarf::DW_AT_location);
58 HasLoc = FormValue.hasValue();
59 if (HasLoc) {
60 // Get PC coverage.
61 if (auto DebugLocOffset = FormValue->getAsSectionOffset()) {
62 auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc();
63 if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) {
64 for (auto Entry : List->Entries)
65 BytesCovered += Entry.End - Entry.Begin;
66 if (List->Entries.size()) {
67 uint64_t FirstDef = List->Entries[0].Begin;
68 uint64_t UnitOfs = getLowPC(Die.getDwarfUnit()->getUnitDIE());
69 // Ranges sometimes start before the lexical scope.
70 if (UnitOfs + FirstDef >= ScopeLowPC)
71 OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC;
72 // Or even after it. Count that as a failure.
73 if (OffsetToFirstDefinition > BytesInScope)
74 OffsetToFirstDefinition = 0;
77 assert(BytesInScope);
78 } else {
79 // Assume the entire range is covered by a single location.
80 BytesCovered = BytesInScope;
83 } else {
84 // Not a variable or constant member.
85 return;
88 // Collect PC range coverage data.
89 auto &FnStats = FnStatMap[Prefix];
90 if (DWARFDie D =
91 Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
92 Die = D;
93 // This is a unique ID for the variable inside the current object file.
94 unsigned CanonicalDieOffset = Die.getOffset();
95 FnStats.VarsInFunction.insert(CanonicalDieOffset);
96 if (BytesInScope) {
97 FnStats.TotalVarWithLoc += (unsigned)HasLoc;
98 // Adjust for the fact the variables often start their lifetime in the
99 // middle of the scope.
100 BytesInScope -= OffsetToFirstDefinition;
101 // Turns out we have a lot of ranges that extend past the lexical scope.
102 GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
103 GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope;
104 assert(GlobalStats.ScopeBytesCovered <=
105 GlobalStats.ScopeBytesFromFirstDefinition);
106 } else {
107 FnStats.ConstantMembers++;
111 /// Recursively collect debug info quality metrics.
112 static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
113 uint64_t ScopeLowPC, uint64_t BytesInScope,
114 StringMap<PerFunctionStats> &FnStatMap,
115 GlobalStats &GlobalStats) {
116 // Handle any kind of lexical scope.
117 if (Die.getTag() == dwarf::DW_TAG_subprogram ||
118 Die.getTag() == dwarf::DW_TAG_inlined_subroutine ||
119 Die.getTag() == dwarf::DW_TAG_lexical_block) {
120 // Ignore forward declarations.
121 if (Die.find(dwarf::DW_AT_declaration))
122 return;
124 // Count the function.
125 if (Die.getTag() != dwarf::DW_TAG_lexical_block) {
126 StringRef Name = Die.getName(DINameKind::LinkageName);
127 if (Name.empty())
128 Name = Die.getName(DINameKind::ShortName);
129 Prefix = Name;
130 // Skip over abstract origins.
131 if (Die.find(dwarf::DW_AT_inline))
132 return;
133 // We've seen an (inlined) instance of this function.
134 auto &FnStats = FnStatMap[Name];
135 FnStats.NumFnInlined++;
136 FnStats.IsFunction = true;
139 // PC Ranges.
140 auto Ranges = Die.getAddressRanges();
141 uint64_t BytesInThisScope = 0;
142 for (auto Range : Ranges)
143 BytesInThisScope += Range.HighPC - Range.LowPC;
144 ScopeLowPC = getLowPC(Die);
146 if (BytesInThisScope)
147 BytesInScope = BytesInThisScope;
148 } else {
149 // Not a scope, visit the Die itself. It could be a variable.
150 collectStatsForDie(Die, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
151 GlobalStats);
154 // Traverse children.
155 DWARFDie Child = Die.getFirstChild();
156 while (Child) {
157 collectStatsRecursive(Child, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
158 GlobalStats);
159 Child = Child.getSibling();
163 /// Print machine-readable output.
164 /// The machine-readable format is single-line JSON output.
165 /// \{
166 static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) {
167 OS << ",\"" << Key << "\":\"" << Value << '"';
168 DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
170 static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) {
171 OS << ",\"" << Key << "\":" << Value;
172 DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
174 /// \}
176 /// Collect debug info quality metrics for an entire DIContext.
178 /// Do the impossible and reduce the quality of the debug info down to a few
179 /// numbers. The idea is to condense the data into numbers that can be tracked
180 /// over time to identify trends in newer compiler versions and gauge the effect
181 /// of particular optimizations. The raw numbers themselves are not particularly
182 /// useful, only the delta between compiling the same program with different
183 /// compilers is.
184 bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
185 Twine Filename, raw_ostream &OS) {
186 StringRef FormatName = Obj.getFileFormatName();
187 GlobalStats GlobalStats;
188 StringMap<PerFunctionStats> Statistics;
189 for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
190 if (DWARFDie CUDie = CU->getUnitDIE(false))
191 collectStatsRecursive(CUDie, "/", 0, 0, Statistics, GlobalStats);
193 /// The version number should be increased every time the algorithm is changed
194 /// (including bug fixes). New metrics may be added without increasing the
195 /// version.
196 unsigned Version = 1;
197 unsigned VarTotal = 0;
198 unsigned VarUnique = 0;
199 unsigned VarWithLoc = 0;
200 unsigned NumFunctions = 0;
201 unsigned NumInlinedFunctions = 0;
202 for (auto &Entry : Statistics) {
203 PerFunctionStats &Stats = Entry.getValue();
204 unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined;
205 unsigned Constants = Stats.ConstantMembers;
206 VarWithLoc += Stats.TotalVarWithLoc + Constants;
207 VarTotal += TotalVars + Constants;
208 VarUnique += Stats.VarsInFunction.size();
209 DEBUG(for (auto V : Stats.VarsInFunction)
210 llvm::dbgs() << Entry.getKey() << ": " << V << "\n");
211 NumFunctions += Stats.IsFunction;
212 NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
215 // Print summary.
216 OS.SetBufferSize(1024);
217 OS << "{\"version\":\"" << Version << '"';
218 DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
219 llvm::dbgs() << "---------------------------------\n");
220 printDatum(OS, "file", Filename.str());
221 printDatum(OS, "format", FormatName);
222 printDatum(OS, "source functions", NumFunctions);
223 printDatum(OS, "inlined functions", NumInlinedFunctions);
224 printDatum(OS, "unique source variables", VarUnique);
225 printDatum(OS, "source variables", VarTotal);
226 printDatum(OS, "variables with location", VarWithLoc);
227 printDatum(OS, "scope bytes total",
228 GlobalStats.ScopeBytesFromFirstDefinition);
229 printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
230 OS << "}\n";
231 DEBUG(
232 llvm::dbgs() << "Total Availability: "
233 << (int)std::round((VarWithLoc * 100.0) / VarTotal) << "%\n";
234 llvm::dbgs() << "PC Ranges covered: "
235 << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
236 GlobalStats.ScopeBytesFromFirstDefinition)
237 << "%\n");
238 return true;