+
+/* Locate the compilation unit from CU's objfile which contains the
+ DIE at OFFSET. Raises an error 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;
+
+ 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)
+ {
+ if (low == 0)
+ error (_("Dwarf Error: could not find partial DIE containing "
+ "offset 0x%lx [in module %s]"),
+ (long) offset, bfd_get_filename (objfile->obfd));
+
+ 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;
+ }
+}
+
+/* Locate the compilation unit from OBJFILE which is located at exactly
+ OFFSET. Raises an error on failure. */
+
+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."), 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);
+ if (cu->dies)
+ free_die_list (cu->dies);
+
+ 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 be
+ sure to unlink it from the cache. Also release any associated storage
+ and perform cache maintenance.
+
+ Only used during partial symbol parsing. */
+
+static void
+free_stack_comp_unit (void *data)
+{
+ struct dwarf2_cu *cu = 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;
+ }
+}
+
+/* A pair of DIE offset and GDB type pointer. We store these
+ in a hash table separate from the DIEs, and preserve them
+ when the DIEs are flushed out of cache. */
+
+struct dwarf2_offset_and_type
+{
+ unsigned int offset;
+ struct type *type;
+};
+
+/* Hash function for a dwarf2_offset_and_type. */
+
+static hashval_t
+offset_and_type_hash (const void *item)
+{
+ const struct dwarf2_offset_and_type *ofs = item;
+ return ofs->offset;
+}
+
+/* Equality function for a dwarf2_offset_and_type. */
+
+static int
+offset_and_type_eq (const void *item_lhs, const void *item_rhs)
+{
+ const struct dwarf2_offset_and_type *ofs_lhs = item_lhs;
+ const struct dwarf2_offset_and_type *ofs_rhs = item_rhs;
+ return ofs_lhs->offset == ofs_rhs->offset;
+}
+
+/* Set the type associated with DIE to TYPE. Save it in CU's hash
+ table if necessary. */
+
+static void
+set_die_type (struct die_info *die, struct type *type, struct dwarf2_cu *cu)
+{
+ struct dwarf2_offset_and_type **slot, ofs;
+
+ die->type = type;
+
+ if (cu->per_cu == NULL)
+ return;
+
+ if (cu->per_cu->type_hash == NULL)
+ cu->per_cu->type_hash
+ = htab_create_alloc_ex (cu->header.length / 24,
+ offset_and_type_hash,
+ offset_and_type_eq,
+ NULL,
+ &cu->objfile->objfile_obstack,
+ hashtab_obstack_allocate,
+ dummy_obstack_deallocate);
+
+ ofs.offset = die->offset;
+ ofs.type = type;
+ slot = (struct dwarf2_offset_and_type **)
+ htab_find_slot_with_hash (cu->per_cu->type_hash, &ofs, ofs.offset, INSERT);
+ *slot = obstack_alloc (&cu->objfile->objfile_obstack, sizeof (**slot));
+ **slot = ofs;
+}
+
+/* Find the type for DIE in TYPE_HASH, or return NULL if DIE does not
+ have a saved type. */
+
+static struct type *
+get_die_type (struct die_info *die, htab_t type_hash)
+{
+ struct dwarf2_offset_and_type *slot, ofs;
+
+ ofs.offset = die->offset;
+ slot = htab_find_with_hash (type_hash, &ofs, ofs.offset);
+ if (slot)
+ return slot->type;
+ else
+ return NULL;
+}
+
+/* Restore the types of the DIE tree starting at START_DIE from the hash
+ table saved in CU. */
+
+static void
+reset_die_and_siblings_types (struct die_info *start_die, struct dwarf2_cu *cu)
+{
+ struct die_info *die;
+
+ if (cu->per_cu->type_hash == NULL)
+ return;
+
+ for (die = start_die; die != NULL; die = die->sibling)
+ {
+ die->type = get_die_type (die, cu->per_cu->type_hash);
+ if (die->child != NULL)
+ reset_die_and_siblings_types (die->child, 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. */
+
+/* Add a dependence relationship from CU to REF_PER_CU. */
+
+static void
+dwarf2_add_dependence (struct dwarf2_cu *cu,
+ struct dwarf2_per_cu_data *ref_per_cu)
+{
+ void **slot;
+
+ if (cu->dependencies == NULL)
+ cu->dependencies
+ = htab_create_alloc_ex (5, htab_hash_pointer, htab_eq_pointer,
+ NULL, &cu->comp_unit_obstack,
+ hashtab_obstack_allocate,
+ dummy_obstack_deallocate);
+
+ slot = htab_find_slot (cu->dependencies, ref_per_cu, INSERT);
+ if (*slot == NULL)
+ *slot = ref_per_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 int
+dwarf2_mark_helper (void **slot, void *data)
+{
+ struct dwarf2_per_cu_data *per_cu;
+
+ per_cu = (struct dwarf2_per_cu_data *) *slot;
+ if (per_cu->cu->mark)
+ return 1;
+ per_cu->cu->mark = 1;
+
+ if (per_cu->cu->dependencies != NULL)
+ htab_traverse (per_cu->cu->dependencies, dwarf2_mark_helper, NULL);
+
+ return 1;
+}
+
+static void
+dwarf2_mark (struct dwarf2_cu *cu)
+{
+ if (cu->mark)
+ return;
+ cu->mark = 1;
+ if (cu->dependencies != NULL)
+ htab_traverse (cu->dependencies, dwarf2_mark_helper, NULL);
+}
+
+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;
+ }
+}
+
+/* Trivial hash function for partial_die_info: the hash value of a DIE
+ is its offset in .debug_info for this objfile. */
+
+static hashval_t
+partial_die_hash (const void *item)
+{
+ const struct partial_die_info *part_die = item;
+ return part_die->offset;
+}
+
+/* Trivial comparison function for partial_die_info structures: two DIEs
+ are equal if they have the same offset. */
+
+static int
+partial_die_eq (const void *item_lhs, const void *item_rhs)
+{
+ const struct partial_die_info *part_die_lhs = item_lhs;
+ const struct partial_die_info *part_die_rhs = 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."),
+ NULL,
+ show_dwarf2_max_cache_age,
+ &set_dwarf2_cmdlist,
+ &show_dwarf2_cmdlist);
+}