]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp
Merge clang 7.0.1 and several follow-up changes
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / llvm-dwarfdump / Statistics.cpp
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"
6
7 #define DEBUG_TYPE "dwarfdump"
8 using namespace llvm;
9 using namespace object;
10
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;
24 };
25
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;
33 };
34
35 /// Extract the low pc from a Die.
36 static uint64_t getLowPC(DWARFDie Die) {
37   auto RangesOrError = Die.getAddressRanges();
38   DWARFAddressRangesVector Ranges;
39   if (RangesOrError)
40     Ranges = RangesOrError.get();
41   else
42     llvm::consumeError(RangesOrError.takeError());
43   if (Ranges.size())
44     return Ranges[0].LowPC;
45   return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
46 }
47
48 /// Collect debug info quality metrics for one DIE.
49 static void collectStatsForDie(DWARFDie Die, std::string Prefix,
50                                uint64_t ScopeLowPC, uint64_t BytesInScope,
51                                StringMap<PerFunctionStats> &FnStatMap,
52                                GlobalStats &GlobalStats) {
53   bool HasLoc = false;
54   uint64_t BytesCovered = 0;
55   uint64_t OffsetToFirstDefinition = 0;
56   if (Die.find(dwarf::DW_AT_const_value)) {
57     // This catches constant members *and* variables.
58     HasLoc = true;
59     BytesCovered = BytesInScope;
60   } else if (Die.getTag() == dwarf::DW_TAG_variable ||
61              Die.getTag() == dwarf::DW_TAG_formal_parameter) {
62     // Handle variables and function arguments.
63     auto FormValue = Die.find(dwarf::DW_AT_location);
64     HasLoc = FormValue.hasValue();
65     if (HasLoc) {
66       // Get PC coverage.
67       if (auto DebugLocOffset = FormValue->getAsSectionOffset()) {
68         auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc();
69         if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) {
70           for (auto Entry : List->Entries)
71             BytesCovered += Entry.End - Entry.Begin;
72           if (List->Entries.size()) {
73             uint64_t FirstDef = List->Entries[0].Begin;
74             uint64_t UnitOfs = getLowPC(Die.getDwarfUnit()->getUnitDIE());
75             // Ranges sometimes start before the lexical scope.
76             if (UnitOfs + FirstDef >= ScopeLowPC)
77               OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC;
78             // Or even after it. Count that as a failure.
79             if (OffsetToFirstDefinition > BytesInScope)
80               OffsetToFirstDefinition = 0;
81           }
82         }
83         assert(BytesInScope);
84       } else {
85         // Assume the entire range is covered by a single location.
86         BytesCovered = BytesInScope;
87       }
88     }
89   } else {
90     // Not a variable or constant member.
91     return;
92   }
93
94   // Collect PC range coverage data.
95   auto &FnStats = FnStatMap[Prefix];
96   if (DWARFDie D =
97           Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
98     Die = D;
99   // This is a unique ID for the variable inside the current object file.
100   unsigned CanonicalDieOffset = Die.getOffset();
101   FnStats.VarsInFunction.insert(CanonicalDieOffset);
102   if (BytesInScope) {
103     FnStats.TotalVarWithLoc += (unsigned)HasLoc;
104     // Adjust for the fact the variables often start their lifetime in the
105     // middle of the scope.
106     BytesInScope -= OffsetToFirstDefinition;
107     // Turns out we have a lot of ranges that extend past the lexical scope.
108     GlobalStats.ScopeBytesCovered += std::min(BytesInScope, BytesCovered);
109     GlobalStats.ScopeBytesFromFirstDefinition += BytesInScope;
110     assert(GlobalStats.ScopeBytesCovered <=
111            GlobalStats.ScopeBytesFromFirstDefinition);
112   } else {
113     FnStats.ConstantMembers++;
114   }
115 }
116
117 /// Recursively collect debug info quality metrics.
118 static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
119                                   uint64_t ScopeLowPC, uint64_t BytesInScope,
120                                   StringMap<PerFunctionStats> &FnStatMap,
121                                   GlobalStats &GlobalStats) {
122   // Handle any kind of lexical scope.
123   if (Die.getTag() == dwarf::DW_TAG_subprogram ||
124       Die.getTag() == dwarf::DW_TAG_inlined_subroutine ||
125       Die.getTag() == dwarf::DW_TAG_lexical_block) {
126     // Ignore forward declarations.
127     if (Die.find(dwarf::DW_AT_declaration))
128       return;
129
130     // Count the function.
131     if (Die.getTag() != dwarf::DW_TAG_lexical_block) {
132       StringRef Name = Die.getName(DINameKind::LinkageName);
133       if (Name.empty())
134         Name = Die.getName(DINameKind::ShortName);
135       Prefix = Name;
136       // Skip over abstract origins.
137       if (Die.find(dwarf::DW_AT_inline))
138         return;
139       // We've seen an (inlined) instance of this function.
140       auto &FnStats = FnStatMap[Name];
141       FnStats.NumFnInlined++;
142       FnStats.IsFunction = true;
143     }
144
145     // PC Ranges.
146     auto RangesOrError = Die.getAddressRanges();
147     if (!RangesOrError) {
148       llvm::consumeError(RangesOrError.takeError());
149       return;
150     }
151        
152     auto Ranges = RangesOrError.get();
153     uint64_t BytesInThisScope = 0;
154     for (auto Range : Ranges)
155       BytesInThisScope += Range.HighPC - Range.LowPC;
156     ScopeLowPC = getLowPC(Die);
157
158     if (BytesInThisScope)
159       BytesInScope = BytesInThisScope;
160   } else {
161     // Not a scope, visit the Die itself. It could be a variable.
162     collectStatsForDie(Die, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
163                        GlobalStats);
164   }
165
166   // Traverse children.
167   DWARFDie Child = Die.getFirstChild();
168   while (Child) {
169     collectStatsRecursive(Child, Prefix, ScopeLowPC, BytesInScope, FnStatMap,
170                           GlobalStats);
171     Child = Child.getSibling();
172   }
173 }
174
175 /// Print machine-readable output.
176 /// The machine-readable format is single-line JSON output.
177 /// \{
178 static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) {
179   OS << ",\"" << Key << "\":\"" << Value << '"';
180   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
181 }
182 static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) {
183   OS << ",\"" << Key << "\":" << Value;
184   LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
185 }
186 /// \}
187
188 /// Collect debug info quality metrics for an entire DIContext.
189 ///
190 /// Do the impossible and reduce the quality of the debug info down to a few
191 /// numbers. The idea is to condense the data into numbers that can be tracked
192 /// over time to identify trends in newer compiler versions and gauge the effect
193 /// of particular optimizations. The raw numbers themselves are not particularly
194 /// useful, only the delta between compiling the same program with different
195 /// compilers is.
196 bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
197                                Twine Filename, raw_ostream &OS) {
198   StringRef FormatName = Obj.getFileFormatName();
199   GlobalStats GlobalStats;
200   StringMap<PerFunctionStats> Statistics;
201   for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
202     if (DWARFDie CUDie = CU->getUnitDIE(false))
203       collectStatsRecursive(CUDie, "/", 0, 0, Statistics, GlobalStats);
204
205   /// The version number should be increased every time the algorithm is changed
206   /// (including bug fixes). New metrics may be added without increasing the
207   /// version.
208   unsigned Version = 1;
209   unsigned VarTotal = 0;
210   unsigned VarUnique = 0;
211   unsigned VarWithLoc = 0;
212   unsigned NumFunctions = 0;
213   unsigned NumInlinedFunctions = 0;
214   for (auto &Entry : Statistics) {
215     PerFunctionStats &Stats = Entry.getValue();
216     unsigned TotalVars = Stats.VarsInFunction.size() * Stats.NumFnInlined;
217     unsigned Constants = Stats.ConstantMembers;
218     VarWithLoc += Stats.TotalVarWithLoc + Constants;
219     VarTotal += TotalVars + Constants;
220     VarUnique += Stats.VarsInFunction.size();
221     LLVM_DEBUG(for (auto V
222                     : Stats.VarsInFunction) llvm::dbgs()
223                << Entry.getKey() << ": " << V << "\n");
224     NumFunctions += Stats.IsFunction;
225     NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
226   }
227
228   // Print summary.
229   OS.SetBufferSize(1024);
230   OS << "{\"version\":\"" << Version << '"';
231   LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
232              llvm::dbgs() << "---------------------------------\n");
233   printDatum(OS, "file", Filename.str());
234   printDatum(OS, "format", FormatName);
235   printDatum(OS, "source functions", NumFunctions);
236   printDatum(OS, "inlined functions", NumInlinedFunctions);
237   printDatum(OS, "unique source variables", VarUnique);
238   printDatum(OS, "source variables", VarTotal);
239   printDatum(OS, "variables with location", VarWithLoc);
240   printDatum(OS, "scope bytes total",
241              GlobalStats.ScopeBytesFromFirstDefinition);
242   printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
243   OS << "}\n";
244   LLVM_DEBUG(
245       llvm::dbgs() << "Total Availability: "
246                    << (int)std::round((VarWithLoc * 100.0) / VarTotal) << "%\n";
247       llvm::dbgs() << "PC Ranges covered: "
248                    << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
249                                       GlobalStats.ScopeBytesFromFirstDefinition)
250                    << "%\n");
251   return true;
252 }