Skip to content

Commit 5a3556a

Browse files
jaro-sevcikJaroslav Sevcik
authored andcommitted
[lldb] Add omitted abstract formal parameters in DWARF symbol files
This patch fixes a problem introduced by clang change https://reviews.llvm.org/D95617 and described by https://bugs.llvm.org/show_bug.cgi?id=50076#c6, where inlined functions omit unused parameters both in the stack trace and in `frame var` command. With this patch, the parameters are listed correctly in the stack trace and in `frame var` command. Specifically, we parse formal parameters from the abstract version of inlined functions and use those formal parameters if they are missing from the concrete version. Differential Revision: https://reviews.llvm.org/D110571
1 parent 9ba5bb4 commit 5a3556a

File tree

7 files changed

+704
-53
lines changed

7 files changed

+704
-53
lines changed

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp

Lines changed: 149 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3092,7 +3092,7 @@ size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) {
30923092
sc.comp_unit->SetVariableList(variables);
30933093

30943094
m_index->GetGlobalVariables(*dwarf_cu, [&](DWARFDIE die) {
3095-
VariableSP var_sp(ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS));
3095+
VariableSP var_sp(ParseVariableDIECached(sc, die));
30963096
if (var_sp) {
30973097
variables->AddVariableIfUnique(var_sp);
30983098
++vars_added;
@@ -3106,6 +3106,26 @@ size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) {
31063106
return 0;
31073107
}
31083108

3109+
VariableSP SymbolFileDWARF::ParseVariableDIECached(const SymbolContext &sc,
3110+
const DWARFDIE &die) {
3111+
if (!die)
3112+
return nullptr;
3113+
3114+
DIEToVariableSP &die_to_variable = die.GetDWARF()->GetDIEToVariable();
3115+
3116+
VariableSP var_sp = die_to_variable[die.GetDIE()];
3117+
if (var_sp)
3118+
return var_sp;
3119+
3120+
var_sp = ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS);
3121+
if (var_sp) {
3122+
die_to_variable[die.GetDIE()] = var_sp;
3123+
if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification))
3124+
die_to_variable[spec_die.GetDIE()] = var_sp;
3125+
}
3126+
return var_sp;
3127+
}
3128+
31093129
VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
31103130
const DWARFDIE &die,
31113131
const lldb::addr_t func_low_pc) {
@@ -3115,9 +3135,6 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
31153135
if (!die)
31163136
return nullptr;
31173137

3118-
if (VariableSP var_sp = GetDIEToVariable()[die.GetDIE()])
3119-
return var_sp; // Already been parsed!
3120-
31213138
const dw_tag_t tag = die.Tag();
31223139
ModuleSP module = GetObjectFile()->GetModule();
31233140

@@ -3127,8 +3144,6 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
31273144

31283145
DWARFAttributes attributes;
31293146
const size_t num_attributes = die.GetAttributes(attributes);
3130-
DWARFDIE spec_die;
3131-
VariableSP var_sp;
31323147
const char *name = nullptr;
31333148
const char *mangled = nullptr;
31343149
Declaration decl;
@@ -3175,9 +3190,6 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
31753190
case DW_AT_location:
31763191
location_form = form_value;
31773192
break;
3178-
case DW_AT_specification:
3179-
spec_die = form_value.Reference();
3180-
break;
31813193
case DW_AT_start_scope:
31823194
// TODO: Implement this.
31833195
break;
@@ -3188,6 +3200,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
31883200
case DW_AT_description:
31893201
case DW_AT_endianity:
31903202
case DW_AT_segment:
3203+
case DW_AT_specification:
31913204
case DW_AT_visibility:
31923205
default:
31933206
case DW_AT_abstract_origin:
@@ -3380,7 +3393,7 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
33803393
location.Update_DW_OP_addr(exe_file_addr);
33813394
} else {
33823395
// Variable didn't make it into the final executable
3383-
return var_sp;
3396+
return nullptr;
33843397
}
33853398
}
33863399
}
@@ -3426,35 +3439,25 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
34263439
}
34273440
}
34283441

