diff --git a/llvm/test/tools/llvm-dwarfdump/X86/locstats-for-inlined-vars.yaml b/llvm/test/tools/llvm-dwarfdump/X86/locstats-for-inlined-vars.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/locstats-for-inlined-vars.yaml @@ -0,0 +1,412 @@ +# RUN: yaml2obj %s | llvm-dwarfdump --statistics - | FileCheck %s + +## Check that zero coverage was reported for inlined variable with +## DW_AT_abstract_origin with no location attribute as well as +## for the variable that has not been generated within the inlined subroutine. +## +## The yaml represents DWARF as: +## +## DW_TAG_compile_unit +## DW_AT_low_pc (0x0000000000000000) +## DW_TAG_subprogram <-- (0x00000014) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_AT_inline (DW_INL_inlined) +## DW_TAG_formal_parameter <-- (0x00000018) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_TAG_formal_parameter <-- (0x0000001b) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_TAG_variable <-- (0x0000001e) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_TAG_subprogram +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_AT_low_pc (0x0000000000000000) +## DW_AT_high_pc (0x000000000000000b) +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x00000014) +## DW_AT_low_pc (0x0000000000000000) +## DW_AT_high_pc (0x0000000000000007) +## DW_AT_call_file (0x01) +## DW_AT_call_line (1) +## DW_AT_call_column (0x01) +## DW_TAG_formal_parameter +## DW_AT_abstract_origin (0x00000018) +## DW_TAG_variable +## DW_AT_abstract_origin (0x0000001e) +## DW_AT_location () +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x00000014) +## DW_AT_low_pc (0x0000000000000000) +## DW_AT_high_pc (0x0000000000000007) +## DW_AT_call_file (0x01) +## DW_AT_call_line (1) +## DW_AT_call_column (0x01) +## DW_TAG_formal_parameter +## DW_AT_abstract_origin (0x00000018) +## DW_AT_location () +## DW_TAG_variable +## DW_AT_abstract_origin (0x0000001e) +## DW_AT_location () +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x00000014) +## DW_AT_low_pc (0x0000000000000000) +## DW_AT_high_pc (0x0000000000000007) +## DW_AT_call_file (0x01) +## DW_AT_call_line (1) +## DW_AT_call_column (0x01) +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x00000014) +## DW_AT_low_pc (0x0000000000000002) +## DW_AT_high_pc (0x000000000000000a) +## DW_AT_call_file (0x01) +## DW_AT_call_line (3) +## DW_AT_call_column (0x03) +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x000000e5) +## DW_AT_low_pc (0x0000000000000006) +## DW_AT_high_pc (0x0000000000000010) +## DW_AT_call_file (0x01) +## DW_AT_call_line (3) +## DW_AT_call_column (0x03) +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x000000e5) +## DW_AT_low_pc (0x0000000000000006) +## DW_AT_high_pc (0x0000000000000010) +## DW_AT_call_file (0x01) +## DW_AT_call_line (3) +## DW_AT_call_column (0x03) +## DW_TAG_formal_parameter +## DW_AT_abstract_origin (0x000000e9) +## DW_AT_location () +## DW_TAG_inlined_subroutine +## DW_AT_abstract_origin (0x000000f0) +## DW_AT_low_pc (0x0000000000000010) +## DW_AT_high_pc (0x000000000000001a) +## DW_AT_call_file (0x01) +## DW_AT_call_line (3) +## DW_AT_call_column (0x03) +## DW_TAG_formal_parameter +## DW_AT_abstract_origin (0x000000f4) +## DW_TAG_lexical_block +## DW_AT_low_pc (0x00000000000000bc) +## DW_AT_high_pc (0x00000000000000bc) +## DW_TAG_variable +## DW_AT_abstract_origin (0x000000f8) +## DW_TAG_subprogram <-- (0x000000e5) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (3) +## DW_AT_inline (DW_INL_inlined) +## DW_TAG_formal_parameter <-- (0x000000e9) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_TAG_variable <-- (0x000000be) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_TAG_subprogram <-- (0x000000f0) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (3) +## DW_AT_inline (DW_INL_inlined) +## DW_TAG_formal_parameter <--(0x000000f4) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) +## DW_TAG_lexical_block +## DW_TAG_variable <--(0x000000f8) +## DW_AT_decl_file (0x01) +## DW_AT_decl_line (1) + +# CHECK: "version": 7, +# CHECK: "#variables processed by location statistics": 15, +# CHECK: "#variables with 0% of parent scope covered by DW_AT_location": 11, +# CHECK: "#variables with 100% of parent scope covered by DW_AT_location": 4, + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +DWARF: + debug_abbrev: + - Table: + - Code: 1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Code: 2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Attribute: DW_AT_inline + Form: DW_FORM_data1 + - Code: 3 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Code: 4 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Code: 5 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Code: 6 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 7 + Tag: DW_TAG_inlined_subroutine + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Attribute: DW_AT_call_file + Form: DW_FORM_data1 + - Attribute: DW_AT_call_line + Form: DW_FORM_data1 + - Attribute: DW_AT_call_column + Form: DW_FORM_data1 + - Code: 8 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + - Code: 9 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + - Attribute: DW_AT_location + Form: DW_FORM_exprloc + - Code: 10 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + - Attribute: DW_AT_location + Form: DW_FORM_exprloc + - Code: 11 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Attribute: DW_AT_inline + Form: DW_FORM_data1 + - Code: 12 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + - Code: 13 + Tag: DW_TAG_lexical_block + Children: DW_CHILDREN_yes + - Code: 14 + Tag: DW_TAG_lexical_block + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_data4 + - Code: 15 + Tag: DW_TAG_formal_parameter + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + - Code: 16 + Tag: DW_TAG_variable + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + debug_info: + - Version: 4 + AbbrOffset: 0x00 + Entries: + - AbbrCode: 1 ## DW_TAG_compile_unit + Values: + - Value: 0x00 ## DW_AT_producer + - AbbrCode: 2 ## DW_TAG_subprogram + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - Value: 1 ## DW_AT_inline + - AbbrCode: 3 ## DW_TAG_formal_parameter + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 4 ## DW_TAG_formal_parameter + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 5 ## DW_TAG_variable + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 0 ## NULL + - AbbrCode: 6 ## DW_TAG_subprogram + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x0b ## DW_AT_high_pc + - AbbrCode: 7 ## DW_TAG_inlined_subroutine + Values: + - Value: 0x14 ## DW_AT_abstract_origin + - Value: 0x00 ## DW_AT_low_pc + - Value: 0x07 ## DW_AT_high_pc + - Value: 1 ## DW_AT_call_file + - Value: 1 ## DW_AT_call_line + - Value: 1 ## DW_AT_call_column + - AbbrCode: 8 ## DW_TAG_formal_parameter + Values: + - Value: 0x18 ## DW_AT_abstract_origin + - AbbrCode: 9 ## DW_TAG_formal_parameter + Values: + - Value: 0x1e ## DW_AT_abstract_origin + - Value: 0x0 ## DW_AT_location + - AbbrCode: 0 ## NULL + - AbbrCode: 7 ## DW_TAG_inlined_subroutine + Values: + - Value: 0x14 ## DW_AT_abstract_origin + - Value: 0x02 ## DW_AT_low_pc + - Value: 0x08 ## DW_AT_high_pc + - Value: 1 ## DW_AT_call_file + - Value: 2 ## DW_AT_call_line + - Value: 2 ## DW_AT_call_column + - AbbrCode: 10 ## DW_TAG_formal_parameter + Values: + - Value: 0x18 ## DW_AT_abstract_origin + - Value: 0x0 ## DW_AT_location + - AbbrCode: 9 ## DW_TAG_variable + Values: + - Value: 0x1e ## DW_AT_abstract_origin + - Value: 0x0 ## DW_AT_location + - AbbrCode: 0 ## NULL + - AbbrCode: 7 ## DW_TAG_inlined_subroutine + Values: + - Value: 0x14 ## DW_AT_abstract_origin + - Value: 0x02 ## DW_AT_low_pc + - Value: 0x08 ## DW_AT_high_pc + - Value: 1 ## DW_AT_call_file + - Value: 3 ## DW_AT_call_line + - Value: 3 ## DW_AT_call_column + - AbbrCode: 0 ## NULL + - AbbrCode: 7 ## DW_TAG_inlined_subroutine + Values: + - Value: 0xe5 ## DW_AT_abstract_origin + - Value: 0x06 ## DW_AT_low_pc + - Value: 0x0a ## DW_AT_high_pc + - Value: 1 ## DW_AT_call_file + - Value: 3 ## DW_AT_call_line + - Value: 3 ## DW_AT_call_column + - AbbrCode: 0 ## NULL + - AbbrCode: 7 ## DW_TAG_inlined_subroutine + Values: + - Value: 0xe5 ## DW_AT_abstract_origin + - Value: 0x06 ## DW_AT_low_pc + - Value: 0x0a ## DW_AT_high_pc + - Value: 1 ## DW_AT_call_file + - Value: 3 ## DW_AT_call_line + - Value: 3 ## DW_AT_call_column + - AbbrCode: 10 ## DW_TAG_formal_parameter + Values: + - Value: 0xe9 ## DW_AT_abstract_origin + - Value: 0x0 ## DW_AT_location + - AbbrCode: 0 ## NULL + - AbbrCode: 7 ## DW_TAG_inlined_subroutine + Values: + - Value: 0xf0 ## DW_AT_abstract_origin + - Value: 0x10 ## DW_AT_low_pc + - Value: 0x0a ## DW_AT_high_pc + - Value: 1 ## DW_AT_call_file + - Value: 3 ## DW_AT_call_line + - Value: 3 ## DW_AT_call_column + - AbbrCode: 15 ## DW_TAG_formal_parameter + Values: + - Value: 0xf4 ## DW_AT_abstract_origin + - AbbrCode: 14 ## DW_TAG_lexical_block + Values: + - Value: 0xbc ## DW_AT_low_pc + - Value: 0x0 ## DW_AT_high_pc + - AbbrCode: 16 ## DW_TAG_variable + Values: + - Value: 0xf8 ## DW_AT_abstract_origin + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 2 ## DW_TAG_subprogram + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 3 ## DW_AT_decl_line + - Value: 1 ## DW_AT_inline + - AbbrCode: 3 ## DW_TAG_formal_parameter + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 5 ## DW_TAG_variable + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 0 ## NULL + - AbbrCode: 11 ## DW_TAG_subprogram + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 3 ## DW_AT_decl_line + - Value: 1 ## DW_AT_inline + - AbbrCode: 3 ## DW_TAG_formal_parameter + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 13 ## DW_TAG_lexical_block + - AbbrCode: 5 ## DW_TAG_variable + Values: + - Value: 1 ## DW_AT_decl_file + - Value: 1 ## DW_AT_decl_line + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL + - AbbrCode: 0 ## NULL diff --git a/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test b/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test --- a/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test @@ -69,7 +69,7 @@ # } # -CHECK: "version": 6, +CHECK: "version": 7, CHECK: "#functions": 3, CHECK: "#functions with location": 3, CHECK: "#inlined functions": 7, diff --git a/llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test b/llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test --- a/llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/statistics-v3.test @@ -64,7 +64,7 @@ # } # -CHECK: "version": 6, +CHECK: "version": 7, CHECK: "#functions": 3, CHECK: "#functions with location": 3, CHECK: "#inlined functions": 8, diff --git a/llvm/test/tools/llvm-dwarfdump/X86/statistics.ll b/llvm/test/tools/llvm-dwarfdump/X86/statistics.ll --- a/llvm/test/tools/llvm-dwarfdump/X86/statistics.ll +++ b/llvm/test/tools/llvm-dwarfdump/X86/statistics.ll @@ -1,6 +1,6 @@ ; RUN: llc -O0 %s -o - -filetype=obj \ ; RUN: | llvm-dwarfdump -statistics - | FileCheck %s -; CHECK: "version": 6, +; CHECK: "version": 7, ; namespace test { ; extern int a; diff --git a/llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml b/llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml --- a/llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml +++ b/llvm/test/tools/llvm-dwarfdump/X86/stats-scope-bytes-covered.yaml @@ -33,7 +33,7 @@ ## DW_AT_location (0x00000023: ## [0x0000000000000003, 0x0000000000000005): DW_OP_reg2 RCX) -# CHECK: "version": 6, +# CHECK: "version": 7, # CHECK: "sum_all_variables(#bytes in parent scope)": 12, # CHECK: "sum_all_variables(#bytes in any scope covered by DW_AT_location)": 8 # CHECK: "sum_all_variables(#bytes in parent scope covered by DW_AT_location)": 4 diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -8,9 +8,7 @@ #include "llvm-dwarfdump.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSet.h" -#include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/Object/ObjectFile.h" @@ -21,13 +19,23 @@ using namespace llvm::dwarfdump; using namespace llvm::object; +namespace { /// This represents the number of categories of debug location coverage being /// calculated. The first category is the number of variables with 0% location /// coverage, but the last category is the number of variables with 100% /// location coverage. constexpr int NumOfCoverageCategories = 12; -namespace { +/// This is used for zero location coverage bucket. +constexpr unsigned ZeroCoverageBucket = 0; + +/// This represents variables DIE offsets. +using InlinedVarsTy = llvm::SmallVector; +/// This maps function DIE offset to its variables. +using InlinedVarsTyMap = llvm::DenseMap; +/// This represents inlined_subroutine DIE offsets. +using InlinedFnInstacesTy = llvm::SmallVector; + /// Holds statistics for one function (or other entity that has a PC range and /// contains variables, such as a compile unit). struct PerFunctionStats { @@ -164,12 +172,14 @@ }; unsigned CoverageBucket = getCoverageBucket(); + VarParamLocStats[CoverageBucket]++; if (IsParam) ParamLocStats[CoverageBucket]++; else if (IsLocalVar) LocalVarLocStats[CoverageBucket]++; } + /// Construct an identifier for a given DIE from its Prefix, Name, DeclFileName /// and DeclLine. The identifier aims to be unique for any unique entities, /// but keeping the same among different instances of the same entity. @@ -215,7 +225,8 @@ uint32_t InlineDepth, StringMap &FnStatMap, GlobalStats &GlobalStats, - LocationStats &LocStats) { + LocationStats &LocStats, + InlinedVarsTy *InlinedVariables) { bool HasLoc = false; bool HasSrcLoc = false; bool HasType = false; @@ -228,6 +239,10 @@ bool IsConstantMember = Die.getTag() == dwarf::DW_TAG_member && Die.find(dwarf::DW_AT_const_value); + // For zero covered inlined variables the locstats will be + // calculated later. + bool DeferLocStats = false; + if (Die.getTag() == dwarf::DW_TAG_call_site || Die.getTag() == dwarf::DW_TAG_GNU_call_site) { GlobalStats.CallSiteDIEs++; @@ -256,6 +271,23 @@ if (Die.findRecursively(dwarf::DW_AT_type)) HasType = true; + // Check if it is an inlined variable. + if (Die.find(dwarf::DW_AT_abstract_origin)) { + if (Die.find(dwarf::DW_AT_location) || + Die.find(dwarf::DW_AT_const_value)) { + if (InlinedVariables) { + auto Offset = Die.find(dwarf::DW_AT_abstract_origin); + // Do not track this inlined var any more, since it has location + // coverage. + llvm::erase_value(*InlinedVariables, (*Offset).getRawUValue()); + } + } else { + // The locstats will be handled at the end of + // the collectStatsRecursive(). + DeferLocStats = true; + } + } + auto IsEntryValue = [&](ArrayRef D) -> bool { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), @@ -315,7 +347,7 @@ } // Calculate the debug location statistics. - if (BytesInScope) { + if (BytesInScope && !DeferLocStats) { LocStats.NumVarParam++; if (IsParam) LocStats.NumParam++; @@ -389,13 +421,33 @@ } } +/// Recursively collect variables from subprogram with +/// DW_AT_inline attribute. +static void collectInlinedFnInfo(DWARFDie Die, + uint64_t SPOffset, + InlinedVarsTyMap &GlobalInlinedFnInfo) { + DWARFDie Child = Die.getFirstChild(); + while (Child) { + const dwarf::Tag ChildTag = Child.getTag(); + if (ChildTag == dwarf::DW_TAG_formal_parameter || + ChildTag == dwarf::DW_TAG_variable) + GlobalInlinedFnInfo[SPOffset].push_back(Child.getOffset()); + else if (ChildTag == dwarf::DW_TAG_lexical_block) + collectInlinedFnInfo(Child, SPOffset, GlobalInlinedFnInfo); + Child = Child.getSibling(); + } +} + /// Recursively collect debug info quality metrics. static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, std::string VarPrefix, uint64_t BytesInScope, uint32_t InlineDepth, StringMap &FnStatMap, GlobalStats &GlobalStats, - LocationStats &LocStats) { + LocationStats &LocStats, + InlinedVarsTyMap &GlobalInlinedFnInfo, + InlinedFnInstacesTy &InlinedFnsToBeProcessed, + InlinedVarsTy *InlinedVarsPtr = nullptr) { const dwarf::Tag Tag = Die.getTag(); // Skip function types. if (Tag == dwarf::DW_TAG_subroutine_type) @@ -405,11 +457,28 @@ const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; - if (IsFunction || IsInlinedFunction || IsBlock) { + InlinedVarsTy InlinedVars; + // Get the vars of the inlined fn, so the locstats + // reports the missing vars (with coverage 0%). + if (IsInlinedFunction) { + auto OffsetFn = Die.find(dwarf::DW_AT_abstract_origin); + if (OffsetFn) { + uint64_t OffsetOfInlineFnCopy = (*OffsetFn).getRawUValue(); + if (GlobalInlinedFnInfo.count(OffsetOfInlineFnCopy)) { + InlinedVars = GlobalInlinedFnInfo[OffsetOfInlineFnCopy]; + InlinedVarsPtr = &InlinedVars; + } else { + // This means that the DW_AT_inline fn copy is out of order, + // so this inlined instance will be processed later. + InlinedFnsToBeProcessed.push_back(Die.getOffset()); + InlinedVarsPtr = nullptr; + } + } + } + if (IsFunction || IsInlinedFunction || IsBlock) { // Reset VarPrefix when entering a new function. - if (Die.getTag() == dwarf::DW_TAG_subprogram || - Die.getTag() == dwarf::DW_TAG_inlined_subroutine) + if (IsFunction || IsInlinedFunction) VarPrefix = "v"; // Ignore forward declarations. @@ -434,9 +503,15 @@ // Count the function. if (!IsBlock) { - // Skip over abstract origins. - if (Die.find(dwarf::DW_AT_inline)) + // Skip over abstract origins, but collect variables + // from it so it can be used for location statistics + // for inlined instancies. + if (Die.find(dwarf::DW_AT_inline)) { + uint64_t SPOffset = Die.getOffset(); + collectInlinedFnInfo(Die, SPOffset, GlobalInlinedFnInfo); return; + } + std::string FnID = constructDieID(Die); // We've seen an instance of this function. auto &FnStats = FnStatMap[FnID]; @@ -465,7 +540,7 @@ } else { // Not a scope, visit the Die itself. It could be a variable. collectStatsForDie(Die, FnPrefix, VarPrefix, BytesInScope, InlineDepth, - FnStatMap, GlobalStats, LocStats); + FnStatMap, GlobalStats, LocStats, InlinedVarsPtr); } // Set InlineDepth correctly for child recursion @@ -486,9 +561,32 @@ ChildVarPrefix += 'p' + toHex(FormalParameterIndex++) + '.'; collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, BytesInScope, - InlineDepth, FnStatMap, GlobalStats, LocStats); + InlineDepth, FnStatMap, GlobalStats, LocStats, + GlobalInlinedFnInfo, InlinedFnsToBeProcessed, + InlinedVarsPtr); Child = Child.getSibling(); } + + if (!IsInlinedFunction) + return; + + // After we have processed all vars of the inlined function, + // we want to know how many variables have no location. + for (auto Offset : InlinedVars) { + LocStats.NumVarParam++; + LocStats.VarParamLocStats[ZeroCoverageBucket]++; + auto InlineDie = Die.getDwarfUnit()->getDIEForOffset(Offset); + if (!InlineDie) + continue; + auto Tag = InlineDie.getTag(); + if (Tag == dwarf::DW_TAG_formal_parameter) { + LocStats.NumParam++; + LocStats.ParamLocStats[ZeroCoverageBucket]++; + } else if (Tag == dwarf::DW_TAG_variable) { + LocStats.NumVar++; + LocStats.LocalVarLocStats[ZeroCoverageBucket]++; + } + } } /// Print human-readable output. @@ -541,6 +639,58 @@ int64_t(DebugSec.getValue())); } +/// Stop tracking inlined variables with a location. +/// This is used for out-of-order DW_AT_inline subprograms only. +static void updateInlinedVarsCovInfo(DWARFDie InlinedFnDie, + InlinedVarsTy &InlinedVars) { + DWARFDie Child = InlinedFnDie.getFirstChild(); + while (Child) { + const dwarf::Tag ChildTag = Child.getTag(); + if ((ChildTag == dwarf::DW_TAG_formal_parameter || + ChildTag == dwarf::DW_TAG_variable) && + (Child.find(dwarf::DW_AT_location) || + Child.find(dwarf::DW_AT_const_value))) { + auto OffsetVar = Child.find(dwarf::DW_AT_abstract_origin); + if (OffsetVar) + llvm::erase_value(InlinedVars, (*OffsetVar).getRawUValue()); + } else if (ChildTag == dwarf::DW_TAG_lexical_block) + updateInlinedVarsCovInfo(Child, InlinedVars); + Child = Child.getSibling(); + } +} + +/// Collect zero location coverage for inlined variables which refer to +/// a DW_AT_inline copy of subprogram that is out of order in the DWARF. +static void +collectZeroCovInlinedVars(DWARFUnit *DwUnit, GlobalStats &GlobalStats, + LocationStats &LocStats, + InlinedVarsTyMap &GlobalInlinedFnInfo, + InlinedFnInstacesTy &InlinedFnsToBeProcessed) { + for (auto FnOffset : InlinedFnsToBeProcessed) { + DWARFDie InlinedFnDie = DwUnit->getDIEForOffset(FnOffset); + auto InlinedCopy = InlinedFnDie.find(dwarf::DW_AT_abstract_origin); + InlinedVarsTy InlinedVars; + if (!InlinedCopy) + continue; + + InlinedVars = GlobalInlinedFnInfo[(*InlinedCopy).getRawUValue()]; + updateInlinedVarsCovInfo(InlinedFnDie, InlinedVars); + + for (auto Offset : InlinedVars) { + LocStats.NumVarParam++; + LocStats.VarParamLocStats[ZeroCoverageBucket]++; + auto Tag = DwUnit->getDIEForOffset(Offset).getTag(); + if (Tag == dwarf::DW_TAG_formal_parameter) { + LocStats.NumParam++; + LocStats.ParamLocStats[ZeroCoverageBucket]++; + } else if (Tag == dwarf::DW_TAG_variable) { + LocStats.NumVar++; + LocStats.LocalVarLocStats[ZeroCoverageBucket]++; + } + } + } +} + /// \} /// Collect debug info quality metrics for an entire DIContext. @@ -557,11 +707,19 @@ StringRef FormatName = Obj.getFileFormatName(); GlobalStats GlobalStats; LocationStats LocStats; + InlinedVarsTyMap GlobalInlinedFnInfo; + InlinedFnInstacesTy InlinedFnsToBeProcessed; StringMap Statistics; - for (const auto &CU : static_cast(&DICtx)->compile_units()) - if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) + for (const auto &CU : static_cast(&DICtx)->compile_units()) { + if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false)) { collectStatsRecursive(CUDie, "/", "g", 0, 0, Statistics, GlobalStats, - LocStats); + LocStats, GlobalInlinedFnInfo, + InlinedFnsToBeProcessed); + + collectZeroCovInlinedVars(CUDie.getDwarfUnit(), GlobalStats, LocStats, + GlobalInlinedFnInfo, InlinedFnsToBeProcessed); + } + } /// Collect the sizes of debug sections. SectionSizes Sizes; @@ -570,7 +728,7 @@ /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the /// version. - unsigned Version = 6; + unsigned Version = 7; unsigned VarParamTotal = 0; unsigned VarParamUnique = 0; unsigned VarParamWithLoc = 0;