Statistics.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. //===-- Statistics.cpp - Debug Info quality metrics -----------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "llvm-dwarfdump.h"
  9. #include "llvm/ADT/DenseMap.h"
  10. #include "llvm/ADT/StringExtras.h"
  11. #include "llvm/ADT/StringSet.h"
  12. #include "llvm/DebugInfo/DIContext.h"
  13. #include "llvm/DebugInfo/DWARF/DWARFContext.h"
  14. #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
  15. #include "llvm/Object/ObjectFile.h"
  16. #include "llvm/Support/JSON.h"
  17. #define DEBUG_TYPE "dwarfdump"
  18. using namespace llvm;
  19. using namespace llvm::dwarfdump;
  20. using namespace llvm::object;
  21. /// This represents the number of categories of debug location coverage being
  22. /// calculated. The first category is the number of variables with 0% location
  23. /// coverage, but the last category is the number of variables with 100%
  24. /// location coverage.
  25. constexpr int NumOfCoverageCategories = 12;
  26. namespace {
  27. /// Holds statistics for one function (or other entity that has a PC range and
  28. /// contains variables, such as a compile unit).
  29. struct PerFunctionStats {
  30. /// Number of inlined instances of this function.
  31. unsigned NumFnInlined = 0;
  32. /// Number of out-of-line instances of this function.
  33. unsigned NumFnOutOfLine = 0;
  34. /// Number of inlined instances that have abstract origins.
  35. unsigned NumAbstractOrigins = 0;
  36. /// Number of variables and parameters with location across all inlined
  37. /// instances.
  38. unsigned TotalVarWithLoc = 0;
  39. /// Number of constants with location across all inlined instances.
  40. unsigned ConstantMembers = 0;
  41. /// Number of arificial variables, parameters or members across all instances.
  42. unsigned NumArtificial = 0;
  43. /// List of all Variables and parameters in this function.
  44. StringSet<> VarsInFunction;
  45. /// Compile units also cover a PC range, but have this flag set to false.
  46. bool IsFunction = false;
  47. /// Function has source location information.
  48. bool HasSourceLocation = false;
  49. /// Number of function parameters.
  50. unsigned NumParams = 0;
  51. /// Number of function parameters with source location.
  52. unsigned NumParamSourceLocations = 0;
  53. /// Number of function parameters with type.
  54. unsigned NumParamTypes = 0;
  55. /// Number of function parameters with a DW_AT_location.
  56. unsigned NumParamLocations = 0;
  57. /// Number of local variables.
  58. unsigned NumLocalVars = 0;
  59. /// Number of local variables with source location.
  60. unsigned NumLocalVarSourceLocations = 0;
  61. /// Number of local variables with type.
  62. unsigned NumLocalVarTypes = 0;
  63. /// Number of local variables with DW_AT_location.
  64. unsigned NumLocalVarLocations = 0;
  65. };
  66. /// Holds accumulated global statistics about DIEs.
  67. struct GlobalStats {
  68. /// Total number of PC range bytes covered by DW_AT_locations.
  69. unsigned TotalBytesCovered = 0;
  70. /// Total number of parent DIE PC range bytes covered by DW_AT_Locations.
  71. unsigned ScopeBytesCovered = 0;
  72. /// Total number of PC range bytes in each variable's enclosing scope.
  73. unsigned ScopeBytes = 0;
  74. /// Total number of PC range bytes covered by DW_AT_locations with
  75. /// the debug entry values (DW_OP_entry_value).
  76. unsigned ScopeEntryValueBytesCovered = 0;
  77. /// Total number of PC range bytes covered by DW_AT_locations of
  78. /// formal parameters.
  79. unsigned ParamScopeBytesCovered = 0;
  80. /// Total number of PC range bytes in each parameter's enclosing scope.
  81. unsigned ParamScopeBytes = 0;
  82. /// Total number of PC range bytes covered by DW_AT_locations with
  83. /// the debug entry values (DW_OP_entry_value) (only for parameters).
  84. unsigned ParamScopeEntryValueBytesCovered = 0;
  85. /// Total number of PC range bytes covered by DW_AT_locations (only for local
  86. /// variables).
  87. unsigned LocalVarScopeBytesCovered = 0;
  88. /// Total number of PC range bytes in each local variable's enclosing scope.
  89. unsigned LocalVarScopeBytes = 0;
  90. /// Total number of PC range bytes covered by DW_AT_locations with
  91. /// the debug entry values (DW_OP_entry_value) (only for local variables).
  92. unsigned LocalVarScopeEntryValueBytesCovered = 0;
  93. /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
  94. unsigned CallSiteEntries = 0;
  95. /// Total number of call site DIEs (DW_TAG_call_site).
  96. unsigned CallSiteDIEs = 0;
  97. /// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
  98. unsigned CallSiteParamDIEs = 0;
  99. /// Total byte size of concrete functions. This byte size includes
  100. /// inline functions contained in the concrete functions.
  101. unsigned FunctionSize = 0;
  102. /// Total byte size of inlined functions. This is the total number of bytes
  103. /// for the top inline functions within concrete functions. This can help
  104. /// tune the inline settings when compiling to match user expectations.
  105. unsigned InlineFunctionSize = 0;
  106. };
  107. /// Holds accumulated debug location statistics about local variables and
  108. /// formal parameters.
  109. struct LocationStats {
  110. /// Map the scope coverage decile to the number of variables in the decile.
  111. /// The first element of the array (at the index zero) represents the number
  112. /// of variables with the no debug location at all, but the last element
  113. /// in the vector represents the number of fully covered variables within
  114. /// its scope.
  115. std::vector<unsigned> VarParamLocStats{
  116. std::vector<unsigned>(NumOfCoverageCategories, 0)};
  117. /// Map non debug entry values coverage.
  118. std::vector<unsigned> VarParamNonEntryValLocStats{
  119. std::vector<unsigned>(NumOfCoverageCategories, 0)};
  120. /// The debug location statistics for formal parameters.
  121. std::vector<unsigned> ParamLocStats{
  122. std::vector<unsigned>(NumOfCoverageCategories, 0)};
  123. /// Map non debug entry values coverage for formal parameters.
  124. std::vector<unsigned> ParamNonEntryValLocStats{
  125. std::vector<unsigned>(NumOfCoverageCategories, 0)};
  126. /// The debug location statistics for local variables.
  127. std::vector<unsigned> LocalVarLocStats{
  128. std::vector<unsigned>(NumOfCoverageCategories, 0)};
  129. /// Map non debug entry values coverage for local variables.
  130. std::vector<unsigned> LocalVarNonEntryValLocStats{
  131. std::vector<unsigned>(NumOfCoverageCategories, 0)};
  132. /// Total number of local variables and function parameters processed.
  133. unsigned NumVarParam = 0;
  134. /// Total number of formal parameters processed.
  135. unsigned NumParam = 0;
  136. /// Total number of local variables processed.
  137. unsigned NumVar = 0;
  138. };
  139. } // namespace
  140. /// Collect debug location statistics for one DIE.
  141. static void collectLocStats(uint64_t ScopeBytesCovered, uint64_t BytesInScope,
  142. std::vector<unsigned> &VarParamLocStats,
  143. std::vector<unsigned> &ParamLocStats,
  144. std::vector<unsigned> &LocalVarLocStats,
  145. bool IsParam, bool IsLocalVar) {
  146. auto getCoverageBucket = [ScopeBytesCovered, BytesInScope]() -> unsigned {
  147. // No debug location at all for the variable.
  148. if (ScopeBytesCovered == 0)
  149. return 0;
  150. // Fully covered variable within its scope.
  151. if (ScopeBytesCovered >= BytesInScope)
  152. return NumOfCoverageCategories - 1;
  153. // Get covered range (e.g. 20%-29%).
  154. unsigned LocBucket = 100 * (double)ScopeBytesCovered / BytesInScope;
  155. LocBucket /= 10;
  156. return LocBucket + 1;
  157. };
  158. unsigned CoverageBucket = getCoverageBucket();
  159. VarParamLocStats[CoverageBucket]++;
  160. if (IsParam)
  161. ParamLocStats[CoverageBucket]++;
  162. else if (IsLocalVar)
  163. LocalVarLocStats[CoverageBucket]++;
  164. }
  165. /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName
  166. /// and DeclLine. The identifier aims to be unique for any unique entities,
  167. /// but keeping the same among different instances of the same entity.
  168. static std::string constructDieID(DWARFDie Die,
  169. StringRef Prefix = StringRef()) {
  170. std::string IDStr;
  171. llvm::raw_string_ostream ID(IDStr);
  172. ID << Prefix
  173. << Die.getName(DINameKind::LinkageName);
  174. // Prefix + Name is enough for local variables and parameters.
  175. if (!Prefix.empty() && !Prefix.equals("g"))
  176. return ID.str();
  177. auto DeclFile = Die.findRecursively(dwarf::DW_AT_decl_file);
  178. std::string File;
  179. if (DeclFile) {
  180. DWARFUnit *U = Die.getDwarfUnit();
  181. if (const auto *LT = U->getContext().getLineTableForUnit(U))
  182. if (LT->getFileNameByIndex(
  183. dwarf::toUnsigned(DeclFile, 0), U->getCompilationDir(),
  184. DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File))
  185. File = std::string(sys::path::filename(File));
  186. }
  187. ID << ":" << (File.empty() ? "/" : File);
  188. ID << ":"
  189. << dwarf::toUnsigned(Die.findRecursively(dwarf::DW_AT_decl_line), 0);
  190. return ID.str();
  191. }
  192. /// Return the number of bytes in the overlap of ranges A and B.
  193. static uint64_t calculateOverlap(DWARFAddressRange A, DWARFAddressRange B) {
  194. uint64_t Lower = std::max(A.LowPC, B.LowPC);
  195. uint64_t Upper = std::min(A.HighPC, B.HighPC);
  196. if (Lower >= Upper)
  197. return 0;
  198. return Upper - Lower;
  199. }
  200. /// Collect debug info quality metrics for one DIE.
  201. static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
  202. std::string VarPrefix, uint64_t BytesInScope,
  203. uint32_t InlineDepth,
  204. StringMap<PerFunctionStats> &FnStatMap,
  205. GlobalStats &GlobalStats,
  206. LocationStats &LocStats) {
  207. bool HasLoc = false;
  208. bool HasSrcLoc = false;
  209. bool HasType = false;
  210. uint64_t TotalBytesCovered = 0;
  211. uint64_t ScopeBytesCovered = 0;
  212. uint64_t BytesEntryValuesCovered = 0;
  213. auto &FnStats = FnStatMap[FnPrefix];
  214. bool IsParam = Die.getTag() == dwarf::DW_TAG_formal_parameter;
  215. bool IsLocalVar = Die.getTag() == dwarf::DW_TAG_variable;
  216. bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member &&
  217. Die.find(dwarf::DW_AT_const_value);
  218. if (Die.getTag() == dwarf::DW_TAG_call_site ||
  219. Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
  220. GlobalStats.CallSiteDIEs++;
  221. return;
  222. }
  223. if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
  224. Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
  225. GlobalStats.CallSiteParamDIEs++;
  226. return;
  227. }
  228. if (!IsParam && !IsLocalVar && !IsConstantMember) {
  229. // Not a variable or constant member.
  230. return;
  231. }
  232. // Ignore declarations of global variables.
  233. if (IsLocalVar && Die.find(dwarf::DW_AT_declaration))
  234. return;
  235. if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
  236. Die.findRecursively(dwarf::DW_AT_decl_line))
  237. HasSrcLoc = true;
  238. if (Die.findRecursively(dwarf::DW_AT_type))
  239. HasType = true;
  240. auto IsEntryValue = [&](ArrayRef<uint8_t> D) -> bool {
  241. DWARFUnit *U = Die.getDwarfUnit();
  242. DataExtractor Data(toStringRef(D),
  243. Die.getDwarfUnit()->getContext().isLittleEndian(), 0);
  244. DWARFExpression Expression(Data, U->getAddressByteSize(),
  245. U->getFormParams().Format);
  246. // Consider the expression containing the DW_OP_entry_value as
  247. // an entry value.
  248. return llvm::any_of(Expression, [](DWARFExpression::Operation &Op) {
  249. return Op.getCode() == dwarf::DW_OP_entry_value ||
  250. Op.getCode() == dwarf::DW_OP_GNU_entry_value;
  251. });
  252. };
  253. if (Die.find(dwarf::DW_AT_const_value)) {
  254. // This catches constant members *and* variables.
  255. HasLoc = true;
  256. ScopeBytesCovered = BytesInScope;
  257. TotalBytesCovered = BytesInScope;
  258. } else {
  259. // Handle variables and function arguments.
  260. Expected<std::vector<DWARFLocationExpression>> Loc =
  261. Die.getLocations(dwarf::DW_AT_location);
  262. if (!Loc) {
  263. consumeError(Loc.takeError());
  264. } else {
  265. HasLoc = true;
  266. // Get PC coverage.
  267. auto Default = find_if(
  268. *Loc, [](const DWARFLocationExpression &L) { return !L.Range; });
  269. if (Default != Loc->end()) {
  270. // Assume the entire range is covered by a single location.
  271. ScopeBytesCovered = BytesInScope;
  272. TotalBytesCovered = BytesInScope;
  273. } else {
  274. // Caller checks this Expected result already, it cannot fail.
  275. auto ScopeRanges = cantFail(Die.getParent().getAddressRanges());
  276. for (auto Entry : *Loc) {
  277. TotalBytesCovered += Entry.Range->HighPC - Entry.Range->LowPC;
  278. uint64_t ScopeBytesCoveredByEntry = 0;
  279. // Calculate how many bytes of the parent scope this entry covers.
  280. // FIXME: In section 2.6.2 of the DWARFv5 spec it says that "The
  281. // address ranges defined by the bounded location descriptions of a
  282. // location list may overlap". So in theory a variable can have
  283. // multiple simultaneous locations, which would make this calculation
  284. // misleading because we will count the overlapped areas
  285. // twice. However, clang does not currently emit DWARF like this.
  286. for (DWARFAddressRange R : ScopeRanges) {
  287. ScopeBytesCoveredByEntry += calculateOverlap(*Entry.Range, R);
  288. }
  289. ScopeBytesCovered += ScopeBytesCoveredByEntry;
  290. if (IsEntryValue(Entry.Expr))
  291. BytesEntryValuesCovered += ScopeBytesCoveredByEntry;
  292. }
  293. }
  294. }
  295. }
  296. // Calculate the debug location statistics.
  297. if (BytesInScope) {
  298. LocStats.NumVarParam++;
  299. if (IsParam)
  300. LocStats.NumParam++;
  301. else if (IsLocalVar)
  302. LocStats.NumVar++;
  303. collectLocStats(ScopeBytesCovered, BytesInScope, LocStats.VarParamLocStats,
  304. LocStats.ParamLocStats, LocStats.LocalVarLocStats, IsParam,
  305. IsLocalVar);
  306. // Non debug entry values coverage statistics.
  307. collectLocStats(ScopeBytesCovered - BytesEntryValuesCovered, BytesInScope,
  308. LocStats.VarParamNonEntryValLocStats,
  309. LocStats.ParamNonEntryValLocStats,
  310. LocStats.LocalVarNonEntryValLocStats, IsParam, IsLocalVar);
  311. }
  312. // Collect PC range coverage data.
  313. if (DWARFDie D =
  314. Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin))
  315. Die = D;
  316. std::string VarID = constructDieID(Die, VarPrefix);
  317. FnStats.VarsInFunction.insert(VarID);
  318. GlobalStats.TotalBytesCovered += TotalBytesCovered;
  319. if (BytesInScope) {
  320. GlobalStats.ScopeBytesCovered += ScopeBytesCovered;
  321. GlobalStats.ScopeBytes += BytesInScope;
  322. GlobalStats.ScopeEntryValueBytesCovered += BytesEntryValuesCovered;
  323. if (IsParam) {
  324. GlobalStats.ParamScopeBytesCovered += ScopeBytesCovered;
  325. GlobalStats.ParamScopeBytes += BytesInScope;
  326. GlobalStats.ParamScopeEntryValueBytesCovered += BytesEntryValuesCovered;
  327. } else if (IsLocalVar) {
  328. GlobalStats.LocalVarScopeBytesCovered += ScopeBytesCovered;
  329. GlobalStats.LocalVarScopeBytes += BytesInScope;
  330. GlobalStats.LocalVarScopeEntryValueBytesCovered +=
  331. BytesEntryValuesCovered;
  332. }
  333. assert(GlobalStats.ScopeBytesCovered <= GlobalStats.ScopeBytes);
  334. }
  335. if (IsConstantMember) {
  336. FnStats.ConstantMembers++;
  337. return;
  338. }
  339. FnStats.TotalVarWithLoc += (unsigned)HasLoc;
  340. if (Die.find(dwarf::DW_AT_artificial)) {
  341. FnStats.NumArtificial++;
  342. return;
  343. }
  344. if (IsParam) {
  345. FnStats.NumParams++;
  346. if (HasType)
  347. FnStats.NumParamTypes++;
  348. if (HasSrcLoc)
  349. FnStats.NumParamSourceLocations++;
  350. if (HasLoc)
  351. FnStats.NumParamLocations++;
  352. } else if (IsLocalVar) {
  353. FnStats.NumLocalVars++;
  354. if (HasType)
  355. FnStats.NumLocalVarTypes++;
  356. if (HasSrcLoc)
  357. FnStats.NumLocalVarSourceLocations++;
  358. if (HasLoc)
  359. FnStats.NumLocalVarLocations++;
  360. }
  361. }
  362. /// Recursively collect debug info quality metrics.
  363. static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix,
  364. std::string VarPrefix, uint64_t BytesInScope,
  365. uint32_t InlineDepth,
  366. StringMap<PerFunctionStats> &FnStatMap,
  367. GlobalStats &GlobalStats,
  368. LocationStats &LocStats) {
  369. const dwarf::Tag Tag = Die.getTag();
  370. // Skip function types.
  371. if (Tag == dwarf::DW_TAG_subroutine_type)
  372. return;
  373. // Handle any kind of lexical scope.
  374. const bool IsFunction = Tag == dwarf::DW_TAG_subprogram;
  375. const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block;
  376. const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine;
  377. if (IsFunction || IsInlinedFunction || IsBlock) {
  378. // Reset VarPrefix when entering a new function.
  379. if (Die.getTag() == dwarf::DW_TAG_subprogram ||
  380. Die.getTag() == dwarf::DW_TAG_inlined_subroutine)
  381. VarPrefix = "v";
  382. // Ignore forward declarations.
  383. if (Die.find(dwarf::DW_AT_declaration))
  384. return;
  385. // Check for call sites.
  386. if (Die.find(dwarf::DW_AT_call_file) && Die.find(dwarf::DW_AT_call_line))
  387. GlobalStats.CallSiteEntries++;
  388. // PC Ranges.
  389. auto RangesOrError = Die.getAddressRanges();
  390. if (!RangesOrError) {
  391. llvm::consumeError(RangesOrError.takeError());
  392. return;
  393. }
  394. auto Ranges = RangesOrError.get();
  395. uint64_t BytesInThisScope = 0;
  396. for (auto Range : Ranges)
  397. BytesInThisScope += Range.HighPC - Range.LowPC;
  398. // Count the function.
  399. if (!IsBlock) {
  400. // Skip over abstract origins.
  401. if (Die.find(dwarf::DW_AT_inline))
  402. return;
  403. std::string FnID = constructDieID(Die);
  404. // We've seen an instance of this function.
  405. auto &FnStats = FnStatMap[FnID];
  406. FnStats.IsFunction = true;
  407. if (IsInlinedFunction) {
  408. FnStats.NumFnInlined++;
  409. if (Die.findRecursively(dwarf::DW_AT_abstract_origin))
  410. FnStats.NumAbstractOrigins++;
  411. } else {
  412. FnStats.NumFnOutOfLine++;
  413. }
  414. if (Die.findRecursively(dwarf::DW_AT_decl_file) &&
  415. Die.findRecursively(dwarf::DW_AT_decl_line))
  416. FnStats.HasSourceLocation = true;
  417. // Update function prefix.
  418. FnPrefix = FnID;
  419. }
  420. if (BytesInThisScope) {
  421. BytesInScope = BytesInThisScope;
  422. if (IsFunction)
  423. GlobalStats.FunctionSize += BytesInThisScope;
  424. else if (IsInlinedFunction && InlineDepth == 0)
  425. GlobalStats.InlineFunctionSize += BytesInThisScope;
  426. }
  427. } else {
  428. // Not a scope, visit the Die itself. It could be a variable.
  429. collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth,
  430. FnStatMap, GlobalStats, LocStats);
  431. }
  432. // Set InlineDepth correctly for child recursion
  433. if (IsFunction)
  434. InlineDepth = 0;
  435. else if (IsInlinedFunction)
  436. ++InlineDepth;
  437. // Traverse children.
  438. unsigned LexicalBlockIndex = 0;
  439. unsigned FormalParameterIndex = 0;
  440. DWARFDie Child = Die.getFirstChild();
  441. while (Child) {
  442. std::string ChildVarPrefix = VarPrefix;
  443. if (Child.getTag() == dwarf::DW_TAG_lexical_block)
  444. ChildVarPrefix += toHex(LexicalBlockIndex++) + '.';
  445. if (Child.getTag() == dwarf::DW_TAG_formal_parameter)
  446. ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.';
  447. collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope,
  448. InlineDepth, FnStatMap, GlobalStats, LocStats);
  449. Child = Child.getSibling();
  450. }
  451. }
  452. /// Print human-readable output.
  453. /// \{
  454. static void printDatum(json::OStream &J, const char *Key, json::Value Value) {
  455. J.attribute(Key, Value);
  456. LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
  457. }
  458. static void printLocationStats(json::OStream &J, const char *Key,
  459. std::vector<unsigned> &LocationStats) {
  460. J.attribute(
  461. (Twine(Key) + " with 0% of parent scope covered by DW_AT_location").str(),
  462. LocationStats[0]);
  463. LLVM_DEBUG(
  464. llvm::dbgs() << Key
  465. << " with 0% of parent scope covered by DW_AT_location: \\"
  466. << LocationStats[0] << '\n');
  467. J.attribute(
  468. (Twine(Key) + " with (0%,10%) of parent scope covered by DW_AT_location")
  469. .str(),
  470. LocationStats[1]);
  471. LLVM_DEBUG(llvm::dbgs()
  472. << Key
  473. << " with (0%,10%) of parent scope covered by DW_AT_location: "
  474. << LocationStats[1] << '\n');
  475. for (unsigned i = 2; i < NumOfCoverageCategories - 1; ++i) {
  476. J.attribute((Twine(Key) + " with [" + Twine((i - 1) * 10) + "%," +
  477. Twine(i * 10) + "%) of parent scope covered by DW_AT_location")
  478. .str(),
  479. LocationStats[i]);
  480. LLVM_DEBUG(llvm::dbgs()
  481. << Key << " with [" << (i - 1) * 10 << "%," << i * 10
  482. << "%) of parent scope covered by DW_AT_location: "
  483. << LocationStats[i]);
  484. }
  485. J.attribute(
  486. (Twine(Key) + " with 100% of parent scope covered by DW_AT_location")
  487. .str(),
  488. LocationStats[NumOfCoverageCategories - 1]);
  489. LLVM_DEBUG(
  490. llvm::dbgs() << Key
  491. << " with 100% of parent scope covered by DW_AT_location: "
  492. << LocationStats[NumOfCoverageCategories - 1]);
  493. }
  494. static void printSectionSizes(json::OStream &J, const SectionSizes &Sizes) {
  495. for (const auto &DebugSec : Sizes.DebugSectionSizes)
  496. J.attribute((Twine("#bytes in ") + DebugSec.getKey()).str(),
  497. int64_t(DebugSec.getValue()));
  498. }
  499. /// \}
  500. /// Collect debug info quality metrics for an entire DIContext.
  501. ///
  502. /// Do the impossible and reduce the quality of the debug info down to a few
  503. /// numbers. The idea is to condense the data into numbers that can be tracked
  504. /// over time to identify trends in newer compiler versions and gauge the effect
  505. /// of particular optimizations. The raw numbers themselves are not particularly
  506. /// useful, only the delta between compiling the same program with different
  507. /// compilers is.
  508. bool dwarfdump::collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
  509. const Twine &Filename,
  510. raw_ostream &OS) {
  511. StringRef FormatName = Obj.getFileFormatName();
  512. GlobalStats GlobalStats;
  513. LocationStats LocStats;
  514. StringMap<PerFunctionStats> Statistics;
  515. for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
  516. if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
  517. collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats,
  518. LocStats);
  519. /// Collect the sizes of debug sections.
  520. SectionSizes Sizes;
  521. calculateSectionSizes(Obj, Sizes, Filename);
  522. /// The version number should be increased every time the algorithm is changed
  523. /// (including bug fixes). New metrics may be added without increasing the
  524. /// version.
  525. unsigned Version = 6;
  526. unsigned VarParamTotal = 0;
  527. unsigned VarParamUnique = 0;
  528. unsigned VarParamWithLoc = 0;
  529. unsigned NumFunctions = 0;
  530. unsigned NumInlinedFunctions = 0;
  531. unsigned NumFuncsWithSrcLoc = 0;
  532. unsigned NumAbstractOrigins = 0;
  533. unsigned ParamTotal = 0;
  534. unsigned ParamWithType = 0;
  535. unsigned ParamWithLoc = 0;
  536. unsigned ParamWithSrcLoc = 0;
  537. unsigned LocalVarTotal = 0;
  538. unsigned LocalVarWithType = 0;
  539. unsigned LocalVarWithSrcLoc = 0;
  540. unsigned LocalVarWithLoc = 0;
  541. for (auto &Entry : Statistics) {
  542. PerFunctionStats &Stats = Entry.getValue();
  543. unsigned TotalVars = Stats.VarsInFunction.size() *
  544. (Stats.NumFnInlined + Stats.NumFnOutOfLine);
  545. // Count variables in global scope.
  546. if (!Stats.IsFunction)
  547. TotalVars =
  548. Stats.NumLocalVars + Stats.ConstantMembers + Stats.NumArtificial;
  549. unsigned Constants = Stats.ConstantMembers;
  550. VarParamWithLoc += Stats.TotalVarWithLoc + Constants;
  551. VarParamTotal += TotalVars;
  552. VarParamUnique += Stats.VarsInFunction.size();
  553. LLVM_DEBUG(for (auto &V
  554. : Stats.VarsInFunction) llvm::dbgs()
  555. << Entry.getKey() << ": " << V.getKey() << "\n");
  556. NumFunctions += Stats.IsFunction;
  557. NumFuncsWithSrcLoc += Stats.HasSourceLocation;
  558. NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
  559. NumAbstractOrigins += Stats.IsFunction * Stats.NumAbstractOrigins;
  560. ParamTotal += Stats.NumParams;
  561. ParamWithType += Stats.NumParamTypes;
  562. ParamWithLoc += Stats.NumParamLocations;
  563. ParamWithSrcLoc += Stats.NumParamSourceLocations;
  564. LocalVarTotal += Stats.NumLocalVars;
  565. LocalVarWithType += Stats.NumLocalVarTypes;
  566. LocalVarWithLoc += Stats.NumLocalVarLocations;
  567. LocalVarWithSrcLoc += Stats.NumLocalVarSourceLocations;
  568. }
  569. // Print summary.
  570. OS.SetBufferSize(1024);
  571. json::OStream J(OS, 2);
  572. J.objectBegin();
  573. J.attribute("version", Version);
  574. LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
  575. llvm::dbgs() << "---------------------------------\n");
  576. printDatum(J, "file", Filename.str());
  577. printDatum(J, "format", FormatName);
  578. printDatum(J, "#functions", NumFunctions);
  579. printDatum(J, "#functions with location", NumFuncsWithSrcLoc);
  580. printDatum(J, "#inlined functions", NumInlinedFunctions);
  581. printDatum(J, "#inlined functions with abstract origins", NumAbstractOrigins);
  582. // This includes local variables and formal parameters.
  583. printDatum(J, "#unique source variables", VarParamUnique);
  584. printDatum(J, "#source variables", VarParamTotal);
  585. printDatum(J, "#source variables with location", VarParamWithLoc);
  586. printDatum(J, "#call site entries", GlobalStats.CallSiteEntries);
  587. printDatum(J, "#call site DIEs", GlobalStats.CallSiteDIEs);
  588. printDatum(J, "#call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
  589. printDatum(J, "sum_all_variables(#bytes in parent scope)",
  590. GlobalStats.ScopeBytes);
  591. printDatum(J,
  592. "sum_all_variables(#bytes in any scope covered by DW_AT_location)",
  593. GlobalStats.TotalBytesCovered);
  594. printDatum(J,
  595. "sum_all_variables(#bytes in parent scope covered by "
  596. "DW_AT_location)",
  597. GlobalStats.ScopeBytesCovered);
  598. printDatum(J,
  599. "sum_all_variables(#bytes in parent scope covered by "
  600. "DW_OP_entry_value)",
  601. GlobalStats.ScopeEntryValueBytesCovered);
  602. printDatum(J, "sum_all_params(#bytes in parent scope)",
  603. GlobalStats.ParamScopeBytes);
  604. printDatum(J,
  605. "sum_all_params(#bytes in parent scope covered by DW_AT_location)",
  606. GlobalStats.ParamScopeBytesCovered);
  607. printDatum(J,
  608. "sum_all_params(#bytes in parent scope covered by "
  609. "DW_OP_entry_value)",
  610. GlobalStats.ParamScopeEntryValueBytesCovered);
  611. printDatum(J, "sum_all_local_vars(#bytes in parent scope)",
  612. GlobalStats.LocalVarScopeBytes);
  613. printDatum(J,
  614. "sum_all_local_vars(#bytes in parent scope covered by "
  615. "DW_AT_location)",
  616. GlobalStats.LocalVarScopeBytesCovered);
  617. printDatum(J,
  618. "sum_all_local_vars(#bytes in parent scope covered by "
  619. "DW_OP_entry_value)",
  620. GlobalStats.LocalVarScopeEntryValueBytesCovered);
  621. printDatum(J, "#bytes within functions", GlobalStats.FunctionSize);
  622. printDatum(J, "#bytes within inlined functions",
  623. GlobalStats.InlineFunctionSize);
  624. // Print the summary for formal parameters.
  625. printDatum(J, "#params", ParamTotal);
  626. printDatum(J, "#params with source location", ParamWithSrcLoc);
  627. printDatum(J, "#params with type", ParamWithType);
  628. printDatum(J, "#params with binary location", ParamWithLoc);
  629. // Print the summary for local variables.
  630. printDatum(J, "#local vars", LocalVarTotal);
  631. printDatum(J, "#local vars with source location", LocalVarWithSrcLoc);
  632. printDatum(J, "#local vars with type", LocalVarWithType);
  633. printDatum(J, "#local vars with binary location", LocalVarWithLoc);
  634. // Print the debug section sizes.
  635. printSectionSizes(J, Sizes);
  636. // Print the location statistics for variables (includes local variables
  637. // and formal parameters).
  638. printDatum(J, "#variables processed by location statistics",
  639. LocStats.NumVarParam);
  640. printLocationStats(J, "#variables", LocStats.VarParamLocStats);
  641. printLocationStats(J, "#variables - entry values",
  642. LocStats.VarParamNonEntryValLocStats);
  643. // Print the location statistics for formal parameters.
  644. printDatum(J, "#params processed by location statistics", LocStats.NumParam);
  645. printLocationStats(J, "#params", LocStats.ParamLocStats);
  646. printLocationStats(J, "#params - entry values",
  647. LocStats.ParamNonEntryValLocStats);
  648. // Print the location statistics for local variables.
  649. printDatum(J, "#local vars processed by location statistics",
  650. LocStats.NumVar);
  651. printLocationStats(J, "#local vars", LocStats.LocalVarLocStats);
  652. printLocationStats(J, "#local vars - entry values",
  653. LocStats.LocalVarNonEntryValLocStats);
  654. J.objectEnd();
  655. OS << '\n';
  656. LLVM_DEBUG(
  657. llvm::dbgs() << "Total Availability: "
  658. << (int)std::round((VarParamWithLoc * 100.0) / VarParamTotal)
  659. << "%\n";
  660. llvm::dbgs() << "PC Ranges covered: "
  661. << (int)std::round((GlobalStats.ScopeBytesCovered * 100.0) /
  662. GlobalStats.ScopeBytes)
  663. << "%\n");
  664. return true;
  665. }