+2021-04-14 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
+
+ * dwarf2/read.c (inherit_abstract_dies): Keep a reference to the
+ corresponding child of the abstract DIE when iterating the
+ children of the concrete DIE.
+
2021-04-13 Tom de Vries <tdevries@suse.de>
* ui-style.c (read_semi_number, extended_color): Change idx parameter
sect_offset_str (die->sect_off),
sect_offset_str (origin_die->sect_off));
+ /* Find if the concrete and abstract trees are structurally the
+ same. This is a shallow traversal and it is not bullet-proof;
+ the compiler can trick the debugger into believing that the trees
+ are isomorphic, whereas they actually are not. However, the
+ likelyhood of this happening is pretty low, and a full-fledged
+ check would be an overkill. */
+ bool are_isomorphic = true;
+ die_info *concrete_child = die->child;
+ die_info *abstract_child = origin_die->child;
+ while (concrete_child != nullptr || abstract_child != nullptr)
+ {
+ if (concrete_child == nullptr
+ || abstract_child == nullptr
+ || concrete_child->tag != abstract_child->tag)
+ {
+ are_isomorphic = false;
+ break;
+ }
+
+ concrete_child = concrete_child->sibling;
+ abstract_child = abstract_child->sibling;
+ }
+
+ /* Walk the origin's children in parallel to the concrete children.
+ This helps match an origin child in case the debug info misses
+ DW_AT_abstract_origin attributes. Keep in mind that the abstract
+ origin tree may not have the same tree structure as the concrete
+ DIE, though. */
+ die_info *corresponding_abstract_child
+ = are_isomorphic ? origin_die->child : nullptr;
+
std::vector<sect_offset> offsets;
for (child_die = die->child;
one. */
if (child_die->tag == DW_TAG_call_site
|| child_die->tag == DW_TAG_GNU_call_site)
- continue;
+ {
+ if (are_isomorphic)
+ corresponding_abstract_child
+ = corresponding_abstract_child->sibling;
+ continue;
+ }
/* For each CHILD_DIE, find the corresponding child of
ORIGIN_DIE. If there is more than one layer of
&child_origin_cu);
}
+ /* If missing DW_AT_abstract_origin, try the corresponding child
+ of the origin. Clang emits such lexical scopes. */
+ if (child_origin_die == child_die
+ && dwarf2_attr (child_die, DW_AT_abstract_origin, cu) == nullptr
+ && are_isomorphic
+ && child_die->tag == DW_TAG_lexical_block)
+ child_origin_die = corresponding_abstract_child;
+
/* According to DWARF3 3.3.8.2 #3 new entries without their abstract
counterpart may exist. */
if (child_origin_die != child_die)
else
offsets.push_back (child_origin_die->sect_off);
}
+
+ if (are_isomorphic)
+ corresponding_abstract_child = corresponding_abstract_child->sibling;
}
std::sort (offsets.begin (), offsets.end ());
sect_offset *offsets_end = offsets.data () + offsets.size ();
+2021-04-14 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
+
+ * gdb.opt/inline-locals.c (scoped): New function.
+ (main): Call 'scoped'.
+ * gdb.opt/inline-locals.exp: Update with "info locals" tests
+ for scoped variables.
+ * gdb.dwarf2/dw2-inline-with-lexical-scope.c: New file.
+ * gdb.dwarf2/dw2-inline-with-lexical-scope.exp: New file.
+
2021-04-14 Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
* lib/dwarf.exp (_location): Recognize DW_OP_fbreg as an op.
--- /dev/null
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2021 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef __GNUC__
+#define ATTR __attribute__((always_inline))
+#else
+#define ATTR
+#endif
+
+int global_num = 0;
+int global_value = 0;
+
+inline ATTR
+static void
+func ()
+{ /* func prologue */
+ global_num = 42;
+ int num= 42;
+ if (num > 2)
+ {
+ asm ("scope_label1: .globl scope_label1");
+ global_value = num;
+ int value = num;
+ asm ("breakpoint_label: .globl breakpoint_label");
+ value += 10;
+ asm ("scope_label2: .globl scope_label2");
+ }
+} /* func end */
+
+int
+main ()
+{ /* main prologue */
+ asm ("main_label: .globl main_label");
+ func (); /* func call */
+ asm ("main_label2: .globl main_label2");
+ return 0; /* main return */
+} /* main end */
--- /dev/null
+# Copyright 2021 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that scoped local variables in an inlined function are printed
+# properly.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets that support DWARF-2 and use
+# gas.
+if {![dwarf2_support]} {
+ return 0
+}
+
+standard_testfile .c .S
+
+# Make some DWARF for the test. The concrete inlined instance
+# (i.e. the DW_TAG_inlined_subroutine) has a DW_TAG_lexical_block that
+# does not contain a DW_AT_abstract_origin attribute. This is
+# deliberate. Bad GDB printed duplicate local variables with
+# "optimized out" values in this case.
+
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ global srcfile srcdir subdir
+ declare_labels int_label func_label num_label value_label lines_label
+
+ get_func_info main
+ set func_call [gdb_get_line_number "func call"]
+
+ set global_num_addr [gdb_target_symbol global_num]
+ set global_value_addr [gdb_target_symbol global_value]
+
+ cu {} {
+ compile_unit {
+ {language @DW_LANG_C99}
+ {name $srcfile}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ {stmt_list ${lines_label} DW_FORM_sec_offset}
+ } {
+ int_label: base_type {
+ {name "int"}
+ {byte_size 4 sdata}
+ {encoding @DW_ATE_signed}
+ }
+
+ func_label: subprogram {
+ {name func}
+ {inline 3 data1}
+ } {
+ num_label: DW_TAG_variable {
+ {name num}
+ {type :$int_label}
+ }
+ lexical_block {
+ } {
+ value_label: DW_TAG_variable {
+ {name value}
+ {type :$int_label}
+ }
+ }
+ }
+
+ subprogram {
+ {name main}
+ {external 1 flag}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ } {
+ inlined_subroutine {
+ {abstract_origin %$func_label}
+ {low_pc main_label addr}
+ {high_pc main_label2 addr}
+ {call_file 1 data1}
+ {call_line $func_call data1}
+ } {
+ DW_TAG_variable {
+ {abstract_origin %$num_label}
+ {location {addr $global_num_addr} SPECIAL_expr}
+ }
+ lexical_block {
+ {low_pc scope_label1 addr}
+ {high_pc scope_label2 addr}
+ } {
+ DW_TAG_variable {
+ {abstract_origin %$value_label}
+ {location {addr $global_value_addr} SPECIAL_expr}
+ }
+ }
+ }
+ }
+ }
+ }
+
+ lines {version 2} lines_label {
+ include_dir "${srcdir}/${subdir}"
+ file_name "$srcfile" 1
+
+ program {
+ {DW_LNE_set_address $main_start}
+ {line [gdb_get_line_number "main prologue"]}
+ {DW_LNS_copy}
+ {DW_LNE_set_address main_label}
+ {line [gdb_get_line_number "func call"]}
+ {DW_LNS_copy}
+ {DW_LNE_set_address main_label}
+ {line [gdb_get_line_number "func end"]}
+ {DW_LNS_copy}
+ {DW_LNE_set_address main_label2}
+ {line [gdb_get_line_number "main end"]}
+ {DW_LNS_copy}
+ {DW_LNE_set_address $main_end}
+ {DW_LNE_end_sequence}
+ }
+ }
+}
+
+if {[prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] {nodebug}]} {
+ return -1
+}
+
+runto breakpoint_label
+
+# Bad GDB was printing an additional "value = <optimized out>".
+gdb_test "info locals" "value = 42\r\nnum = 42"
return x * func1 (arg2);
}
+inline ATTR
+void
+scoped (int s)
+{
+ int loc1 = 10;
+ if (s > 0)
+ {
+ int loc2 = 20;
+ s++; /* bp for locals 1 */
+ if (s > 1)
+ {
+ int loc3 = 30;
+ s++; /* bp for locals 2 */
+ }
+ }
+ s++; /* bp for locals 3 */
+}
+
int main (void)
{
int val;
val = func2 (result);
result = val;
+ scoped (40);
+
return 0;
}
}
gdb_test "print array\[0\]" "\\\$$decimal = 184" "print local 3"
+
+# Test printing scoped local variables.
+
+proc check_scoped_locals {bp_label pass_re} {
+ global srcfile
+
+ set locals_bp [gdb_get_line_number $bp_label ${srcfile}]
+ gdb_breakpoint $srcfile:$locals_bp
+
+ gdb_continue_to_breakpoint "$bp_label" ".*$srcfile:$locals_bp.*"
+ set kfail_re [multi_line $pass_re ".*<optimized out>"]
+ gdb_test_multiple "info locals" "scoped info locals at $bp_label" {
+ -re -wrap $pass_re {
+ pass $gdb_test_name
+ }
+ -re -wrap $kfail_re {
+ if {[test_compiler_info {gcc-[0-8]-*-*}]} {
+ kfail gdb/25695 $gdb_test_name
+ } else {
+ fail $gdb_test_name
+ }
+ }
+ }
+}
+
+if {! $no_frames } {
+ check_scoped_locals "bp for locals 1" "loc2 = 20\r\nloc1 = 10"
+ check_scoped_locals "bp for locals 2" "loc3 = 30\r\nloc2 = 20\r\nloc1 = 10"
+ check_scoped_locals "bp for locals 3" "loc1 = 10"
+}