diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/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, @@ -391,8 +393,9 @@ size_t ParseVariablesInFunctionContextRecursive( const lldb_private::SymbolContext &sc, const DWARFDIE &die, - const lldb::addr_t func_low_pc, - lldb_private::VariableList &variable_list); + const lldb::addr_t func_low_pc, lldb_private::VariableList &variable_list, + llvm::SmallVectorImpl &abstract_formal_parameters, + size_t &abstract_parameter_index); bool ClassOrStructIsVirtual(const DWARFDIE &die); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/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; @@ -3544,16 +3548,47 @@ sc.function->GetBlock(/*can_create=*/true).FindBlockByID(die.GetID()); const bool can_create = false; VariableListSP variable_list_sp = block->GetBlockVariableList(can_create); - return ParseVariablesInFunctionContextRecursive(sc, die, func_low_pc, - *variable_list_sp); + llvm::SmallVector abstract_formal_parameters; + size_t abstract_parameter_index = 0; + return ParseVariablesInFunctionContextRecursive( + sc, die, func_low_pc, *variable_list_sp, abstract_formal_parameters, + abstract_parameter_index); } 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, VariableList &variable_list, + llvm::SmallVectorImpl &abstract_formal_parameters, + size_t &abstract_parameter_index) { size_t vars_added = 0; dw_tag_t tag = die.Tag(); + // If we are parsing a formal parameter, let us make sure that we insert + // preceding abstract parameters if they were omitted from the concrete + // function instance. + if (tag == DW_TAG_formal_parameter) { + while (abstract_parameter_index < abstract_formal_parameters.size()) { + const DWARFDIE &abstract_parameter = + abstract_formal_parameters[abstract_parameter_index++]; + DWARFDIE parameter = die; + bool found = false; + while (parameter) { + parameter = parameter.GetReferencedDIE(DW_AT_abstract_origin); + if (parameter == abstract_parameter) { + found = true; + break; + } + } + if (found) + break; + VariableSP var_sp(ParseVariableDIE(sc, abstract_parameter, func_low_pc)); + if (var_sp) { + variable_list.AddVariableIfUnique(var_sp); + ++vars_added; + } + } + } + if ((tag == DW_TAG_variable) || (tag == DW_TAG_constant) || (tag == DW_TAG_formal_parameter)) { VariableSP var_sp(ParseVariableDIE(sc, die, func_low_pc)); @@ -3592,10 +3627,50 @@ block_variable_list_sp = std::make_shared(); block->SetVariableList(block_variable_list_sp); } + + llvm::SmallVector block_abstract_formal_parameters; + if (tag == DW_TAG_inlined_subroutine) { + // 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_inline_subroutine or 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 extract the parameter + // DIEs. + DWARFDIE abs_die = die; + while (abs_die && abs_die.Tag() != DW_TAG_subprogram) { + abs_die = abs_die.GetReferencedDIE(DW_AT_abstract_origin); + } + if (abs_die && abs_die.HasChildren()) { + for (DWARFDIE child = abs_die.GetFirstChild(); child; + child = child.GetSibling()) { + if (child.Tag() == DW_TAG_formal_parameter) { + block_abstract_formal_parameters.push_back(child); + } + } + } + } + + size_t block_parameter_index = 0; 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_variable_list_sp, + block_abstract_formal_parameters, block_parameter_index); + } + + // If there any remaining abstract formal parameters, parse and insert + // them as well. + while (block_parameter_index < block_abstract_formal_parameters.size()) { + const DWARFDIE &abstract_parameter = + block_abstract_formal_parameters[block_parameter_index++]; + VariableSP var_sp(ParseVariableDIE(sc, abstract_parameter, func_low_pc)); + if (var_sp) { + block_variable_list_sp->AddVariableIfUnique(var_sp); + ++vars_added; + } } break; @@ -3606,7 +3681,8 @@ for (DWARFDIE child = die.GetFirstChild(); child; child = child.GetSibling()) { vars_added += ParseVariablesInFunctionContextRecursive( - sc, child, func_low_pc, variable_list); + sc, child, func_low_pc, variable_list, abstract_formal_parameters, + abstract_parameter_index); } break; diff --git a/lldb/test/API/functionalities/unused-inlined-parameters/Makefile b/lldb/test/API/functionalities/unused-inlined-parameters/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/unused-inlined-parameters/Makefile @@ -0,0 +1,4 @@ +C_SOURCES := main.c +CFLAGS_EXTRAS := -O1 + +include Makefile.rules diff --git a/lldb/test/API/functionalities/unused-inlined-parameters/TestUnusedInlinedParameters.py b/lldb/test/API/functionalities/unused-inlined-parameters/TestUnusedInlinedParameters.py new file mode 100644 --- /dev/null +++ b/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()) diff --git a/lldb/test/API/functionalities/unused-inlined-parameters/main.c b/lldb/test/API/functionalities/unused-inlined-parameters/main.c new file mode 100644 --- /dev/null +++ b/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; +} diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/Inputs/unused-inlined-params.s b/lldb/test/Shell/SymbolFile/DWARF/x86/Inputs/unused-inlined-params.s new file mode 100644 --- /dev/null +++ b/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: diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/unused-inlined-params.test b/lldb/test/Shell/SymbolFile/DWARF/x86/unused-inlined-params.test new file mode 100644 --- /dev/null +++ b/lldb/test/Shell/SymbolFile/DWARF/x86/unused-inlined-params.test @@ -0,0 +1,49 @@ +# UNSUPPORTED: system-darwin, system-windows + +# 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: 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 = \ No newline at end of file