3429-
if (symbol_context_scope) {
3430-
auto type_sp = std::make_shared<SymbolFileType>(
3431-
*this, GetUID(type_die_form.Reference()));
3432-
3433-
if (use_type_size_for_value && type_sp->GetType())
3434-
location.UpdateValue(
3435-
const_value_form.Unsigned(),
3436-
type_sp->GetType()->GetByteSize(nullptr).getValueOr(0),
3437-
die.GetCU()->GetAddressByteSize());
3438-
3439-
var_sp = std::make_shared<Variable>(
3440-
die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
3441-
scope_ranges, &decl, location, is_external, is_artificial,
3442-
location_is_const_value_data, is_static_member);
3443-
} else {
3442+
if (!symbol_context_scope) {
34443443
// Not ready to parse this variable yet. It might be a global or static
34453444
// variable that is in a function scope and the function in the symbol
34463445
// context wasn't filled in yet
3447-
return var_sp;
3446+
return nullptr;
34483447
}
3449-
// Cache var_sp even if NULL (the variable was just a specification or was
3450-
// missing vital information to be able to be displayed in the debugger
3451-
// (missing location due to optimization, etc)) so we don't re-parse this
3452-
// DIE over and over later...
3453-
GetDIEToVariable()[die.GetDIE()] = var_sp;
3454-
if (spec_die)
3455-
GetDIEToVariable()[spec_die.GetDIE()] = var_sp;
34563448

3457-
return var_sp;
3449+
auto type_sp = std::make_shared<SymbolFileType>(
3450+
*this, GetUID(type_die_form.Reference()));
3451+
3452+
if (use_type_size_for_value && type_sp->GetType())
3453+
location.UpdateValue(const_value_form.Unsigned(),
3454+
type_sp->GetType()->GetByteSize(nullptr).getValueOr(0),
3455+
die.GetCU()->GetAddressByteSize());
3456+
3457+
return std::make_shared<Variable>(
3458+
die.GetID(), name, mangled, type_sp, scope, symbol_context_scope,
3459+
scope_ranges, &decl, location, is_external, is_artificial,
3460+
location_is_const_value_data, is_static_member);
34583461
}
34593462

34603463
DWARFDIE
@@ -3518,7 +3521,9 @@ void SymbolFileDWARF::ParseAndAppendGlobalVariable(
35183521
return;
35193522
}
35203523

3521-
// We haven't already parsed it, lets do that now.
3524+
// We haven't parsed the variable yet, lets do that now. Also, let us include
3525+
// the variable in the relevant compilation unit's variable list, if it
3526+
// exists.
35223527
VariableListSP variable_list_sp;
35233528
DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die);
35243529
dw_tag_t parent_tag = sc_parent_die.Tag();
@@ -3545,7 +3550,7 @@ void SymbolFileDWARF::ParseAndAppendGlobalVariable(
35453550
return;
35463551
}
35473552

3548-
var_sp = ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS);
3553+
var_sp = ParseVariableDIECached(sc, die);
35493554
if (!var_sp)
35503555
return;
35513556

@@ -3554,32 +3559,109 @@ void SymbolFileDWARF::ParseAndAppendGlobalVariable(
35543559
variable_list_sp->AddVariableIfUnique(var_sp);
35553560
}
35563561

