Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -379,6 +379,8 @@ lldb::VariableSP ParseVariableDIE(const lldb_private::SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc); + lldb::VariableSP ParseVariableDIECached(const lldb_private::SymbolContext &sc, + const DWARFDIE &die); void ParseAndAppendGlobalVariable(const lldb_private::SymbolContext &sc, @@ -392,7 +394,13 @@ size_t ParseVariablesInFunctionContextRecursive( const lldb_private::SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc, - lldb_private::VariableList &variable_list); + llvm::SmallVectorImpl &accumulator); + + size_t PopulateBlockVariableList( + lldb_private::VariableList &variable_list, + const lldb_private::SymbolContext &sc, + const llvm::SmallVectorImpl &variable_dies, + const lldb::addr_t func_low_pc); bool ClassOrStructIsVirtual(const DWARFDIE &die); Index: lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp =================================================================== --- lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -3071,8 +3071,7 @@ m_index->GetGlobalVariables( dwarf_cu->GetNonSkeletonUnit(), [&](DWARFDIE die) { - VariableSP var_sp( - ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS)); + VariableSP var_sp(ParseVariableDIECached(sc, die)); if (var_sp) { variables->AddVariableIfUnique(var_sp); ++vars_added; @@ -3086,6 +3085,26 @@ return 0; } +VariableSP SymbolFileDWARF::ParseVariableDIECached(const SymbolContext &sc, + const DWARFDIE &die) { + if (!die) + return nullptr; + + DIEToVariableSP &die_to_variable = die.GetDWARF()->GetDIEToVariable(); + + VariableSP var_sp = die_to_variable[die.GetDIE()]; + if (var_sp) + return var_sp; + + var_sp = ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS); + if (var_sp) { + die_to_variable[die.GetDIE()] = var_sp; + if (DWARFDIE spec_die = die.GetReferencedDIE(DW_AT_specification)) + die_to_variable[spec_die.GetDIE()] = var_sp; + } + return var_sp; +} + VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc) { @@ -3095,9 +3114,6 @@ if (!die) return nullptr; - if (VariableSP var_sp = GetDIEToVariable()[die.GetDIE()]) - return var_sp; // Already been parsed! - const dw_tag_t tag = die.Tag(); ModuleSP module = GetObjectFile()->GetModule(); @@ -3107,8 +3123,6 @@ DWARFAttributes attributes; const size_t num_attributes = die.GetAttributes(attributes); - DWARFDIE spec_die; - VariableSP var_sp; const char *name = nullptr; const char *mangled = nullptr; Declaration decl; @@ -3155,9 +3169,6 @@ case DW_AT_location: location_form = form_value; break; - case DW_AT_specification: - spec_die = form_value.Reference(); - break; case DW_AT_start_scope: // TODO: Implement this. break; @@ -3168,6 +3179,7 @@ case DW_AT_description: case DW_AT_endianity: case DW_AT_segment: + case DW_AT_specification: case DW_AT_visibility: default: case DW_AT_abstract_origin: @@ -3360,7 +3372,7 @@ location.Update_DW_OP_addr(exe_file_addr); } else { // Variable didn't make it into the final executable - return var_sp; + return nullptr; } } } @@ -3406,35 +3418,25 @@ } } - if (symbol_context_scope) { - auto type_sp = std::make_shared( - *this, GetUID(type_die_form.Reference())); - - if (use_type_size_for_value && type_sp->GetType()) - location.UpdateValue( - const_value_form.Unsigned(), - type_sp->GetType()->GetByteSize(nullptr).getValueOr(0), - die.GetCU()->GetAddressByteSize()); - - var_sp = std::make_shared( - die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, - scope_ranges, &decl, location, is_external, is_artificial, - location_is_const_value_data, is_static_member); - } else { + if (!symbol_context_scope) { // Not ready to parse this variable yet. It might be a global or static // variable that is in a function scope and the function in the symbol // context wasn't filled in yet - return var_sp; + return nullptr; } - // Cache var_sp even if NULL (the variable was just a specification or was - // missing vital information to be able to be displayed in the debugger - // (missing location due to optimization, etc)) so we don't re-parse this - // DIE over and over later... - GetDIEToVariable()[die.GetDIE()] = var_sp; - if (spec_die) - GetDIEToVariable()[spec_die.GetDIE()] = var_sp; - return var_sp; + auto type_sp = std::make_shared( + *this, GetUID(type_die_form.Reference())); + + if (use_type_size_for_value && type_sp->GetType()) + location.UpdateValue(const_value_form.Unsigned(), + type_sp->GetType()->GetByteSize(nullptr).getValueOr(0), + die.GetCU()->GetAddressByteSize()); + + return std::make_shared( + die.GetID(), name, mangled, type_sp, scope, symbol_context_scope, + scope_ranges, &decl, location, is_external, is_artificial, + location_is_const_value_data, is_static_member); } DWARFDIE @@ -3498,7 +3500,9 @@ return; } - // We haven't already parsed it, lets do that now. + // We haven't parsed the variable yet, lets do that now. Also, let us include + // the variable in the relevant compilation unit's variable list, if it + // exists. VariableListSP variable_list_sp; DWARFDIE sc_parent_die = GetParentSymbolContextDIE(die); dw_tag_t parent_tag = sc_parent_die.Tag(); @@ -3525,7 +3529,7 @@ return; } - var_sp = ParseVariableDIE(sc, die, LLDB_INVALID_ADDRESS); + var_sp = ParseVariableDIECached(sc, die); if (!var_sp) return; @@ -3534,33 +3538,115 @@ variable_list_sp->AddVariableIfUnique(var_sp); } +namespace { + +using DIEVector = llvm::SmallVector; + +DIEVector MergeBlockAbstractParameters(const DWARFDIE &block_die, + DIEVector &&variable_dies) { + // DW_TAG_inline_subroutine objects may omit DW_TAG_formal_parameter in + // instances of the function when they are unused (i.e., the parameter's + // location list would be empty). The current DW_TAG_inline_subroutine may + // refer to another DW_TAG_subprogram that might actually have the definitions + // of the parameters and we need to include these so they show up in the + // variables for this function (for example, in a stack trace). Let us try to + // find the abstract subprogram that might contain the parameter definitions + // and merge with the concrete parameters. + + // Nothing to merge if the block is not an inlined function. + if (block_die.Tag() != DW_TAG_inlined_subroutine) { + return std::move(variable_dies); + } + + // Nothing to merge if the block does not have abstract parameters. + DWARFDIE abs_die = block_die.GetReferencedDIE(DW_AT_abstract_origin); + if (!abs_die || abs_die.Tag() != DW_TAG_subprogram || + !abs_die.HasChildren()) { + return std::move(variable_dies); + } + + // For each abstract parameter, if we have its concrete counterpart, insert + // it. Otherwise, insert the abstract parameter. + DIEVector::iterator concrete_it = variable_dies.begin(); + DWARFDIE abstract_child = abs_die.GetFirstChild(); + DIEVector merged; + bool did_merge_abstract = false; + for (; abstract_child; abstract_child = abstract_child.GetSibling()) { + if (abstract_child.Tag() == DW_TAG_formal_parameter) { + if (concrete_it == variable_dies.end() || + concrete_it->Tag() != DW_TAG_formal_parameter) { + // We arrived at the end of the concrete parameter list, so all + // the remaining abstract parameters must have been omitted. + // Let us insert them to the merged list here. + merged.push_back(abstract_child); + did_merge_abstract = true; + continue; + } + + DWARFDIE origin_of_concrete = + concrete_it->GetReferencedDIE(DW_AT_abstract_origin); + if (origin_of_concrete && origin_of_concrete == abstract_child) { + // The current abstract paramater is the origin of the current + // concrete parameter, just push the concrete parameter. + merged.push_back(*concrete_it); + ++concrete_it; + } else { + // Otherwise, the parameter must have been omitted from the concrete + // function, so insert the abstract one. + merged.push_back(abstract_child); + did_merge_abstract = true; + } + } + } + + // Shortcut if no merging happened. + if (!did_merge_abstract) + return std::move(variable_dies); + + // We inserted all the abstract parameters (or their concrete counterparts). + // Let us insert all the remaining concrete variables to the merged list. + // During the insertion, let us check there are no remaining concrete + // formal parameters. If that's the case, then just bailout from the merge - + // the variable list is malformed. + for (; concrete_it != variable_dies.end(); ++concrete_it) { + if (concrete_it->Tag() == DW_TAG_formal_parameter) { + return std::move(variable_dies); + } + merged.push_back(*concrete_it); + } + return std::move(merged); +} + +} // namespace + size_t SymbolFileDWARF::ParseVariablesInFunctionContext( const SymbolContext &sc, const DWARFDIE &die, const lldb::addr_t func_low_pc) { if (!die || !sc.function) return 0; - Block *block = - sc.function->GetBlock(/*can_create=*/true).FindBlockByID(die.GetID()); - const bool can_create = false; - VariableListSP variable_list_sp = block->GetBlockVariableList(can_create); + DIEVector dummy_block_variables; // The recursive call should not add anything + // to this vector because |die| should be a + // subprogram, so all variables will be added + // to the subprogram's list. return ParseVariablesInFunctionContextRecursive(sc, die, func_low_pc, - *variable_list_sp); + dummy_block_variables); } +// This method parses all the variables in the blocks in the subtree of |die|, +// and inserts them to the variable list for all the nested blocks. +// The uninserted variables for the current block are accumulated in +// |accumulator|. size_t SymbolFileDWARF::ParseVariablesInFunctionContextRecursive( const lldb_private::SymbolContext &sc, const DWARFDIE &die, - const lldb::addr_t func_low_pc, VariableList &variable_list) { + const lldb::addr_t func_low_pc, + llvm::SmallVectorImpl &accumulator) { size_t vars_added = 0; dw_tag_t tag = die.Tag(); if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter)) { - VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc)); - if (var_sp) { - variable_list.AddVariableIfUnique(var_sp); - ++vars_added; - } + accumulator.push_back(die); } switch (tag) { @@ -3592,29 +3678,46 @@ block_variable_list_sp = std::make_shared(); block->SetVariableList(block_variable_list_sp); } + + llvm::SmallVector block_variables; for (DWARFDIE child = die.GetFirstChild(); child; child = child.GetSibling()) { vars_added += ParseVariablesInFunctionContextRecursive( - sc, child, func_low_pc, *block_variable_list_sp); + sc, child, func_low_pc, block_variables); } - + block_variables = + MergeBlockAbstractParameters(die, std::move(block_variables)); + vars_added += PopulateBlockVariableList(*block_variable_list_sp, sc, + block_variables, func_low_pc); break; } default: - // Recurse to children with the same variable list. + // Recurse to children with the same variable accumulator. for (DWARFDIE child = die.GetFirstChild(); child; child = child.GetSibling()) { vars_added += ParseVariablesInFunctionContextRecursive( - sc, child, func_low_pc, variable_list); + sc, child, func_low_pc, accumulator); } - break; } return vars_added; } +size_t SymbolFileDWARF::PopulateBlockVariableList( + VariableList &variable_list, const lldb_private::SymbolContext &sc, + const llvm::SmallVectorImpl &variable_dies, + const lldb::addr_t func_low_pc) { + // Parse the variable DIEs and insert them to the list. + for (auto &die : variable_dies) { + if (VariableSP var_sp = ParseVariableDIE(sc, die, func_low_pc)) { + variable_list.AddVariableIfUnique(var_sp); + } + } + return variable_dies.size(); +} + /// Collect call site parameters in a DW_TAG_call_site DIE. static CallSiteParameterArray CollectCallSiteParameters(ModuleSP module, DWARFDIE call_site_die) { Index: lldb/test/API/functionalities/unused-inlined-parameters/Makefile =================================================================== --- /dev/null +++ lldb/test/API/functionalities/unused-inlined-parameters/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -O1 + +include Makefile.rules Index: lldb/test/API/functionalities/unused-inlined-parameters/TestUnusedInlinedParameters.py =================================================================== --- /dev/null +++ lldb/test/API/functionalities/unused-inlined-parameters/TestUnusedInlinedParameters.py @@ -0,0 +1,20 @@ +""" +Test that unused inlined parameters are displayed. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestUnusedInlinedParameters(TestBase): + mydir = TestBase.compute_mydir(__file__) + + def test_unused_inlined_parameters(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "// break here", lldb.SBFileSpec("main.c")) + + # For the unused parameters, only check the types. + self.assertEqual("void *", self.frame().FindVariable("unused1").GetType().GetName()) + self.assertEqual(42, self.frame().FindVariable("used").GetValueAsUnsigned()) + self.assertEqual("int", self.frame().FindVariable("unused2").GetType().GetName()) Index: lldb/test/API/functionalities/unused-inlined-parameters/main.c =================================================================== --- /dev/null +++ lldb/test/API/functionalities/unused-inlined-parameters/main.c @@ -0,0 +1,11 @@ +#include + +__attribute__((always_inline)) +void f(void* unused1, int used, int unused2) { + printf("f %i", used); // break here +} + +int main(int argc, char** argv) { + f(argv, 42, 1); + return 0; +} Index: lldb/test/Shell/SymbolFile/DWARF/x86/Inputs/unused-inlined-params.s =================================================================== --- /dev/null +++ lldb/test/Shell/SymbolFile/DWARF/x86/Inputs/unused-inlined-params.s @@ -0,0 +1,458 @@ +# The below program is roughly derived from the following C program. +# To see the annotated debug info, look for the section +# '.section .debug_info' below. +# +# __attribute__((always_inline)) +# void f(void* unused1, int used, int unused2, int partial, int unused3) { +# used += partial; +# printf("f %i", partial); +# printf("f %i", used); // |partial| is not live at this line. +# } +# +# void g(int unused) { +# printf("Hello"); +# } +# +# __attribute__((noinline)) +# void other() { +# f(nullptr, 1, 0, 2, 0); +# } +# +# int main(int argc, char** argv) { +# f(argv, 42, 1, argc, 2); +# g(1); +# other(); +# return 0; +# } + + .text + .file "unused-inlined-params.c" + +.Lcu_begin: + + .globl other +other: + nop +.Linlined_f_in_other: +break_at_inlined_f_in_other: + callq printf # Omitted the setup of arguments. +.Linlined_f_in_other_between_printfs: + callq printf # Omitted the setup of arguments. +.Linlined_f_in_other_end: + retq +.Lother_end: + .size other, .Lother_end-other + + .globl main +main: + .file 1 "/example" "unused-inlined-params.c" + movl $1, %esi +.Linlined_f: +break_at_inlined_f_in_main: + leal 42(%rsi), %ebx +.Linlined_f_before_printf: + callq printf # Omitted the setup of arguments. +.Linlined_f_between_printfs: +break_at_inlined_f_in_main_between_printfs: + callq printf # Omitted the setup of arguments. +.Linlined_f_end: +.Linlined_g: +break_at_inlined_g_in_main: + callq printf # Omitted the setup of arguments. +.Linlined_g_end: + callq other + retq +.Lmain_end: + .size main, .Lmain_end-main + +# Dummy printf implementation. +printf: + retq + +# Simple entry point to make the linker happy. + .globl _start +_start: + jmp main + +.Lcu_end: + + + .section .debug_loc,"",@progbits +.Ldebug_loc_partial: + .quad .Linlined_f-.Lcu_begin + .quad .Linlined_f_between_printfs-.Lcu_begin + .short 1 # Loc expr size + .byte 84 # super-register DW_OP_reg4 + .quad 0 + .quad 0 +.Ldebug_loc_used: + .quad .Linlined_f-.Lcu_begin + .quad .Linlined_f_before_printf-.Lcu_begin + .short 3 # Loc expr size + .byte 17 # DW_OP_consts + .byte 42 # value + .byte 159 # DW_OP_stack_value + .quad .Linlined_f_before_printf-.Lcu_begin + .quad .Linlined_f_end-.Lcu_begin + .short 1 # Loc expr size + .byte 83 # super-register DW_OP_reg3 + .quad 0 + .quad 0 +.Ldebug_loc_partial_in_other: + .quad .Linlined_f_in_other-.Lcu_begin + .quad .Linlined_f_in_other_between_printfs-.Lcu_begin + .short 3 # Loc expr size + .byte 17 # DW_OP_consts + .byte 2 # value + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 +.Ldebug_loc_used_in_other: + .quad .Linlined_f_in_other-.Lcu_begin + .quad .Linlined_f_in_other_end-.Lcu_begin + .short 3 # Loc expr size + .byte 17 # DW_OP_consts + .byte 1 # value + .byte 159 # DW_OP_stack_value + .quad 0 + .quad 0 + + .section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FORM_sec_offset + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 4 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 5 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 32 # DW_AT_inline + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 6 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 7 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 8 # Abbreviation Code + .byte 36 # DW_TAG_base_type + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 62 # DW_AT_encoding + .byte 11 # DW_FORM_data1 + .byte 11 # DW_AT_byte_size + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 9 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 64 # DW_AT_frame_base + .byte 24 # DW_FORM_exprloc + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 39 # DW_AT_prototyped + .byte 25 # DW_FORM_flag_present + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 63 # DW_AT_external + .byte 25 # DW_FORM_flag_present + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 10 # Abbreviation Code + .byte 5 # DW_TAG_formal_parameter + .byte 0 # DW_CHILDREN_no + .byte 2 # DW_AT_location + .byte 23 # DW_FORM_sec_offset + .byte 3 # DW_AT_name + .byte 14 # DW_FORM_strp + .byte 58 # DW_AT_decl_file + .byte 11 # DW_FORM_data1 + .byte 59 # DW_AT_decl_line + .byte 11 # DW_FORM_data1 + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 11 # Abbreviation Code + .byte 29 # DW_TAG_inlined_subroutine + .byte 1 # DW_CHILDREN_yes + .byte 49 # DW_AT_abstract_origin + .byte 19 # DW_FORM_ref4 + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 88 # DW_AT_call_file + .byte 11 # DW_FORM_data1 + .byte 89 # DW_AT_call_line + .byte 11 # DW_FORM_data1 + .byte 87 # DW_AT_call_column + .byte 11 # DW_FORM_data1 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + + .byte 12 # Abbreviation Code + .byte 15 # DW_TAG_pointer_type + .byte 0 # DW_CHILDREN_no + .byte 73 # DW_AT_type + .byte 19 # DW_FORM_ref4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 # EOM(3) + + .section .debug_info,"",@progbits +.Ldi_cu_begin: + .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .short 4 # DWARF version number + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 8 # Address Size (in bytes) + .byte 1 # Abbrev [1] DW_TAG_compile_unit + .long .Linfo_string_fname # DW_AT_name + .long .Lline_table_start0 # DW_AT_stmt_list + .quad .Lcu_begin # DW_AT_low_pc + .long .Lcu_end-.Lcu_begin # DW_AT_high_pc + +# Debug info for |f| (abstract version with all parameters). + +.Ldebug_info_f: + .byte 5 # Abbrev [5] DW_TAG_subprogram + .long .Linfo_string_f # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + # DW_AT_prototyped + # DW_AT_external + .byte 1 # DW_AT_inline +.Ldebug_info_param1: + .byte 6 # Abbrev [6] DW_TAG_formal_parameter + .long .Linfo_string_unused1 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .long .Ldebug_info_void_ptr-.Ldi_cu_begin + # DW_AT_type +.Ldebug_info_param2: + .byte 6 # Abbrev [6] DW_TAG_formal_parameter + .long .Linfo_string_used # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .long .Ldebug_info_int-.Ldi_cu_begin # DW_AT_type +.Ldebug_info_param3: + .byte 6 # Abbrev [6] DW_TAG_formal_parameter + .long .Linfo_string_unused2 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .long .Ldebug_info_int-.Ldi_cu_begin # DW_AT_type +.Ldebug_info_param4: + .byte 6 # Abbrev [6] DW_TAG_formal_parameter + .long .Linfo_string_partial # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .long .Ldebug_info_int-.Ldi_cu_begin # DW_AT_type +.Ldebug_info_param5: + .byte 6 # Abbrev [6] DW_TAG_formal_parameter + .long .Linfo_string_unused3 # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + .long .Ldebug_info_int-.Ldi_cu_begin # DW_AT_type + .byte 0 # End Of Children Mark (DW_TAG_subprogram) + +# Debug info for |g| (abstract version with all parameters). + +.Ldebug_info_g: + .byte 5 # Abbrev [5] DW_TAG_subprogram + .long .Linfo_string_g # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 4 # DW_AT_decl_line + # DW_AT_prototyped + # DW_AT_external + .byte 1 # DW_AT_inline +.Ldebug_info_g_param1: + .byte 6 # Abbrev [6] DW_TAG_formal_parameter + .long .Linfo_string_unused # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 10 # DW_AT_decl_line + .long .Ldebug_info_int-.Ldi_cu_begin + .byte 0 # End Of Children Mark (DW_TAG_subprogram) + +# Debug info for |main|. + + .byte 9 # Abbrev [9] DW_TAG_subprogram + .quad main # DW_AT_low_pc + .long .Lmain_end-main # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + .long .Linfo_string_main # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 18 # DW_AT_decl_line + # DW_AT_prototyped + .long .Ldebug_info_int-.Ldi_cu_begin # DW_AT_type + # DW_AT_external + +# Debug info for concrete |f| inlined into |main|. + + .byte 11 # Abbrev [11] DW_TAG_inlined_subroutine + .long .Ldebug_info_f-.Ldi_cu_begin + # DW_AT_abstract_origin + .quad .Linlined_f # DW_AT_low_pc + .long .Linlined_f_end-.Linlined_f # DW_AT_high_pc + .byte 1 # DW_AT_call_file + .byte 20 # DW_AT_call_line + .byte 3 # DW_AT_call_column + .byte 4 # Abbrev [4] DW_TAG_formal_parameter + .long .Ldebug_loc_used # DW_AT_location + .long .Ldebug_info_param2-.Ldi_cu_begin + # DW_AT_abstract_origin + .byte 4 # Abbrev [4] DW_TAG_formal_parameter + .long .Ldebug_loc_partial # DW_AT_location + .long .Ldebug_info_param4-.Ldi_cu_begin + # DW_AT_abstract_origin + .byte 0 # End Of Children Mark (DW_TAG_inlined_subroutine) + +# Debug info for concrete |g| inlined into |main|. + + .byte 11 # Abbrev [11] DW_TAG_inlined_subroutine + .long .Ldebug_info_g-.Ldi_cu_begin + # DW_AT_abstract_origin + .quad .Linlined_g # DW_AT_low_pc + .long .Linlined_g_end-.Linlined_g # DW_AT_high_pc + .byte 1 # DW_AT_call_file + .byte 21 # DW_AT_call_line + .byte 3 # DW_AT_call_column + .byte 0 # End Of Children Mark (DW_TAG_inlined_subroutine) + + .byte 0 # End Of Children Mark (DW_TAG_subprogram) + +# Debug info for |other|. + + .byte 9 # Abbrev [9] DW_TAG_subprogram + .quad other # DW_AT_low_pc + .long .Lother_end-other # DW_AT_high_pc + .byte 1 # DW_AT_frame_base + .byte 87 + .long .Linfo_string_other # DW_AT_name + .byte 1 # DW_AT_decl_file + .byte 15 # DW_AT_decl_line + # DW_AT_prototyped + .long .Ldebug_info_int-.Ldi_cu_begin # DW_AT_type + # DW_AT_external + +# Debug info for concrete |f| inlined into |other|. + + .byte 11 # Abbrev [11] DW_TAG_inlined_subroutine + .long .Ldebug_info_f-.Ldi_cu_begin + # DW_AT_abstract_origin + .quad .Linlined_f_in_other # DW_AT_low_pc + .long .Linlined_f_in_other_end-.Linlined_f_in_other + # DW_AT_high_pc + .byte 1 # DW_AT_call_file + .byte 16 # DW_AT_call_line + .byte 3 # DW_AT_call_column + .byte 4 # Abbrev [4] DW_TAG_formal_parameter + .long .Ldebug_loc_used_in_other # DW_AT_location + .long .Ldebug_info_param2-.Ldi_cu_begin + # DW_AT_abstract_origin + .byte 4 # Abbrev [4] DW_TAG_formal_parameter + .long .Ldebug_loc_partial_in_other # DW_AT_location + .long .Ldebug_info_param4-.Ldi_cu_begin + # DW_AT_abstract_origin + .byte 0 # End Of Children Mark (DW_TAG_inlined_subroutine) + .byte 0 # End Of Children Mark (DW_TAG_subprogram) + +.Ldebug_info_void_ptr: + .byte 7 # Abbrev [7] DW_TAG_pointer_type +.Ldebug_info_int: + .byte 8 # Abbrev [8] DW_TAG_base_type + .long .Linfo_string_int # DW_AT_name + .byte 5 # DW_AT_encoding + .byte 4 # DW_AT_byte_size + + .byte 0 # End Of Children Mark (DW_TAG_compile_unit) +.Ldebug_info_end0: + .section .debug_str,"MS",@progbits,1 +.Linfo_string_fname: + .asciz "unused-inlined-params.c" +.Linfo_string_f: + .asciz "f" +.Linfo_string_unused1: + .asciz "unused1" +.Linfo_string_used: + .asciz "used" +.Linfo_string_int: + .asciz "int" +.Linfo_string_unused2: + .asciz "unused2" +.Linfo_string_partial: + .asciz "partial" +.Linfo_string_unused3: + .asciz "unused3" +.Linfo_string_main: + .asciz "main" +.Linfo_string_g: + .asciz "g" +.Linfo_string_unused: + .asciz "unused" +.Linfo_string_other: + .asciz "other" + .section ".note.GNU-stack","",@progbits + .addrsig + .section .debug_line,"",@progbits +.Lline_table_start0: Index: lldb/test/Shell/SymbolFile/DWARF/x86/unused-inlined-params.test =================================================================== --- /dev/null +++ lldb/test/Shell/SymbolFile/DWARF/x86/unused-inlined-params.test @@ -0,0 +1,48 @@ +# RUN: llvm-mc -filetype=obj %S/Inputs/unused-inlined-params.s \ +# RUN: -triple x86_64-pc-linux -o %t.o +# RUN: %lldb %t.o -s %s -o exit | FileCheck %s + + +# In this test we verify that inlined functions still mention +# all their parameters in `frame variable`, even when those +# parameters were completely optimized away from the concrete +# instance of the inlined function in the debug info. +# The debugger should look up the parameters in the abstract +# origin of the concrete instance. + +# Let us check that unused parameters of an inlined function are listed +# at the inlined function entry. +image lookup -v -s break_at_inlined_f_in_main +# CHECK-LABEL: image lookup -v -s break_at_inlined_f_in_main +# CHECK: name = "unused1", type = "void *", location = +# CHECK: name = "used", type = "int", location = DW_OP_consts +42 +# CHECK: name = "unused2", type = "int", location = +# CHECK: name = "partial", type = "int", location = DW_OP_reg4 RSI +# CHECK: name = "unused3", type = "int", location = + +# Show variables outsid of the live range of the 'partial' parameter +# and verify that the output is as expected. +image lookup -v -s break_at_inlined_f_in_main_between_printfs +# CHECK-LABEL: image lookup -v -s break_at_inlined_f_in_main_between_printfs +# CHECK: name = "unused1", type = "void *", location = +# CHECK: name = "used", type = "int", location = DW_OP_reg3 RBX +# CHECK: name = "unused2", type = "int", location = +# Note: image lookup does not show variables outside of their +# location, so |partial| is missing here. +# CHECK-NOT: partial +# CHECK: name = "unused3", type = "int", location = + +# Check that we show parameters even if all of them are compiled away. +image lookup -v -s break_at_inlined_g_in_main +# CHECK-LABEL: image lookup -v -s break_at_inlined_g_in_main +# CHECK: name = "unused", type = "int", location = + +# Check that even the other inlined instance of f displays the correct +# parameters. +image lookup -v -s break_at_inlined_f_in_other +# CHECK-LABEL: image lookup -v -s break_at_inlined_f_in_other +# CHECK: name = "unused1", type = "void *", location = +# CHECK: name = "used", type = "int", location = DW_OP_consts +1 +# CHECK: name = "unused2", type = "int", location = +# CHECK: name = "partial", type = "int", location = DW_OP_consts +2 +# CHECK: name = "unused3", type = "int", location =