[RFA/dwarf] Optimize partial DIE reading for uninteresting DIEs

Daniel Jacobowitz
2004-02-25T03:06:47Z

read_partial_die is a pretty heavyweight function. One of the largest
indicators of this is how high read_uleb128 shows up in startup profiles.
This patch provides an alternative for DIEs we know are uninteresting: we
can just read in the abbrev, and the sibling pointer if any.

Performancewise, on mainline this is a bit of a wash for typical code.
That's because we read every file-scope or namespace-scope DIE using
read_partial_die before we decide if it's interesting. A followup patch to
use peek_die_abbrev for that provides a several percent improvement in GDB
startup time.

OK to commit?

--
Daniel Jacobowitz
MontaVista Software Debian GNU/Linux Developer

2004-02-24 Daniel Jacobowitz <drow@mvista.com>

* dwarf2read.c (skip_leb128, peek_die_abbrev, skip_one_die)
(skip_children): New functions.
(locate_pdi_sibling): Call skip_children.

Index: dwarf2read.c
===================================================================
RCS file: /big/fsf/rsync/src-cvs/src/gdb/dwarf2read.c,v
retrieving revision 1.135
diff -u -p -r1.135 dwarf2read.c
--- dwarf2read.c 21 Feb 2004 02:13:35 -0000 1.135
+++ dwarf2read.c 25 Feb 2004 03:05:54 -0000
@@ -720,6 +720,8 @@ static unsigned long read_unsigned_leb12

static long read_signed_leb128 (bfd *, char *, unsigned int *);

+static char *skip_leb128 (bfd *, char *);
+
static void set_cu_language (unsigned int, struct dwarf2_cu *);

