diff options
Diffstat (limited to 'gdb/dwarf2read.c')
-rw-r--r-- | gdb/dwarf2read.c | 505 |
1 files changed, 497 insertions, 8 deletions
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c index beeed02..f31038b 100644 --- a/gdb/dwarf2read.c +++ b/gdb/dwarf2read.c @@ -45,6 +45,8 @@ #include "dwarf2loc.h" #include "cp-support.h" #include "hashtab.h" +#include "command.h" +#include "gdbcmd.h" #include <fcntl.h> #include "gdb_string.h" @@ -166,6 +168,18 @@ struct dwarf2_per_objfile char *macinfo_buffer; char *ranges_buffer; char *loc_buffer; + + /* A list of all the compilation units. This will be set if and + only if we have encountered a compilation unit with inter-CU + references. */ + struct dwarf2_per_cu_data **all_comp_units; + + /* The number of compilation units in ALL_COMP_UNITS. */ + int n_comp_units; + + /* A chain of compilation units that are currently read in, so that + they can be freed later. */ + struct dwarf2_per_cu_data *read_in_chain; }; static struct dwarf2_per_objfile *dwarf2_per_objfile; @@ -299,6 +313,25 @@ struct dwarf2_cu unit, including partial DIEs. */ struct obstack comp_unit_obstack; + /* When multiple dwarf2_cu structures are living in memory, this field + chains them all together, so that they can be released efficiently. + We will probably also want a generation counter so that most-recently-used + compilation units are cached... */ + struct dwarf2_per_cu_data *read_in_chain; + + /* Backchain to our per_cu entry if the tree has been built. */ + struct dwarf2_per_cu_data *per_cu; + + /* How many compilation units ago was this CU last referenced? */ + int last_used; + + /* Mark used when releasing cached dies. */ + unsigned int mark : 1; + + /* This flag will be set if this compilation unit might include + inter-compilation-unit references. */ + unsigned int has_form_ref_addr : 1; + /* This flag will be set if this compilation unit includes any DW_TAG_namespace DIEs. If we know that there are explicit DIEs for namespaces, we don't need to try to infer them @@ -306,6 +339,22 @@ struct dwarf2_cu unsigned int has_namespace_info : 1; }; +struct dwarf2_per_cu_data +{ + /* The start offset and length of this compilation unit. 2**31-1 + bytes should suffice to store the length of any compilation unit + - if it doesn't, GDB will fall over anyway. */ + unsigned long offset; + unsigned long length : 31; + + /* Flag indicating this compilation unit will be read in before + any of the current compilation units are processed. */ + unsigned long queued : 1; + + /* Set iff currently read in. */ + struct dwarf2_cu *cu; +}; + /* The line number information for a compilation unit (found in the .debug_line section) begins with a "statement program header", which contains the following information. */ @@ -585,6 +634,13 @@ struct field_info int nfnfields; }; +/* Loaded secondary compilation units are kept in memory until they + have not been referenced for the processing of this many + compilation units. Set this to zero to disable caching. Cache + sizes of up to at least twenty will improve startup time for + typical inter-CU-reference binaries, at an obvious memory cost. */ +static int dwarf2_max_cache_age = 5; + /* Various complaints about symbol reading that don't abort the process */ static void @@ -965,6 +1021,26 @@ static hashval_t partial_die_hash (const void *item); static int partial_die_eq (const void *item_lhs, const void *item_rhs); +static struct dwarf2_per_cu_data *dwarf2_find_containing_comp_unit + (unsigned long offset, struct objfile *objfile); + +static struct dwarf2_per_cu_data *dwarf2_find_comp_unit + (unsigned long offset, struct objfile *objfile); + +static void free_one_comp_unit (void *); + +static void free_cached_comp_units (void *); + +static void age_cached_comp_units (void); + +static void free_one_cached_comp_unit (void *); + +static void create_all_comp_units (struct objfile *); + +static void dwarf2_mark (struct dwarf2_cu *); + +static void dwarf2_clear_marks (struct dwarf2_per_cu_data *); + /* Try to locate the sections we need for DWARF 2 debugging information and return true if we have enough to do something. */ @@ -1291,10 +1367,15 @@ dwarf2_build_psymtabs_hard (struct objfile *objfile, int mainline) char *beg_of_comp_unit; struct partial_die_info comp_unit_die; struct partial_symtab *pst; + struct cleanup *back_to; CORE_ADDR lowpc, highpc, baseaddr; info_ptr = dwarf2_per_objfile->info_buffer; + /* Any cached compilation units will be linked by the per-objfile + read_in_chain. Make sure to free them when we're done. */ + back_to = make_cleanup (free_cached_comp_units, NULL); + /* Since the objects we're extracting from .debug_info vary in length, only the individual functions to extract them (like read_comp_unit_head and load_partial_die) can really know whether @@ -1335,12 +1416,13 @@ dwarf2_build_psymtabs_hard (struct objfile *objfile, int mainline) cu.list_in_scope = &file_symbols; - cu.partial_dies = NULL; - /* Read the abbrevs for this compilation unit into a table */ dwarf2_read_abbrevs (abfd, &cu); make_cleanup (dwarf2_free_abbrev_table, &cu); + if (cu.has_form_ref_addr && dwarf2_per_objfile->all_comp_units == NULL) + create_all_comp_units (objfile); + /* Read the compilation unit die */ abbrev = peek_die_abbrev (info_ptr, &bytes_read, &cu); info_ptr = read_partial_die (&comp_unit_die, abbrev, bytes_read, @@ -1356,8 +1438,8 @@ dwarf2_build_psymtabs_hard (struct objfile *objfile, int mainline) objfile->global_psymbols.next, objfile->static_psymbols.next); - if (comp_unit_die.dirname) - pst->dirname = xstrdup (comp_unit_die.dirname); + if (comp_unit_die.dirname) + pst->dirname = xstrdup (comp_unit_die.dirname); pst->read_symtab_private = (char *) obstack_alloc (&objfile->objfile_obstack, sizeof (struct dwarf2_pinfo)); @@ -1367,6 +1449,32 @@ dwarf2_build_psymtabs_hard (struct objfile *objfile, int mainline) /* Store the function that reads in the rest of the symbol table */ pst->read_symtab = dwarf2_psymtab_to_symtab; + if (dwarf2_per_objfile->all_comp_units != NULL) + { + struct dwarf2_per_cu_data *per_cu; + + per_cu = dwarf2_find_comp_unit (cu.header.offset, objfile); + + /* If this compilation unit was already read in, free the + cached copy in order to read it in again. This is + necessary because we skipped some symbols when we first + read in the compilation unit (see load_partial_dies). + This problem could be avoided, but the benefit is + unclear. */ + if (per_cu->cu != NULL) + free_one_cached_comp_unit (per_cu->cu); + + cu.per_cu = per_cu; + + /* Note that this is a pointer to our stack frame, being + added to a global data structure. It will be cleaned up + in free_stack_comp_unit when we finish with this + compilation unit. */ + per_cu->cu = &cu; + } + else + cu.per_cu = NULL; + /* Check if comp unit has_children. If so, read the rest of the partial symbols from this comp unit. If not, there's no more debug_info for this comp unit. */ @@ -1420,6 +1528,122 @@ dwarf2_build_psymtabs_hard (struct objfile *objfile, int mainline) do_cleanups (back_to_inner); } + do_cleanups (back_to); +} + +/* Load the DIEs for a secondary CU into memory. */ + +static void +load_comp_unit (struct dwarf2_per_cu_data *this_cu, struct objfile *objfile) +{ + bfd *abfd = objfile->obfd; + char *info_ptr, *beg_of_comp_unit; + struct partial_die_info comp_unit_die; + struct dwarf2_cu *cu; + struct abbrev_info *abbrev; + unsigned int bytes_read; + struct cleanup *back_to; + + info_ptr = dwarf2_per_objfile->info_buffer + this_cu->offset; + beg_of_comp_unit = info_ptr; + + cu = xmalloc (sizeof (struct dwarf2_cu)); + memset (cu, 0, sizeof (struct dwarf2_cu)); + + obstack_init (&cu->comp_unit_obstack); + + cu->objfile = objfile; + info_ptr = partial_read_comp_unit_head (&cu->header, info_ptr, abfd); + + /* Complete the cu_header. */ + cu->header.offset = beg_of_comp_unit - dwarf2_per_objfile->info_buffer; + cu->header.first_die_ptr = info_ptr; + cu->header.cu_head_ptr = beg_of_comp_unit; + + /* Read the abbrevs for this compilation unit into a table. */ + dwarf2_read_abbrevs (abfd, cu); + back_to = make_cleanup (dwarf2_free_abbrev_table, cu); + + /* Read the compilation unit die. */ + abbrev = peek_die_abbrev (info_ptr, &bytes_read, cu); + info_ptr = read_partial_die (&comp_unit_die, abbrev, bytes_read, + abfd, info_ptr, cu); + + /* Set the language we're debugging. */ + set_cu_language (comp_unit_die.language, cu); + + /* Link this compilation unit into the compilation unit tree. */ + this_cu->cu = cu; + cu->per_cu = this_cu; + + /* Check if comp unit has_children. + If so, read the rest of the partial symbols from this comp unit. + If not, there's no more debug_info for this comp unit. */ + if (comp_unit_die.has_children) + load_partial_dies (abfd, info_ptr, 0, cu); + + do_cleanups (back_to); +} + +/* Create a list of all compilation units in OBJFILE. We do this only + if an inter-comp-unit reference is found; presumably if there is one, + there will be many, and one will occur early in the .debug_info section. + So there's no point in building this list incrementally. */ + +static void +create_all_comp_units (struct objfile *objfile) +{ + int n_allocated; + int n_comp_units; + struct dwarf2_per_cu_data **all_comp_units; + char *info_ptr = dwarf2_per_objfile->info_buffer; + + n_comp_units = 0; + n_allocated = 10; + all_comp_units = xmalloc (n_allocated + * sizeof (struct dwarf2_per_cu_data *)); + + while (info_ptr < dwarf2_per_objfile->info_buffer + dwarf2_per_objfile->info_size) + { + struct comp_unit_head cu_header; + char *beg_of_comp_unit; + struct dwarf2_per_cu_data *this_cu; + unsigned long offset; + int bytes_read; + + offset = info_ptr - dwarf2_per_objfile->info_buffer; + + /* Read just enough information to find out where the next + compilation unit is. */ + cu_header.length = read_initial_length (objfile->obfd, info_ptr, + &cu_header, &bytes_read); + + /* Save the compilation unit for later lookup. */ + this_cu = obstack_alloc (&objfile->objfile_obstack, + sizeof (struct dwarf2_per_cu_data)); + memset (this_cu, 0, sizeof (*this_cu)); + this_cu->offset = offset; + this_cu->length = cu_header.length + cu_header.initial_length_size; + + if (n_comp_units == n_allocated) + { + n_allocated *= 2; + all_comp_units = xrealloc (all_comp_units, + n_allocated + * sizeof (struct dwarf2_per_cu_data *)); + } + all_comp_units[n_comp_units++] = this_cu; + + info_ptr = info_ptr + this_cu->length; + } + + dwarf2_per_objfile->all_comp_units + = obstack_alloc (&objfile->objfile_obstack, + n_comp_units * sizeof (struct dwarf2_per_cu_data *)); + memcpy (dwarf2_per_objfile->all_comp_units, all_comp_units, + n_comp_units * sizeof (struct dwarf2_per_cu_data *)); + xfree (all_comp_units); + dwarf2_per_objfile->n_comp_units = n_comp_units; } /* Process all loaded DIEs for compilation unit CU, starting at FIRST_DIE. @@ -2144,6 +2368,7 @@ psymtab_to_symtab_1 (struct partial_symtab *pst) /* We're in the global namespace. */ processing_current_prefix = ""; + memset (&cu, 0, sizeof (struct dwarf2_cu)); obstack_init (&cu.comp_unit_obstack); back_to = make_cleanup (free_stack_comp_unit, &cu); @@ -4614,6 +4839,17 @@ dwarf2_read_abbrevs (bfd *abfd, struct dwarf2_cu *cu) = xrealloc (cur_attrs, (allocated_attrs * sizeof (struct attr_abbrev))); } + + /* Record whether this compilation unit might have + inter-compilation-unit references. If we don't know what form + this attribute will have, then it might potentially be a + DW_FORM_ref_addr, so we conservatively expect inter-CU + references. */ + + if (abbrev_form == DW_FORM_ref_addr + || abbrev_form == DW_FORM_indirect) + cu->has_form_ref_addr = 1; + cur_attrs[cur_abbrev->num_attrs].name = abbrev_name; cur_attrs[cur_abbrev->num_attrs++].form = abbrev_form; abbrev_name = read_unsigned_leb128 (abfd, abbrev_ptr, &bytes_read); @@ -5066,8 +5302,25 @@ find_partial_die (unsigned long offset, struct dwarf2_cu *cu, return find_partial_die_in_comp_unit (offset, cu); } - internal_error (__FILE__, __LINE__, - "unsupported inter-compilation-unit reference"); + per_cu = dwarf2_find_containing_comp_unit (offset, cu->objfile); + + /* If this offset isn't pointing into a known compilation unit, + the debug information is probably corrupted. */ + if (per_cu == NULL) + error ("Dwarf Error: could not find partial DIE containing " + "offset 0x%lx [in module %s]", + (long) offset, bfd_get_filename (cu->objfile->obfd)); + + if (per_cu->cu == NULL) + { + load_comp_unit (per_cu, cu->objfile); + per_cu->cu->read_in_chain = dwarf2_per_objfile->read_in_chain; + dwarf2_per_objfile->read_in_chain = per_cu; + } + + per_cu->cu->last_used = 0; + *target_cu = per_cu->cu; + return find_partial_die_in_comp_unit (offset, per_cu->cu); } /* Adjust PART_DIE before generating a symbol for it. This function @@ -8812,9 +9065,81 @@ dwarf2_symbol_mark_computed (struct attribute *attr, struct symbol *sym, } } +/* Locate the compilation unit from CU's objfile which contains the + DIE at OFFSET. Returns NULL on failure. */ + +static struct dwarf2_per_cu_data * +dwarf2_find_containing_comp_unit (unsigned long offset, + struct objfile *objfile) +{ + struct dwarf2_per_cu_data *this_cu; + int low, high; + + if (dwarf2_per_objfile->all_comp_units == NULL) + error ("Dwarf Error: offset 0x%lx points outside this " + "compilation unit [in module %s]", + offset, bfd_get_filename (objfile->obfd)); + + low = 0; + high = dwarf2_per_objfile->n_comp_units - 1; + while (high > low) + { + int mid = low + (high - low) / 2; + if (dwarf2_per_objfile->all_comp_units[mid]->offset >= offset) + high = mid; + else + low = mid + 1; + } + gdb_assert (low == high); + if (dwarf2_per_objfile->all_comp_units[low]->offset > offset) + { + gdb_assert (low > 0); + gdb_assert (dwarf2_per_objfile->all_comp_units[low-1]->offset <= offset); + return dwarf2_per_objfile->all_comp_units[low-1]; + } + else + { + this_cu = dwarf2_per_objfile->all_comp_units[low]; + if (low == dwarf2_per_objfile->n_comp_units - 1 + && offset >= this_cu->offset + this_cu->length) + error ("invalid dwarf2 offset %ld", offset); + gdb_assert (offset < this_cu->offset + this_cu->length); + return this_cu; + } +} + +static struct dwarf2_per_cu_data * +dwarf2_find_comp_unit (unsigned long offset, struct objfile *objfile) +{ + struct dwarf2_per_cu_data *this_cu; + this_cu = dwarf2_find_containing_comp_unit (offset, objfile); + if (this_cu->offset != offset) + error ("no compilation unit with offset %ld\n", offset); + return this_cu; +} + +/* Release one cached compilation unit, CU. We unlink it from the tree + of compilation units, but we don't remove it from the read_in_chain; + the caller is responsible for that. */ + +static void +free_one_comp_unit (void *data) +{ + struct dwarf2_cu *cu = data; + + if (cu->per_cu != NULL) + cu->per_cu->cu = NULL; + cu->per_cu = NULL; + + obstack_free (&cu->comp_unit_obstack, NULL); + + xfree (cu); +} + /* This cleanup function is passed the address of a dwarf2_cu on the stack - when we're finished with it. We can't free the pointer itself, but - release any associated storage. + when we're finished with it. We can't free the pointer itself, but be + sure to unlink it from the cache. Also release any associated storage + and perform cache maintenance. Only used during partial symbol parsing. */ @@ -8825,6 +9150,127 @@ free_stack_comp_unit (void *data) obstack_free (&cu->comp_unit_obstack, NULL); cu->partial_dies = NULL; + + if (cu->per_cu != NULL) + { + /* This compilation unit is on the stack in our caller, so we + should not xfree it. Just unlink it. */ + cu->per_cu->cu = NULL; + cu->per_cu = NULL; + + /* If we had a per-cu pointer, then we may have other compilation + units loaded, so age them now. */ + age_cached_comp_units (); + } +} + +/* Free all cached compilation units. */ + +static void +free_cached_comp_units (void *data) +{ + struct dwarf2_per_cu_data *per_cu, **last_chain; + + per_cu = dwarf2_per_objfile->read_in_chain; + last_chain = &dwarf2_per_objfile->read_in_chain; + while (per_cu != NULL) + { + struct dwarf2_per_cu_data *next_cu; + + next_cu = per_cu->cu->read_in_chain; + + free_one_comp_unit (per_cu->cu); + *last_chain = next_cu; + + per_cu = next_cu; + } +} + +/* Increase the age counter on each cached compilation unit, and free + any that are too old. */ + +static void +age_cached_comp_units (void) +{ + struct dwarf2_per_cu_data *per_cu, **last_chain; + + dwarf2_clear_marks (dwarf2_per_objfile->read_in_chain); + per_cu = dwarf2_per_objfile->read_in_chain; + while (per_cu != NULL) + { + per_cu->cu->last_used ++; + if (per_cu->cu->last_used <= dwarf2_max_cache_age) + dwarf2_mark (per_cu->cu); + per_cu = per_cu->cu->read_in_chain; + } + + per_cu = dwarf2_per_objfile->read_in_chain; + last_chain = &dwarf2_per_objfile->read_in_chain; + while (per_cu != NULL) + { + struct dwarf2_per_cu_data *next_cu; + + next_cu = per_cu->cu->read_in_chain; + + if (!per_cu->cu->mark) + { + free_one_comp_unit (per_cu->cu); + *last_chain = next_cu; + } + else + last_chain = &per_cu->cu->read_in_chain; + + per_cu = next_cu; + } +} + +/* Remove a single compilation unit from the cache. */ + +static void +free_one_cached_comp_unit (void *target_cu) +{ + struct dwarf2_per_cu_data *per_cu, **last_chain; + + per_cu = dwarf2_per_objfile->read_in_chain; + last_chain = &dwarf2_per_objfile->read_in_chain; + while (per_cu != NULL) + { + struct dwarf2_per_cu_data *next_cu; + + next_cu = per_cu->cu->read_in_chain; + + if (per_cu->cu == target_cu) + { + free_one_comp_unit (per_cu->cu); + *last_chain = next_cu; + break; + } + else + last_chain = &per_cu->cu->read_in_chain; + + per_cu = next_cu; + } +} + +/* Set the mark field in CU and in every other compilation unit in the + cache that we must keep because we are keeping CU. */ + +static void +dwarf2_mark (struct dwarf2_cu *cu) +{ + if (cu->mark) + return; + cu->mark = 1; +} + +static void +dwarf2_clear_marks (struct dwarf2_per_cu_data *per_cu) +{ + while (per_cu) + { + per_cu->cu->mark = 0; + per_cu = per_cu->cu->read_in_chain; + } } /* Allocation function for the libiberty hash table which uses an @@ -8870,10 +9316,53 @@ partial_die_eq (const void *item_lhs, const void *item_rhs) return part_die_lhs->offset == part_die_rhs->offset; } +static struct cmd_list_element *set_dwarf2_cmdlist; +static struct cmd_list_element *show_dwarf2_cmdlist; + +static void +set_dwarf2_cmd (char *args, int from_tty) +{ + help_list (set_dwarf2_cmdlist, "maintenance set dwarf2 ", -1, gdb_stdout); +} + +static void +show_dwarf2_cmd (char *args, int from_tty) +{ + cmd_show_list (show_dwarf2_cmdlist, from_tty, ""); +} + void _initialize_dwarf2_read (void); void _initialize_dwarf2_read (void) { dwarf2_objfile_data_key = register_objfile_data (); + + add_prefix_cmd ("dwarf2", class_maintenance, set_dwarf2_cmd, + "Set DWARF 2 specific variables.\n" + "Configure DWARF 2 variables such as the cache size", + &set_dwarf2_cmdlist, "maintenance set dwarf2 ", + 0/*allow-unknown*/, &maintenance_set_cmdlist); + + add_prefix_cmd ("dwarf2", class_maintenance, show_dwarf2_cmd, + "Show DWARF 2 specific variables\n" + "Show DWARF 2 variables such as the cache size", + &show_dwarf2_cmdlist, "maintenance show dwarf2 ", + 0/*allow-unknown*/, &maintenance_show_cmdlist); + + add_setshow_zinteger_cmd ("max-cache-age", class_obscure, + &dwarf2_max_cache_age, + "Set the upper bound on the age of cached " + "dwarf2 compilation units.", + "Show the upper bound on the age of cached " + "dwarf2 compilation units.", + "A higher limit means that cached " + "compilation units will be stored\n" + "in memory longer, and more total memory will " + "be used. Zero disables\n" + "caching, which can slow down startup.", + "The upper bound on the age of cached " + "dwarf2 compilation units is %d.", + NULL, NULL, &set_dwarf2_cmdlist, + &show_dwarf2_cmdlist); } |