along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "dwarf2/cooked-index-entry.h"
+#include "dwarf2/read.h"
#include "dwarf2/tag.h"
#include "gdbsupport/selftest.h"
MAP_ENUM_FLAG (IS_TYPE_DECLARATION),
MAP_ENUM_FLAG (IS_PARENT_DEFERRED),
MAP_ENUM_FLAG (IS_SYNTHESIZED),
+ MAP_ENUM_FLAG (IS_INLINED),
};
return flags.to_string (mapping);
obstack_grow (storage, sep, strlen (sep));
}
+/* See cooked_index_entry.h. */
+
+void
+cooked_index_entry::force_set_language () const
+{
+ gdb_assert (lang == language_unknown);
+ lang = per_cu->lang ();
+}
+
+/* See cooked-index-entry.h. */
+
+bool
+cooked_index_entry::visit_defining_cus (per_cu_callback callback) const
+{
+ if ((flags & IS_INLINED) == 0)
+ return callback (per_cu->canonical_outermost_cu ());
+
+ return per_cu->recursively_visit_cus (callback);
+}
+
+/* See cooked-index-entry.h. */
+
INIT_GDB_FILE (dwarf2_entry)
{
#if GDB_SELF_TEST
/* True if this entry was synthesized by gdb (as opposed to coming
directly from the DWARF). */
IS_SYNTHESIZED = 32,
+ /* True if this is a function that has DW_AT_inline set in a way
+ that indicates it was inlined. */
+ IS_INLINED = 64,
};
DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag);
return m_parent_entry.deferred;
}
+ /* Force the language to be set to the CU's language. This may only
+ be called when the language is unknown, which can only happen
+ with .gdb_index. This method is const because it is called from
+ a location that normally handles const entries. */
+ void force_set_language () const;
+
+ /* Type of callback used when visiting defining CUs. */
+ using per_cu_callback = gdb::function_view<bool (dwarf2_per_cu *)>;
+
+ /* Calls CALLBACK for each CU that "defines" this entry.
+
+ If any call to CALLBACK returns false, this immediately returns
+ false (skipping the remaining calls); otherwise returns true.
+
+ For most entries, there is a single defining CU, which is the
+ canonical outermost includer of the CU holding the entry. In the
+ most typical case (i.e., where "dwz" has not been used), this is
+ just the entry's CU.
+
+ However, conceptually an inlined subroutine is defined in each
+ such outermost includer -- if a subroutine is inlined in many
+ places, and then "dwz" is run, the IS_INLINED subroutine may be
+ defined in some CU that is included by many other CUs. */
+ bool visit_defining_cus (per_cu_callback callback) const;
+
/* The name as it appears in DWARF. This always points into one of
the mapped DWARF sections. Note that this may be the name or the
linkage name -- two entries are created for DIEs which have both
enum dwarf_tag tag;
/* Any flags attached to this entry. */
cooked_index_flag flags;
- /* The language of this symbol. */
- ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
+ /* The language of this symbol. This is mutable because there is a
+ situation where the language is initially unknown, and then only
+ filled in later. In particular this can happen when using
+ .gdb_index. See also cooked_index_functions::search. */
+ mutable ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
/* The offset of this DIE. */
sect_offset die_offset;
- /* The CU from which this entry originates. */
+ /* The CU from which this entry originates. This may point to a
+ partial CU, which can't be expanded in isolation; see
+ visit_defining_cus. */
dwarf2_per_cu *per_cu;
private:
/* See cooked-index-worker.h. */
+void
+cooked_index_worker_result::invert_cu_inclusions ()
+{
+ for (auto &iter : m_inclusions)
+ for (dwarf2_per_cu *included : iter.second)
+ included->add_includer (iter.first);
+}
+
+/* See cooked-index-worker.h. */
+
void
cooked_index_worker::start ()
{
m_all_parents_map.add_map (*one_result.get_parent_map ());
}
+ /* Update all the CU inclusion information. */
+ for (auto &item : m_results)
+ item.invert_cu_inclusions ();
+
dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
cooked_index *table
= (gdb::checked_static_cast<cooked_index *>
/* Add an entry to the index. The arguments describe the entry; see
cooked-index.h. The new entry is returned. */
- cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
- cooked_index_flag flags,
- const char *name,
- cooked_index_entry_ref parent_entry,
- dwarf2_per_cu *per_cu)
- {
- return m_shard->add (die_offset, tag, flags, per_cu->lang (),
- name, parent_entry, per_cu);
- }
-
- /* Overload that allows the language to be specified. */
cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
cooked_index_flag flags, enum language lang,
const char *name,
void emit_complaints_and_exceptions
(gdb::unordered_set<gdb_exception> &seen_exceptions);
+ /* Record that the FROM CU included the TO CU. */
+ void record_inclusion (dwarf2_per_cu *from, dwarf2_per_cu *to)
+ {
+ m_inclusions[from].push_back (to);
+ }
+
+ /* Update CUs with all the inclusion information that we've
+ discovered. */
+ void invert_cu_inclusions ();
+
private:
/* The abbrev table cache used by this indexer. */
abbrev_table_cache m_abbrev_table_cache;
/* Exceptions that we're storing to emit later. */
std::vector<gdb_exception> m_exceptions;
+
+ /* Map from a CU to a list of all the CUs that it directly
+ includes. */
+ gdb::unordered_map<dwarf2_per_cu *,
+ std::vector<dwarf2_per_cu *>> m_inclusions;
};
/* The possible states of the index. See the explanatory comment
/* See cooked-indexer.h. */
cooked_indexer::cooked_indexer (cooked_index_worker_result *storage,
- dwarf2_per_cu *per_cu, enum language language)
+ enum language language)
: m_index_storage (storage),
- m_per_cu (per_cu),
m_language (language),
m_die_range_map (storage->get_parent_map ())
{
Doing this check here avoids self-imports as well. */
if (for_scanning)
{
+ /* Record the inclusion precisely here. Inclusions are done to
+ ensure that imported units cause the correct expansions when
+ searched; and in particular that all including units are
+ expanded when finding an inline function. So, this must be
+ done for all units that are importing "for scanning".
+ However, doing this any later than this spot would be too
+ late -- because we want to do this for all importing units,
+ not just the one that happens to win the race to do the
+ scanning. */
+ m_index_storage->record_inclusion (reader->cu ()->per_cu, per_cu);
+
bool nope = false;
if (!per_cu->scanned.compare_exchange_strong (nope, true))
return nullptr;
const cooked_index_entry **parent_entry,
parent_map::addr_type *maybe_defer,
bool *is_enum_class,
+ bool *is_inlined,
bool for_specification)
{
bool is_declaration = false;
scanning_per_cu, abbrev->tag);
}
break;
+
+ case DW_AT_inline:
+ if (abbrev->tag == DW_TAG_subprogram)
+ {
+ std::optional<ULONGEST> val = attr.unsigned_constant ();
+ if (val.has_value () && (*val == DW_INL_inlined
+ || *val == DW_INL_declared_inlined))
+ *is_inlined = true;
+ }
+ break;
}
}
scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
new_info_ptr, new_abbrev, name, linkage_name,
flags, nullptr, parent_entry, maybe_defer,
- is_enum_class, true);
+ is_enum_class, is_inlined, true);
}
if (!for_specification)
if (!target.has_value ())
return info_ptr;
- cutu_reader *new_reader
- = ensure_cu_exists (reader, *target, true);
+ cutu_reader *new_reader = ensure_cu_exists (reader, *target, true);
if (new_reader != nullptr)
index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
sect_offset sibling {};
const cooked_index_entry *this_parent_entry = parent_entry;
bool is_enum_class = false;
+ bool is_inlined = false;
/* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
its surrounding subroutine. */
info_ptr
= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
abbrev, &name, &linkage_name, &flags, &sibling,
- &this_parent_entry, &defer, &is_enum_class, false);
+ &this_parent_entry, &defer, &is_enum_class,
+ &is_inlined, false);
/* A DW_TAG_entry_point inherits its static/extern property from
the enclosing subroutine. */
if (abbrev->tag == DW_TAG_entry_point)
}
}
+ if (is_inlined)
+ flags |= IS_INLINED;
+
cooked_index_entry *this_entry = nullptr;
+ /* Always use the reader's CU for the entry CU. */
+ dwarf2_per_cu *cu_for_entry = reader->cu ()->per_cu;
if (name != nullptr)
{
if (defer != 0)
this_entry
= m_index_storage->add (this_die, abbrev->tag,
- flags | IS_PARENT_DEFERRED, name,
- defer, m_per_cu);
+ flags | IS_PARENT_DEFERRED,
+ m_language, name,
+ defer, cu_for_entry);
else
this_entry
- = m_index_storage->add (this_die, abbrev->tag, flags, name,
- this_parent_entry, m_per_cu);
+ = m_index_storage->add (this_die, abbrev->tag, flags,
+ m_language, name,
+ this_parent_entry, cu_for_entry);
}
if (linkage_name != nullptr)
have linkage name present but name is absent. */
if (name != nullptr
|| (abbrev->tag != DW_TAG_subprogram
- && abbrev->tag != DW_TAG_inlined_subroutine
&& abbrev->tag != DW_TAG_entry_point))
flags = flags | IS_LINKAGE;
- m_index_storage->add (this_die, abbrev->tag, flags,
- linkage_name, nullptr, m_per_cu);
+ m_index_storage->add (this_die, abbrev->tag, flags, m_language,
+ linkage_name, nullptr, cu_for_entry);
}
if (abbrev->has_children)
class cooked_indexer
{
public:
- cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu,
+ cooked_indexer (cooked_index_worker_result *storage,
enum language language);
DISABLE_COPY_AND_ASSIGN (cooked_indexer);
const cooked_index_entry **parent_entry,
parent_map::addr_type *maybe_defer,
bool *is_enum_class,
+ bool *is_inlined,
bool for_specification);
/* Handle DW_TAG_imported_unit, by scanning the DIE to find
/* The storage object, where the results are kept. */
cooked_index_worker_result *m_index_storage;
- /* The CU that we are reading on behalf of. This object might be
- asked to index one CU but to treat the results as if they come
- from some including CU; in this case the including CU would be
- recorded here. */
- dwarf2_per_cu *m_per_cu;
/* The language that we're assuming when reading. */
enum language m_language;
/* The next available abbrev number. */
int next_abbrev = 1;
+ /* Storage for the CU list for inline entries. This is outside
+ the loops to try to avoid repeated allocations. */
+ std::vector<dwarf2_per_cu *> per_cus;
+
for (auto &[name, these_entries] : m_name_to_value_set)
{
/* Sort the items within each bucket. This ensures that the
for (const cooked_index_entry *entry : these_entries)
{
- unit_kind kind = (entry->per_cu->is_debug_types ()
- ? unit_kind::tu
- : unit_kind::cu);
- /* Some Ada parentage is synthesized by the reader and so
- must be ignored here. */
- const cooked_index_entry *parent = entry->get_parent ();
- if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0)
- parent = nullptr;
-
- int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag,
- kind,
- entry->flags,
- entry->lang,
- parent != nullptr)];
- if (abbrev == 0)
+ /* Accumulate all the relevant CUs. This is needed for
+ the DW_TAG_inlined_subroutine special case. */
+ per_cus.clear ();
+ entry->visit_defining_cus ([&] (dwarf2_per_cu *one_cu)
+ {
+ per_cus.push_back (one_cu);
+ return true;
+ });
+ /* Make sure the output is stable. */
+ std::sort (per_cus.begin (), per_cus.end (),
+ [] (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs)
+ {
+ return lhs->index < rhs->index;
+ });
+
+ for (dwarf2_per_cu *one_cu : per_cus)
{
- abbrev = next_abbrev++;
+ unit_kind kind = (entry->per_cu->is_debug_types ()
+ ? unit_kind::tu
+ : unit_kind::cu);
+ /* Some Ada parentage is synthesized by the reader and so
+ must be ignored here. */
+ const cooked_index_entry *parent = entry->get_parent ();
+ if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0)
+ parent = nullptr;
+
+ int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag,
+ kind,
+ entry->flags,
+ entry->lang,
+ parent != nullptr)];
+ if (abbrev == 0)
+ {
+ abbrev = next_abbrev++;
+
+ /* Abbrev number and tag. */
+ m_abbrev_table.append_unsigned_leb128 (abbrev);
+ m_abbrev_table.append_unsigned_leb128 (entry->tag);
+
+ /* Unit index. */
+ m_abbrev_table.append_unsigned_leb128
+ (kind == unit_kind::cu
+ ? DW_IDX_compile_unit
+ : DW_IDX_type_unit);
+ m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+
+ /* DIE offset. */
+ m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset);
+ m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr);
+
+ /* Language. */
+ m_abbrev_table.append_unsigned_leb128
+ (DW_IDX_GNU_language);
+ m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+
+ /* Internal linkage flag. */
+ if (!tag_is_type (entry->tag)
+ && (entry->flags & IS_STATIC) != 0)
+ {
+ m_abbrev_table.append_unsigned_leb128
+ (DW_IDX_GNU_internal);
+ m_abbrev_table.append_unsigned_leb128
+ (DW_FORM_flag_present);
+ }
+
+ /* Main subprogram flag. */
+ if ((entry->flags & IS_MAIN) != 0)
+ {
+ m_abbrev_table.append_unsigned_leb128
+ (DW_IDX_GNU_main);
+ m_abbrev_table.append_unsigned_leb128
+ (DW_FORM_flag_present);
+ }
+
+ /* Linkage name flag. */
+ if ((entry->flags & IS_LINKAGE) != 0)
+ {
+ m_abbrev_table.append_unsigned_leb128
+ (DW_IDX_GNU_linkage_name);
+ m_abbrev_table.append_unsigned_leb128
+ (DW_FORM_flag_present);
+ }
+
+ /* Parent offset. */
+ if (parent != nullptr)
+ {
+ m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
+ m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
+ }
+
+ /* Terminate attributes list. */
+ m_abbrev_table.append_unsigned_leb128 (0);
+ m_abbrev_table.append_unsigned_leb128 (0);
+ }
- /* Abbrev number and tag. */
- m_abbrev_table.append_unsigned_leb128 (abbrev);
- m_abbrev_table.append_unsigned_leb128 (entry->tag);
+ /* Record the offset in the pool at which this entry will
+ reside. */
+ const auto offset_inserted
+ = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
+ .second);
+ gdb_assert (offset_inserted
+ || (entry->flags & IS_INLINED) != 0);
+
+ /* Write the entry to the pool, starting with the
+ abbrev number. */
+ m_entry_pool.append_unsigned_leb128 (abbrev);
/* Unit index. */
- m_abbrev_table.append_unsigned_leb128
- (kind == unit_kind::cu
- ? DW_IDX_compile_unit
- : DW_IDX_type_unit);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+ const auto it = m_cu_index_htab.find (one_cu);
+ gdb_assert (it != m_cu_index_htab.cend ());
+ m_entry_pool.append_unsigned_leb128 (it->second);
/* DIE offset. */
- m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr);
+ m_entry_pool.append_uint (dwarf5_offset_size (),
+ m_dwarf5_byte_order,
+ to_underlying (entry->die_offset));
/* Language. */
- m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_language);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
-
- /* Internal linkage flag. */
- if (!tag_is_type (entry->tag)
- && (entry->flags & IS_STATIC) != 0)
- {
- m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_internal);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
- }
-
- /* Main subprogram flag. */
- if ((entry->flags & IS_MAIN) != 0)
- {
- m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_main);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
- }
-
- /* Linkage name flag. */
- if ((entry->flags & IS_LINKAGE) != 0)
- {
- m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_linkage_name);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
- }
+ m_entry_pool.append_unsigned_leb128
+ (entry->per_cu->dw_lang ());
/* Parent offset. */
if (parent != nullptr)
{
- m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
- m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
- }
-
- /* Terminate attributes list. */
- m_abbrev_table.append_unsigned_leb128 (0);
- m_abbrev_table.append_unsigned_leb128 (0);
- }
-
- /* Record the offset in the pool at which this entry will
- reside. */
- const auto offset_inserted
- = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
- .second);
- gdb_assert (offset_inserted);
-
- /* Write the entry to the pool, starting with the abbrev number. */
- m_entry_pool.append_unsigned_leb128 (abbrev);
-
- /* Unit index. */
- const auto it = m_cu_index_htab.find (entry->per_cu);
- gdb_assert (it != m_cu_index_htab.cend ());
- m_entry_pool.append_unsigned_leb128 (it->second);
-
- /* DIE offset. */
- m_entry_pool.append_uint (dwarf5_offset_size (),
- m_dwarf5_byte_order,
- to_underlying (entry->die_offset));
+ m_offsets_to_patch.emplace_back
+ (m_entry_pool.size (), parent);
- /* Language. */
- m_entry_pool.append_unsigned_leb128 (entry->per_cu->dw_lang ());
-
- /* Parent offset. */
- if (parent != nullptr)
- {
- m_offsets_to_patch.emplace_back (m_entry_pool.size (), parent);
-
- /* Write a dummy number, this gets patched later. */
- m_entry_pool.append_uint (4, m_dwarf5_byte_order,
- 0xfafafafa);
+ /* Write a dummy number, this gets patched later. */
+ m_entry_pool.append_uint (4, m_dwarf5_byte_order,
+ 0xfafafafa);
+ }
}
}
for (const cooked_index_entry *entry : table->all_entries ())
{
- const auto it = cu_index_htab.find (entry->per_cu);
- gdb_assert (it != cu_index_htab.cend ());
-
const char *name = entry->full_name (symtab->obstack ());
if (entry->lang == language_ada)
else
kind = GDB_INDEX_SYMBOL_KIND_OTHER;
- symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0,
- kind, it->second);
+ entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu)
+ {
+ const auto it = cu_index_htab.find (per_cu);
+ gdb_assert (it != cu_index_htab.cend ());
+ symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0,
+ kind, it->second);
+ return true;
+ });
}
}
dw2_instantiate_symtab (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile,
bool skip_partial)
{
+ /* Expand the corresponding canonical outermost CU. This will
+ expand the desired CU as a side effect when following the
+ DW_TAG_imported_unit. */
+ per_cu = per_cu->canonical_outermost_cu ();
+
if (!per_objfile->compunit_symtab_set_p (per_cu))
{
free_cached_comp_units freer (per_objfile);
if (this_cu->scanned.compare_exchange_strong (nope, true))
{
gdb_assert (storage != nullptr);
- cooked_indexer indexer (storage, this_cu, reader->cu ()->lang ());
+ cooked_indexer indexer (storage, reader->cu ()->lang ());
indexer.make_index (reader);
}
}
return;
gdb_assert (storage != nullptr);
- cooked_indexer indexer (storage, per_cu, cu->lang ());
+ cooked_indexer indexer (storage, cu->lang ());
indexer.make_index (reader);
}
we see a symbol with an unknown language we find the CU's
language. Only the .gdb_index reader creates such
symbols. */
- enum language entry_lang = entry->lang;
- if (lang_matcher != nullptr || entry_lang == language_unknown)
+ if (lang_matcher != nullptr || entry->lang == language_unknown)
{
entry->per_cu->ensure_lang (per_objfile);
if (lang_matcher != nullptr
&& !entry->per_cu->maybe_multi_language ()
&& !lang_matcher (entry->per_cu->lang ()))
continue;
- entry_lang = entry->per_cu->lang ();
+ else if (entry->lang == language_unknown)
+ entry->force_set_language ();
}
/* We've found the base name of the symbol; now walk its
bool found = true;
const cooked_index_entry *parent = entry->get_parent ();
- const language_defn *lang_def = language_def (entry_lang);
+ const language_defn *lang_def = language_def (entry->lang);
for (int i = name_vec.size () - 1; i > 0; --i)
{
/* If we ran out of entries, or if this segment doesn't
else if (!symbol_matcher (full_name))
continue;
- if (!search_one (entry->per_cu, per_objfile, cus_to_skip,
- file_matcher, listener, nullptr))
+ bool check = entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu)
+ {
+ return search_one (per_cu, per_objfile, cus_to_skip,
+ file_matcher, listener, nullptr);
+ });
+ if (!check)
return false;
}
}
true, std::nullopt, abbrev_table_cache);
}
+/* See read.h. */
+
+bool
+dwarf2_per_cu::recursively_visit_cus (per_cu_callback callback)
+{
+ if (including_cus.empty ())
+ return callback (this);
+ for (dwarf2_per_cu *iter : including_cus)
+ if (!iter->recursively_visit_cus (callback))
+ return false;
+ return true;
+}
+
+/* See read.h. */
+
+dwarf2_per_cu *
+dwarf2_per_cu::canonical_outermost_cu ()
+{
+ dwarf2_per_cu *iter = this;
+ while (!iter->including_cus.empty ())
+ iter = *iter->including_cus.begin ();
+ return iter;
+}
+
/* Return the unit from ALL_UNITS that potentially contains TARGET.
Since the unit lengths may not be known yet, this function doesn't check that
/* Backlink to the owner of this. */
dwarf2_per_bfd *m_per_bfd;
+ /* Compare two CUs. */
+ struct compare
+ {
+ bool operator() (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs) const
+ {
+ return lhs->index < rhs->index;
+ }
+ };
+
public:
/* The file and directory for this CU. This is cached so that we
don't need to re-examine the DWO in some situations. This may be
http://sourceware.org/bugzilla/show_bug.cgi?id=15021. */
std::vector<dwarf2_per_cu *> imported_symtabs;
+ /* Pointer to all the CUs that include this CU via
+ DW_TAG_imported_unit. This is used for the special handling of
+ inline functions appearing in partial units. An ordered set is
+ used to ensure that the canonical CU is the same across
+ invocations of gdb. */
+ std::set<dwarf2_per_cu *, compare> including_cus;
+
bool is_debug_types () const
{ return m_is_debug_types; }
/* Free any cached file names. */
void free_cached_file_names ();
+
+ /* Indicate that INCLUDER includes this CU via
+ DW_TAG_imported_unit. */
+ void add_includer (dwarf2_per_cu *includer)
+ {
+ including_cus.emplace (includer);
+ }
+
+ /* Type of callback used when visiting defining CUs. */
+ using per_cu_callback = gdb::function_view<bool (dwarf2_per_cu *)>;
+
+ /* Calls CALLBACK for each CU that is the outermost includer of
+ ONE_CU. If ONE_CU has no includers, it calls CALLBACK on ONE_CU.
+ If any call to CALLBACK returns false, this immediately returns
+ false (skipping the remaining calls); otherwise returns true. */
+ bool recursively_visit_cus (per_cu_callback callback);
+
+ /* Return a canonical outermost CU corresponding to this CU. If
+ this CU is standalone (not included by other CUs), then this
+ method will simply return 'this'. */
+ dwarf2_per_cu *canonical_outermost_cu ();
};
/* Entry in the signatured_types hash table. */
}
gdb_test "break N1::C1::baz" "\\(2 locations\\)"
+
+if {[have_index $binfile] == ""} {
+ set index_dir [host_standard_output_file ""]
+ # This is a regression test for a crash that occurred in the
+ # dwz+inlining series. See PR symtab/30728.
+ gdb_test_no_output "save gdb-index -dwarf-5 $index_dir" \
+ "save gdb-index"
+}