3562+
DIEArray
3563+
SymbolFileDWARF::MergeBlockAbstractParameters(const DWARFDIE &block_die,
3564+
DIEArray &&variable_dies) {
3565+
// DW_TAG_inline_subroutine objects may omit DW_TAG_formal_parameter in
3566+
// instances of the function when they are unused (i.e., the parameter's
3567+
// location list would be empty). The current DW_TAG_inline_subroutine may
3568+
// refer to another DW_TAG_subprogram that might actually have the definitions
3569+
// of the parameters and we need to include these so they show up in the
3570+
// variables for this function (for example, in a stack trace). Let us try to
3571+
// find the abstract subprogram that might contain the parameter definitions
3572+
// and merge with the concrete parameters.
3573+
3574+
// Nothing to merge if the block is not an inlined function.
3575+
if (block_die.Tag() != DW_TAG_inlined_subroutine) {
3576+
return std::move(variable_dies);
3577+
}
3578+
3579+
// Nothing to merge if the block does not have abstract parameters.
3580+
DWARFDIE abs_die = block_die.GetReferencedDIE(DW_AT_abstract_origin);
3581+
if (!abs_die || abs_die.Tag() != DW_TAG_subprogram ||
3582+
!abs_die.HasChildren()) {
3583+
return std::move(variable_dies);
3584+
}
3585+
3586+
// For each abstract parameter, if we have its concrete counterpart, insert
3587+
// it. Otherwise, insert the abstract parameter.
3588+
DIEArray::iterator concrete_it = variable_dies.begin();
3589+
DWARFDIE abstract_child = abs_die.GetFirstChild();
3590+
DIEArray merged;
3591+
bool did_merge_abstract = false;
3592+
for (; abstract_child; abstract_child = abstract_child.GetSibling()) {
3593+
if (abstract_child.Tag() == DW_TAG_formal_parameter) {
3594+
if (concrete_it == variable_dies.end() ||
3595+
GetDIE(*concrete_it).Tag() != DW_TAG_formal_parameter) {
3596+
// We arrived at the end of the concrete parameter list, so all
3597+
// the remaining abstract parameters must have been omitted.
3598+
// Let us insert them to the merged list here.
3599+
merged.push_back(*abstract_child.GetDIERef());
3600+
did_merge_abstract = true;
3601+
continue;
3602+
}
3603+
3604+
DWARFDIE origin_of_concrete =
3605+
GetDIE(*concrete_it).GetReferencedDIE(DW_AT_abstract_origin);
3606+
if (origin_of_concrete == abstract_child) {
3607+
// The current abstract paramater is the origin of the current
3608+
// concrete parameter, just push the concrete parameter.
3609+
merged.push_back(*concrete_it);
3610+
++concrete_it;
3611+
} else {
3612+
// Otherwise, the parameter must have been omitted from the concrete
3613+
// function, so insert the abstract one.
3614+
merged.push_back(*abstract_child.GetDIERef());
3615+
did_merge_abstract = true;
3616+
}
3617+
}
3618+
}
3619+
3620+
// Shortcut if no merging happened.
3621+
if (!did_merge_abstract)
3622+
return std::move(variable_dies);
3623+
3624+
// We inserted all the abstract parameters (or their concrete counterparts).
3625+
// Let us insert all the remaining concrete variables to the merged list.
3626+
// During the insertion, let us check there are no remaining concrete
3627+
// formal parameters. If that's the case, then just bailout from the merge -
3628+
// the variable list is malformed.
3629+
for (; concrete_it != variable_dies.end(); ++concrete_it) {
3630+
if (GetDIE(*concrete_it).Tag() == DW_TAG_formal_parameter) {
3631+
return std::move(variable_dies);
3632+
}
3633+
merged.push_back(*concrete_it);
3634+
}
3635+
return std::move(merged);
3636+
}
3637+
35573638
size_t SymbolFileDWARF::ParseVariablesInFunctionContext(
35583639
const SymbolContext &sc, const DWARFDIE &die,
35593640
const lldb::addr_t func_low_pc) {
35603641
if (!die || !sc.function)
35613642
return 0;
35623643

3563-
VariableList empty_variable_list;
3564-
// Since |die| corresponds to a Block instance, the recursive call will get
3565-
// a variable list from the block. |empty_variable_list| should remain empty.
3644+
DIEArray dummy_block_variables; // The recursive call should not add anything
3645+
// to this vector because |die| should be a
3646+
// subprogram, so all variables will be added
3647+
// to the subprogram's list.
35663648
return ParseVariablesInFunctionContextRecursive(sc, die, func_low_pc,
3567-
empty_variable_list);
3649+
dummy_block_variables);
35683650
}
35693651

3652+
// This method parses all the variables in the blocks in the subtree of |die|,
3653+
// and inserts them to the variable list for all the nested blocks.
3654+
// The uninserted variables for the current block are accumulated in
3655+
// |accumulator|.
35703656
size_t SymbolFileDWARF::ParseVariablesInFunctionContextRecursive(
35713657
const lldb_private::SymbolContext &sc, const DWARFDIE &die,
3572-
const lldb::addr_t func_low_pc, VariableList &variable_list) {
3658+
lldb::addr_t func_low_pc, DIEArray &accumulator) {
35733659
size_t vars_added = 0;
35743660
dw_tag_t tag = die.Tag();
35753661

35763662
if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) ||
35773663
(tag == DW_TAG_formal_parameter)) {
3578-
VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc));
3579-
if (var_sp) {
3580-
variable_list.AddVariableIfUnique(var_sp);
3581-
++vars_added;
3582-
}
3664+
accumulator.push_back(*die.GetDIERef());
35833665
}
35843666

35853667
switch (tag) {
@@ -3611,29 +3693,45 @@ size_t SymbolFileDWARF::ParseVariablesInFunctionContextRecursive(
36113693
block_variable_list_sp = std::make_shared<VariableList>();
36123694
block->SetVariableList(block_variable_list_sp);
36133695
}
3696+
3697+
DIEArray block_variables;
36143698
for (DWARFDIE child = die.GetFirstChild(); child;
36153699
child = child.GetSibling()) {
36163700
vars_added += ParseVariablesInFunctionContextRecursive(
3617-
sc, child, func_low_pc, *block_variable_list_sp);
3701+
sc, child, func_low_pc, block_variables);
36183702
}
3619-
3703+
block_variables =
3704+
MergeBlockAbstractParameters(die, std::move(block_variables));
3705+
vars_added += PopulateBlockVariableList(*block_variable_list_sp, sc,
3706+
block_variables, func_low_pc);
36203707
break;
36213708
}
36223709

