--- /dev/null
+# 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
#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"
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<uint64_t>;
+/// This maps function DIE offset to its variables.
+using InlinedVarsTyMap = llvm::DenseMap<uint64_t, InlinedVarsTy>;
+/// This represents inlined_subroutine DIE offsets.
+using InlinedFnInstacesTy = llvm::SmallVector<uint64_t>;
+
/// Holds statistics for one function (or other entity that has a PC range and
/// contains variables, such as a compile unit).
struct PerFunctionStats {
};
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.
uint32_t InlineDepth,
StringMap<PerFunctionStats> &FnStatMap,
GlobalStats &GlobalStats,
- LocationStats &LocStats) {
+ LocationStats &LocStats,
+ InlinedVarsTy *InlinedVariables) {
bool HasLoc = false;
bool HasSrcLoc = false;
bool HasType = false;
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++;
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<uint8_t> D) -> bool {
DWARFUnit *U = Die.getDwarfUnit();
DataExtractor Data(toStringRef(D),
}
// Calculate the debug location statistics.
- if (BytesInScope) {
+ if (BytesInScope && !DeferLocStats) {
LocStats.NumVarParam++;
if (IsParam)
LocStats.NumParam++;
}
}
+/// 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<PerFunctionStats> &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)
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.
// 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];
} 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
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.
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.
StringRef FormatName = Obj.getFileFormatName();
GlobalStats GlobalStats;
LocationStats LocStats;
+ InlinedVarsTyMap GlobalInlinedFnInfo;
+ InlinedFnInstacesTy InlinedFnsToBeProcessed;
StringMap<PerFunctionStats> Statistics;
- for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units())
- if (DWARFDie CUDie = CU->getNonSkeletonUnitDIE(false))
+ for (const auto &CU : static_cast<DWARFContext *>(&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;
/// 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;