static struct attribute *dwarf2_attr (struct die_info *, unsigned int,
@@ -923,6 +925,9 @@ static void
dwarf2_symbol_mark_computed (struct attribute *attr, struct symbol *sym,
struct dwarf2_cu *cu);

+static char *skip_one_die (char *info_ptr, struct abbrev_info *abbrev,
+ struct dwarf2_cu *cu);
+
/* Try to locate the sections we need for DWARF 2 debugging
information and return true if we have enough to do something. */

@@ -1748,8 +1753,154 @@ add_partial_enumeration (struct partial_
return info_ptr;
}

-/* Locate ORIG_PDI's sibling; INFO_PTR should point to the next DIE
- after ORIG_PDI. */
+/* Read the initial uleb128 in the die at INFO_PTR in compilation unit CU.
+ Return the corresponding abbrev, or NULL if the number is zero (indicating
+ an empty DIE). In either case *BYTES_READ will be set to the length of
+ the initial number. */
+
+static struct abbrev_info *
+peek_die_abbrev (char *info_ptr, int *bytes_read, struct dwarf2_cu *cu)
+{
+ bfd *abfd = cu->objfile->obfd;
+ unsigned int abbrev_number;
+ struct abbrev_info *abbrev;
+
+ abbrev_number = read_unsigned_leb128 (abfd, info_ptr, bytes_read);
+
+ if (abbrev_number == 0)
+ return NULL;
+
+ abbrev = dwarf2_lookup_abbrev (abbrev_number, cu);
+ if (!abbrev)
+ {
+ error ("Dwarf Error: Could not find abbrev number %d [in module %s]", abbrev_number,
+ bfd_get_filename (abfd));
+ }
+
+ return abbrev;
+}
+
+/* Scan the debug information for CU starting at INFO_PTR. Returns a
+ pointer to the end of a series of DIEs, terminated by an empty
+ DIE. Any children of the skipped DIEs will also be skipped. */
+
+static char *
+skip_children (char *info_ptr, struct dwarf2_cu *cu)
+{
+ struct abbrev_info *abbrev;
+ unsigned int bytes_read;
+
+ while (1)
+ {
+ abbrev = peek_die_abbrev (info_ptr, &bytes_read, cu);
+ if (abbrev == NULL)
+ return info_ptr + bytes_read;
+ else
+ info_ptr = skip_one_die (info_ptr + bytes_read, abbrev, cu);
+ }
+}
+
+/* Scan the debug information for CU starting at INFO_PTR. INFO_PTR
+ should point just after the initial uleb128 of a DIE, and the
+ abbrev corresponding to that skipped uleb128 should be passed in
+ ABBREV. Returns a pointer to this DIE's sibling, skipping any
+ children. */
+
+static char *
+skip_one_die (char *info_ptr, struct abbrev_info *abbrev,
+ struct dwarf2_cu *cu)
+{
+ unsigned int bytes_read;
+ struct attribute attr;
+ bfd *abfd = cu->objfile->obfd;
+ unsigned int form, i;
+
+ for (i = 0; i < abbrev->num_attrs; i++)
+ {
+ /* The only abbrev we care about is DW_AT_sibling. */
+ if (abbrev->attrs[i].name == DW_AT_sibling)
+ {
+ read_attribute (&attr, &abbrev->attrs[i],
+ abfd, info_ptr, cu);
+ if (attr.form == DW_FORM_ref_addr)
+ complaint (&symfile_complaints, "ignoring absolute DW_AT_sibling");
+ else
+ return dwarf_info_buffer + dwarf2_get_ref_die_offset (&attr, cu);
+ }
+
+ /* If it isn't DW_AT_sibling, skip this attribute. */
+ form = abbrev->attrs[i].form;
+ skip_attribute:
+ switch (form)
+ {
+ case DW_FORM_addr:
+ case DW_FORM_ref_addr:
+ info_ptr += cu->header.addr_size;
+ break;
+ case DW_FORM_data1:
+ case DW_FORM_ref1:
+ case DW_FORM_flag:
+ info_ptr += 1;
+ break;
+ case DW_FORM_data2:
+ case DW_FORM_ref2:
+ info_ptr += 2;
+ break;
+ case DW_FORM_data4:
+ case DW_FORM_ref4:
+ info_ptr += 4;
+ break;
+ case DW_FORM_data8:
+ case DW_FORM_ref8:
+ info_ptr += 8;
+ break;
+ case DW_FORM_string:
+ read_string (abfd, info_ptr, &bytes_read);
+ info_ptr += bytes_read;
+ break;
+ case DW_FORM_strp:
+ info_ptr += cu->header.offset_size;
+ break;
+ case DW_FORM_block:
+ info_ptr += read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
+ info_ptr += bytes_read;
+ break;
+ case DW_FORM_block1:
+ info_ptr += 1 + read_1_byte (abfd, info_ptr);
+ break;
+ case DW_FORM_block2:
+ info_ptr += 2 + read_2_bytes (abfd, info_ptr);
+ break;
+ case DW_FORM_block4:
+ info_ptr += 4 + read_4_bytes (abfd, info_ptr);
+ break;
+ case DW_FORM_sdata:
+ case DW_FORM_udata:
+ case DW_FORM_ref_udata:
+ info_ptr = skip_leb128 (abfd, info_ptr);
+ break;
+ case DW_FORM_indirect:
+ form = read_unsigned_leb128 (abfd, info_ptr, &bytes_read);
+ info_ptr += bytes_read;
+ /* We need to continue parsing from here, so just go back to
+ the top. */
+ goto skip_attribute;
+
+ default:
+ error ("Dwarf Error: Cannot handle %s in DWARF reader [in module %s]",
+ dwarf_form_name (form),
+ bfd_get_filename (abfd));
+ }
+ }
+
+ if (abbrev->has_children)
+ return skip_children (info_ptr, cu);
+ else
+ return info_ptr;
+}
+
+/* Locate ORIG_PDI's sibling; INFO_PTR should point to the start of
+ the next DIE after ORIG_PDI. */

static char *
locate_pdi_sibling (struct partial_die_info *orig_pdi, char *info_ptr,
@@ -1765,21 +1916,9 @@ locate_pdi_sibling (struct partial_die_i
if (!orig_pdi->has_children)
return info_ptr;

- /* Okay, we don't know the sibling, but we have children that we
- want to skip. So read children until we run into one without a
- tag; return whatever follows it. */
-
- while (1)
- {
- struct partial_die_info pdi;
-
- info_ptr = read_partial_die (&pdi, abfd, info_ptr, cu);
+ /* Skip the children the long way. */

- if (pdi.tag == 0)
- return info_ptr;
- else
- info_ptr = locate_pdi_sibling (&pdi, info_ptr, abfd, cu);
- }
+ return skip_children (info_ptr, cu);
}

/* Expand this partial symbol table into a full symbol table. */
@@ -4925,6 +5064,22 @@ read_signed_leb128 (bfd *abfd, char *buf
}
*bytes_read_ptr = num_read;
return result;
+}
+
+/* Return a pointer to just past the end of an LEB128 number in BUF. */
+
+static char *
+skip_leb128 (bfd *abfd, char *buf)
+{
+ int byte;
+
+ while (1)
+ {
+ byte = bfd_get_8 (abfd, (bfd_byte *) buf);
+ buf++;
+ if ((byte & 128) == 0)
+ return buf;
+ }
}

static void