From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 5762 invoked by alias); 25 Feb 2004 03:06:47 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 5755 invoked from network); 25 Feb 2004 03:06:45 -0000 Received: from unknown (HELO nevyn.them.org) (66.93.172.17) by sources.redhat.com with SMTP; 25 Feb 2004 03:06:45 -0000 Received: from drow by nevyn.them.org with local (Exim 4.30 #1 (Debian)) id 1AvpNk-0002RD-UA; Tue, 24 Feb 2004 22:06:44 -0500 Date: Wed, 25 Feb 2004 03:06:00 -0000 From: Daniel Jacobowitz To: gdb-patches@sources.redhat.com Cc: Jim Blandy , Elena Zannoni Subject: [RFA/dwarf] Optimize partial DIE reading for uninteresting DIEs Message-ID: <20040225030644.GA5167@nevyn.them.org> Mail-Followup-To: gdb-patches@sources.redhat.com, Jim Blandy , Elena Zannoni Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.1i X-SW-Source: 2004-02/txt/msg00714.txt.bz2 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 * 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