]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[gdb] Add regression test for PR symtab/33777
authorTom de Vries <tdevries@suse.de>
Tue, 20 Jan 2026 08:06:20 +0000 (09:06 +0100)
committerTom de Vries <tdevries@suse.de>
Tue, 20 Jan 2026 08:06:20 +0000 (09:06 +0100)
PR symtab/33777 reports a problem where toplevel DIEs of partial units are
read many times.

The problem manifest as a timeout when building gdb with -O0 and Asan, and
running test-case gdb.base/tls-dlobj.exp with glibc debuginfo installed.

Reproduce the problem in a simpler and quicker way, without relying on glibc
debuginfo:
- build an executable with 100 partial units
- run info line "$file:$line" a 100 times
- add a line to "maint print statistics" that tells us how many time a
  toplevel DIE is read.

Without the fix we have:
...
   Number of read units: 1
   Number of unread units: 108
   Number of read top-level DIEs: 10118
...
and with the fix we have:
...
   Number of read units: 1
   Number of unread units: 108
   Number of read top-level DIEs: 218
...

Detect the problem by asserting:
...
gdb_assert { $top_level_die <= 3 * ( $read_cu + $unread_cu ) }
...

The factor 3 is used because for some target boards $top_level_die is slightly
more than 2 * ( $read_cu + $unread_cu ).

Tested on x86_64-linux.

gdb/dwarf2/read.c
gdb/dwarf2/read.h
gdb/testsuite/gdb.dwarf2/dwz-many-2.c [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dwz-many.c [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dwz-many.exp [new file with mode: 0644]

index 98cc1aa5b9411e0b2def0d1062110e72a1a185c5..65e30891f65ba186eff0b0269fdf84fded7bcdfa 100644 (file)
@@ -1956,6 +1956,8 @@ dwarf2_base_index_functions::print_stats (struct objfile *objfile,
     }
   gdb_printf (_("  Number of read units: %d\n"), total - count);
   gdb_printf (_("  Number of unread units: %d\n"), count);
+  gdb_printf (_("  Number of read top-level DIEs: %d\n"),
+             per_objfile->per_bfd->nr_toplevel_dies_read.load ());
 }
 
 void
@@ -14068,6 +14070,7 @@ cutu_reader::read_full_die (int num_extra_attrs, bool allow_reprocess)
 die_info *
 cutu_reader::read_toplevel_die (gdb::array_view<attribute *> extra_attrs)
 {
+  m_new_cu.get ()->per_objfile->per_bfd->nr_toplevel_dies_read++;
   const gdb_byte *begin_info_ptr = m_info_ptr;
   die_info *die = this->read_full_die (extra_attrs.size (), false);
 
index 112bf7b7800792f788aff538208c8e4454abc856..742dce9f2fc9bc3864643f377471da99a87dd0e8 100644 (file)
@@ -691,6 +691,9 @@ public:
      are doing.  */
   struct tu_stats tu_stats;
 
+  /* Statistic indicating how many times a toplevel DIE was read.  */
+  std::atomic<int> nr_toplevel_dies_read = 0;
+
   /* Set of dwo_file objects.  */
   dwo_file_up_set dwo_files;
 
diff --git a/gdb/testsuite/gdb.dwarf2/dwz-many-2.c b/gdb/testsuite/gdb.dwarf2/dwz-many-2.c
new file mode 100644 (file)
index 0000000..774f0e0
--- /dev/null
@@ -0,0 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 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/>.  */
+
+int
+foo (void)
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dwz-many.c b/gdb/testsuite/gdb.dwarf2/dwz-many.c
new file mode 100644 (file)
index 0000000..86726f1
--- /dev/null
@@ -0,0 +1,24 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2026 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/>.  */
+
+extern int foo (void);
+
+int
+main (void)
+{
+  return foo ();
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dwz-many.exp b/gdb/testsuite/gdb.dwarf2/dwz-many.exp
new file mode 100644 (file)
index 0000000..551dd07
--- /dev/null
@@ -0,0 +1,81 @@
+# Copyright 2026 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/>.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile .c -dw.S -2.c
+
+# Create the DWARF.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+    for {set i 0} {$i < 100} {incr i} {
+       cu {} {
+           partial_unit {} {
+               base_type {
+                   DW_AT_name int
+                   DW_AT_byte_size 4 sdata
+                   DW_AT_encoding @DW_ATE_signed
+               }
+           }
+       }
+    }
+}
+
+if {[prepare_for_testing_full "failed to prepare" \
+        [list \
+             $testfile {debug} \
+             $srcfile {nodebug} \
+             $asm_file {nodebug} \
+             $srcfile3 {debug}]]} {
+    return -1
+}
+
+set linenr [gdb_get_line_number "return 0" $srcfile3]
+
+for {set i 0} {$i < 100} {incr i} {
+    with_test_prefix $i {
+       gdb_test "info line $srcfile3:$linenr"
+    }
+}
+
+set re \
+    [multi_line \
+        "  Number of read units: ($decimal)" \
+        "  Number of unread units: ($decimal)" \
+        "  Number of read top-level DIEs: ($decimal)" \
+        ".*"]
+
+gdb_test_multiple "maint print statistics" "" {
+    -re -wrap $re {
+       set read_cu $expect_out(1,string)
+       set unread_cu $expect_out(2,string)
+       set top_level_die $expect_out(3,string)
+    }
+}
+
+# Regression test for PR symtab/33777.  Without the fix we have:
+#   Number of read units: 1
+#   Number of unread units: 108
+#   Number of read top-level DIEs: 10118
+# and with the fix we have:
+#   Number of read units: 1
+#   Number of unread units: 108
+#   Number of read top-level DIEs: 218
+# so $top_level_die == 2 * ( $read_cu + $unread_cu ).  However, for some
+# target boards $top_level_die is slightly larger, so we use a factor 3.
+gdb_assert { $top_level_die <= 3 * ( $read_cu + $unread_cu ) }