36233710
default:
3624-
// Recurse to children with the same variable list.
3711+
// Recurse to children with the same variable accumulator.
36253712
for (DWARFDIE child = die.GetFirstChild(); child;
36263713
child = child.GetSibling()) {
36273714
vars_added += ParseVariablesInFunctionContextRecursive(
3628-
sc, child, func_low_pc, variable_list);
3715+
sc, child, func_low_pc, accumulator);
36293716
}
3630-
36313717
break;
36323718
}
36333719

36343720
return vars_added;
36353721
}
36363722

3723+
size_t SymbolFileDWARF::PopulateBlockVariableList(
3724+
VariableList &variable_list, const lldb_private::SymbolContext &sc,
3725+
llvm::ArrayRef<DIERef> variable_dies, lldb::addr_t func_low_pc) {
3726+
// Parse the variable DIEs and insert them to the list.
3727+
for (auto &die : variable_dies) {
3728+
if (VariableSP var_sp = ParseVariableDIE(sc, GetDIE(die), func_low_pc)) {
3729+
variable_list.AddVariableIfUnique(var_sp);
3730+
}
3731+
}
3732+
return variable_dies.size();
3733+
}
3734+
36373735
/// Collect call site parameters in a DW_TAG_call_site DIE.
36383736
static CallSiteParameterArray
36393737
CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) {

lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ class SymbolFileDWARF : public lldb_private::SymbolFile,
379379
lldb::VariableSP ParseVariableDIE(const lldb_private::SymbolContext &sc,
380380
const DWARFDIE &die,
381381
const lldb::addr_t func_low_pc);
382+
lldb::VariableSP ParseVariableDIECached(const lldb_private::SymbolContext &sc,
383+
const DWARFDIE &die);
382384

383385
void
384386
ParseAndAppendGlobalVariable(const lldb_private::SymbolContext &sc,
@@ -391,8 +393,15 @@ class SymbolFileDWARF : public lldb_private::SymbolFile,
391393

392394
size_t ParseVariablesInFunctionContextRecursive(
393395
const lldb_private::SymbolContext &sc, const DWARFDIE &die,
394-
const lldb::addr_t func_low_pc,
395-
lldb_private::VariableList &variable_list);
396+
lldb::addr_t func_low_pc, DIEArray &accumulator);
397+
398+
size_t PopulateBlockVariableList(lldb_private::VariableList &variable_list,
399+
const lldb_private::SymbolContext &sc,
400+
llvm::ArrayRef<DIERef> variable_dies,
401+
lldb::addr_t func_low_pc);
402+
403+
DIEArray MergeBlockAbstractParameters(const DWARFDIE &block_die,
404+
DIEArray &&variable_dies);
396405

397406
bool ClassOrStructIsVirtual(const DWARFDIE &die);
398407

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
CFLAGS_EXTRAS := -O1
3+
4+
include Makefile.rules
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Test that unused inlined parameters are displayed.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.lldbtest import *
7+
from lldbsuite.test import lldbutil
8+
9+
10+
class TestUnusedInlinedParameters(TestBase):
11+
mydir = TestBase.compute_mydir(__file__)
12+
13+
def test_unused_inlined_parameters(self):
14+
self.build()
15+
lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c"))
16+
17+
# For the unused parameters, only check the types.
18+
self.assertIn("(void *) unused1 = <no location, value may have been optimized out>",
19+
lldbutil.get_description(self.frame().FindVariable("unused1")))
20+
self.assertEqual(42, self.frame().FindVariable("used").GetValueAsUnsigned())
21+
self.assertIn("(int) unused2 = <no location, value may have been optimized out>",
22+
lldbutil.get_description(self.frame().FindVariable("unused2")))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <stdio.h>
2+
3+
__attribute__((optnone)) __attribute__((nodebug)) void use(int used) {}
4+
5+
__attribute__((always_inline)) void f(void *unused1, int used, int unused2) {
6+
use(used); // break here
7+
}
8+
9+
int main(int argc, char **argv) {
10+
f(argv, 42, 1);
11+
return 0;
12+
}

0 commit comments

Comments
 (0)