diff options
Diffstat (limited to 'gdb/dwarf2/cooked-index.c')
-rw-r--r-- | gdb/dwarf2/cooked-index.c | 693 |
1 files changed, 10 insertions, 683 deletions
diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c index 724615f..0f20b07 100644 --- a/gdb/dwarf2/cooked-index.c +++ b/gdb/dwarf2/cooked-index.c @@ -18,25 +18,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "dwarf2/cooked-index.h" -#include "dwarf2/index-common.h" #include "dwarf2/read.h" #include "dwarf2/stringify.h" -#include "dwarf2/index-cache.h" -#include "cp-support.h" -#include "c-lang.h" -#include "ada-lang.h" -#include "dwarf2/tag.h" #include "event-top.h" -#include "exceptions.h" -#include "split-name.h" #include "observable.h" #include "run-on-main-thread.h" -#include <algorithm> -#include "gdbsupport/gdb-safe-ctype.h" -#include "gdbsupport/selftest.h" #include "gdbsupport/task-group.h" -#include "gdbsupport/thread-pool.h" -#include <chrono> #include "cli/cli-cmds.h" /* We don't want gdb to exit while it is in the process of writing to @@ -44,25 +31,12 @@ here, and then these are all waited for before exit proceeds. */ static gdb::unordered_set<cooked_index *> active_vectors; -/* See cooked-index.h. */ - -std::string -to_string (cooked_index_flag flags) -{ - static constexpr cooked_index_flag::string_mapping mapping[] = { - MAP_ENUM_FLAG (IS_MAIN), - MAP_ENUM_FLAG (IS_STATIC), - MAP_ENUM_FLAG (IS_LINKAGE), - MAP_ENUM_FLAG (IS_TYPE_DECLARATION), - MAP_ENUM_FLAG (IS_PARENT_DEFERRED), - }; - - return flags.to_string (mapping); -} - -/* See cooked-index.h. */ +/* Return true if LANG requires canonicalization. This is used + primarily to work around an issue computing the name of "main". + This function must be kept in sync with + cooked_index_shard::finalize. */ -bool +static bool language_requires_canonicalization (enum language lang) { return (lang == language_ada @@ -70,648 +44,6 @@ language_requires_canonicalization (enum language lang) || lang == language_cplus); } -/* Return true if a plain "main" could be the main program for this - language. Languages that are known to use some other mechanism are - excluded here. */ - -static bool -language_may_use_plain_main (enum language lang) -{ - /* No need to handle "unknown" here. */ - return (lang == language_c - || lang == language_objc - || lang == language_cplus - || lang == language_m2 - || lang == language_asm - || lang == language_opencl - || lang == language_minimal); -} - -/* See cooked-index.h. */ - -int -cooked_index_entry::compare (const char *stra, const char *strb, - comparison_mode mode) -{ -#if defined (__GNUC__) && !defined (__clang__) && __GNUC__ <= 7 - /* Work around error with gcc 7.5.0. */ - auto munge = [] (char c) -> unsigned char -#else - auto munge = [] (char c) constexpr -> unsigned char -#endif - { - /* Treat '<' as if it ended the string. This lets something - like "func<t>" match "func<t<int>>". See the "Breakpoints in - template functions" section in the manual. */ - if (c == '<') - return '\0'; - return TOLOWER ((unsigned char) c); - }; - - unsigned char a = munge (*stra); - unsigned char b = munge (*strb); - - while (a != '\0' && b != '\0' && a == b) - { - a = munge (*++stra); - b = munge (*++strb); - } - - if (a == b) - return 0; - - /* When completing, if STRB ends earlier than STRA, consider them as - equal. */ - if (mode == COMPLETE || (mode == MATCH && a == munge ('<'))) - { - if (b == '\0') - return 0; - } - - return a < b ? -1 : 1; -} - -#if GDB_SELF_TEST - -namespace { - -void -test_compare () -{ - /* Convenience aliases. */ - const auto mode_compare = cooked_index_entry::MATCH; - const auto mode_sort = cooked_index_entry::SORT; - const auto mode_complete = cooked_index_entry::COMPLETE; - - SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "abcd", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", - mode_compare) < 0); - SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", - mode_compare) > 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "ABCDE", - mode_complete) < 0); - SELF_CHECK (cooked_index_entry::compare ("ABCDE", "abcd", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name", "name<>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<>", "name", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name", "name<>", - mode_complete) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<>", "name", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<arg>", - mode_complete) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", "name<ag>", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", - "name<arg<more>>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg>", - "name<arg<more>>", - mode_compare) == 0); - - SELF_CHECK (cooked_index_entry::compare ("name", "name<arg<more>>", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", - mode_compare) == 0); - SELF_CHECK (cooked_index_entry::compare ("name<arg<more>>", "name<arg<", - mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_compare) < 0); - SELF_CHECK (cooked_index_entry::compare ("", "abcd", mode_complete) < 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_compare) > 0); - SELF_CHECK (cooked_index_entry::compare ("abcd", "", mode_complete) == 0); - - SELF_CHECK (cooked_index_entry::compare ("func", "func<type>", - mode_sort) == 0); - SELF_CHECK (cooked_index_entry::compare ("func<type>", "func1", - mode_sort) < 0); -} - -} /* anonymous namespace */ - -#endif /* GDB_SELF_TEST */ - -/* See cooked-index.h. */ - -bool -cooked_index_entry::matches (domain_search_flags kind) const -{ - /* Just reject type declarations. */ - if ((flags & IS_TYPE_DECLARATION) != 0) - return false; - - return tag_matches_domain (tag, kind, lang); -} - -/* See cooked-index.h. */ - -const char * -cooked_index_entry::full_name (struct obstack *storage, - cooked_index_full_name_flag name_flags, - const char *default_sep) const -{ - const char *local_name = ((name_flags & FOR_MAIN) != 0) ? name : canonical; - - if ((flags & IS_LINKAGE) != 0 || get_parent () == nullptr) - return local_name; - - const char *sep = default_sep; - switch (lang) - { - case language_cplus: - case language_rust: - case language_fortran: - sep = "::"; - break; - - case language_ada: - if ((name_flags & FOR_ADA_LINKAGE_NAME) != 0) - { - sep = "__"; - break; - } - [[fallthrough]]; - case language_go: - case language_d: - sep = "."; - break; - - default: - if (sep == nullptr) - return local_name; - break; - } - - /* The FOR_ADA_LINKAGE_NAME flag should only affect Ada entries, so - disable it here if we don't need it. */ - if (lang != language_ada) - name_flags &= ~FOR_ADA_LINKAGE_NAME; - - get_parent ()->write_scope (storage, sep, name_flags); - obstack_grow0 (storage, local_name, strlen (local_name)); - return (const char *) obstack_finish (storage); -} - -/* See cooked-index.h. */ - -void -cooked_index_entry::write_scope (struct obstack *storage, - const char *sep, - cooked_index_full_name_flag flags) const -{ - if (get_parent () != nullptr) - get_parent ()->write_scope (storage, sep, flags); - /* When computing the Ada linkage name, the entry might not have - been canonicalized yet, so use the 'name'. */ - const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0 - ? name - : canonical); - obstack_grow (storage, local_name, strlen (local_name)); - obstack_grow (storage, sep, strlen (sep)); -} - -/* See cooked-index.h. */ - -cooked_index_entry * -cooked_index_shard::create (sect_offset die_offset, - enum dwarf_tag tag, - cooked_index_flag flags, - enum language lang, - const char *name, - cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) -{ - if (tag == DW_TAG_module || tag == DW_TAG_namespace) - flags &= ~IS_STATIC; - else if (lang == language_cplus - && (tag == DW_TAG_class_type - || tag == DW_TAG_interface_type - || tag == DW_TAG_structure_type - || tag == DW_TAG_union_type - || tag == DW_TAG_enumeration_type - || tag == DW_TAG_enumerator)) - flags &= ~IS_STATIC; - else if (tag_is_type (tag)) - flags |= IS_STATIC; - - return new (&m_storage) cooked_index_entry (die_offset, tag, flags, - lang, name, parent_entry, - per_cu); -} - -/* See cooked-index.h. */ - -cooked_index_entry * -cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag, - cooked_index_flag flags, enum language lang, - const char *name, cooked_index_entry_ref parent_entry, - dwarf2_per_cu *per_cu) -{ - cooked_index_entry *result = create (die_offset, tag, flags, lang, name, - parent_entry, per_cu); - m_entries.push_back (result); - - /* An explicitly-tagged main program should always override the - implicit "main" discovery. */ - if ((flags & IS_MAIN) != 0) - m_main = result; - else if ((flags & IS_PARENT_DEFERRED) == 0 - && parent_entry.resolved == nullptr - && m_main == nullptr - && language_may_use_plain_main (lang) - && strcmp (name, "main") == 0) - m_main = result; - - return result; -} - -/* See cooked-index.h. */ - -void -cooked_index_shard::handle_gnat_encoded_entry - (cooked_index_entry *entry, - htab_t gnat_entries, - std::vector<cooked_index_entry *> &new_entries) -{ - /* We decode Ada names in a particular way: operators and wide - characters are left as-is. This is done to make name matching a - bit simpler; and for wide characters, it means the choice of Ada - source charset does not affect the indexer directly. */ - std::string canonical = ada_decode (entry->name, false, false, false); - if (canonical.empty ()) - { - entry->canonical = entry->name; - return; - } - std::vector<std::string_view> names = split_name (canonical.c_str (), - split_style::DOT_STYLE); - std::string_view tail = names.back (); - names.pop_back (); - - const cooked_index_entry *parent = nullptr; - for (const auto &name : names) - { - uint32_t hashval = dwarf5_djb_hash (name); - void **slot = htab_find_slot_with_hash (gnat_entries, &name, - hashval, INSERT); - /* CUs are processed in order, so we only need to check the most - recent entry. */ - cooked_index_entry *last = (cooked_index_entry *) *slot; - if (last == nullptr || last->per_cu != entry->per_cu) - { - const char *new_name = m_names.insert (name); - last = create (entry->die_offset, DW_TAG_module, - IS_SYNTHESIZED, language_ada, new_name, parent, - entry->per_cu); - last->canonical = last->name; - new_entries.push_back (last); - *slot = last; - } - - parent = last; - } - - entry->set_parent (parent); - entry->canonical = m_names.insert (tail); -} - -/* Hash a cooked index entry by name pointer value. - - We can use pointer equality here because names come from .debug_str, which - will normally be unique-ified by the linker. Also, duplicates are relatively - harmless -- they just mean a bit of extra memory is used. */ - -struct cooked_index_entry_name_ptr_hash -{ - using is_avalanching = void; - - std::uint64_t operator () (const cooked_index_entry *entry) const noexcept - { - return ankerl::unordered_dense::hash<const char *> () (entry->name); - } -}; - -/* Compare cooked index entries by name pointer value. */ - -struct cooked_index_entry_name_ptr_eq -{ - bool operator () (const cooked_index_entry *a, - const cooked_index_entry *b) const noexcept - { - return a->name == b->name; - } -}; - -/* See cooked-index.h. */ - -void -cooked_index_shard::finalize (const parent_map_map *parent_maps) -{ - gdb::unordered_set<const cooked_index_entry *, - cooked_index_entry_name_ptr_hash, - cooked_index_entry_name_ptr_eq> seen_names; - - auto hash_entry = [] (const void *e) - { - const cooked_index_entry *entry = (const cooked_index_entry *) e; - return dwarf5_djb_hash (entry->canonical); - }; - - auto eq_entry = [] (const void *a, const void *b) -> int - { - const cooked_index_entry *ae = (const cooked_index_entry *) a; - const std::string_view *sv = (const std::string_view *) b; - return (strlen (ae->canonical) == sv->length () - && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0); - }; - - htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry, - nullptr, xcalloc, xfree)); - std::vector<cooked_index_entry *> new_gnat_entries; - - for (cooked_index_entry *entry : m_entries) - { - if ((entry->flags & IS_PARENT_DEFERRED) != 0) - { - const cooked_index_entry *new_parent - = parent_maps->find (entry->get_deferred_parent ()); - entry->resolve_parent (new_parent); - } - - /* Note that this code must be kept in sync with - language_requires_canonicalization. */ - gdb_assert (entry->canonical == nullptr); - if ((entry->flags & IS_LINKAGE) != 0) - entry->canonical = entry->name; - else if (entry->lang == language_ada) - { - /* Newer versions of GNAT emit DW_TAG_module and use a - hierarchical structure. In this case, we don't need to - do any extra work. This can be detected by looking for a - GNAT-encoded name. */ - if (strstr (entry->name, "__") == nullptr) - { - entry->canonical = entry->name; - - /* If the entry does not have a parent, then there's - nothing extra to do here -- the entry itself is - sufficient. - - However, if it does have a parent, we have to - synthesize an entry with the full name. This is - unfortunate, but it's necessary due to how some of - the Ada name-lookup code currently works. For - example, without this, ada_get_tsd_type will - fail. - - Eventually it would be good to change the Ada lookup - code, and then remove these entries (and supporting - code in cooked_index_entry::full_name). */ - if (entry->get_parent () != nullptr) - { - const char *fullname - = entry->full_name (&m_storage, FOR_ADA_LINKAGE_NAME); - cooked_index_entry *linkage = create (entry->die_offset, - entry->tag, - (entry->flags - | IS_LINKAGE - | IS_SYNTHESIZED), - language_ada, - fullname, - nullptr, - entry->per_cu); - linkage->canonical = fullname; - new_gnat_entries.push_back (linkage); - } - } - else - handle_gnat_encoded_entry (entry, gnat_entries.get (), - new_gnat_entries); - } - else if (entry->lang == language_cplus || entry->lang == language_c) - { - auto [it, inserted] = seen_names.insert (entry); - - if (inserted) - { - /* No entry with that name was present, compute the canonical - name. */ - gdb::unique_xmalloc_ptr<char> canon_name - = (entry->lang == language_cplus - ? cp_canonicalize_string (entry->name) - : c_canonicalize_name (entry->name)); - if (canon_name == nullptr) - entry->canonical = entry->name; - else - entry->canonical = m_names.insert (std::move (canon_name)); - } - else - { - /* An entry with that name was present, re-use its canonical - name. */ - entry->canonical = (*it)->canonical; - } - } - else - entry->canonical = entry->name; - } - - /* Make sure any new Ada entries end up in the results. This isn't - done when creating these new entries to avoid invalidating the - m_entries iterator used in the foreach above. */ - m_entries.insert (m_entries.end (), new_gnat_entries.begin (), - new_gnat_entries.end ()); - - m_entries.shrink_to_fit (); - std::sort (m_entries.begin (), m_entries.end (), - [] (const cooked_index_entry *a, const cooked_index_entry *b) - { - return *a < *b; - }); -} - -/* See cooked-index.h. */ - -cooked_index_shard::range -cooked_index_shard::find (const std::string &name, bool completing) const -{ - cooked_index_entry::comparison_mode mode = (completing - ? cooked_index_entry::COMPLETE - : cooked_index_entry::MATCH); - - auto lower = std::lower_bound (m_entries.cbegin (), m_entries.cend (), name, - [=] (const cooked_index_entry *entry, - const std::string &n) - { - return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) < 0; - }); - - auto upper = std::upper_bound (m_entries.cbegin (), m_entries.cend (), name, - [=] (const std::string &n, - const cooked_index_entry *entry) - { - return cooked_index_entry::compare (entry->canonical, n.c_str (), mode) > 0; - }); - - return range (lower, upper); -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::start () -{ - gdb::thread_pool::g_thread_pool->post_task ([this] () - { - try - { - do_reading (); - } - catch (const gdb_exception &exc) - { - m_failed = exc; - set (cooked_state::CACHE_DONE); - } - - bfd_thread_cleanup (); - }); -} - -/* See cooked-index.h. */ - -bool -cooked_index_worker::wait (cooked_state desired_state, bool allow_quit) -{ - bool done; -#if CXX_STD_THREAD - { - std::unique_lock<std::mutex> lock (m_mutex); - - /* This may be called from a non-main thread -- this functionality - is needed for the index cache -- but in this case we require - that the desired state already have been attained. */ - gdb_assert (is_main_thread () || desired_state <= m_state); - - while (desired_state > m_state) - { - if (allow_quit) - { - std::chrono::milliseconds duration { 15 }; - if (m_cond.wait_for (lock, duration) == std::cv_status::timeout) - QUIT; - } - else - m_cond.wait (lock); - } - done = m_state == cooked_state::CACHE_DONE; - } -#else - /* Without threads, all the work is done immediately on the main - thread, and there is never anything to wait for. */ - done = desired_state == cooked_state::CACHE_DONE; -#endif /* CXX_STD_THREAD */ - - /* Only the main thread is allowed to report complaints and the - like. */ - if (!is_main_thread ()) - return false; - - if (m_reported) - return done; - m_reported = true; - - /* Emit warnings first, maybe they were emitted before an exception - (if any) was thrown. */ - m_warnings.emit (); - - if (m_failed.has_value ()) - { - /* do_reading failed -- report it. */ - exception_print (gdb_stderr, *m_failed); - m_failed.reset (); - return done; - } - - /* Only show a given exception a single time. */ - gdb::unordered_set<gdb_exception> seen_exceptions; - for (auto &one_result : m_results) - { - re_emit_complaints (std::get<1> (one_result)); - for (auto &one_exc : std::get<2> (one_result)) - if (seen_exceptions.insert (one_exc).second) - exception_print (gdb_stderr, one_exc); - } - - print_stats (); - - struct objfile *objfile = m_per_objfile->objfile; - dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd; - cooked_index *table - = (gdb::checked_static_cast<cooked_index *> - (per_bfd->index_table.get ())); - - auto_obstack temp_storage; - enum language lang = language_unknown; - const char *main_name = table->get_main_name (&temp_storage, &lang); - if (main_name != nullptr) - set_objfile_main_name (objfile, main_name, lang); - - /* dwarf_read_debug_printf ("Done building psymtabs of %s", */ - /* objfile_name (objfile)); */ - - return done; -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::set (cooked_state desired_state) -{ - gdb_assert (desired_state != cooked_state::INITIAL); - -#if CXX_STD_THREAD - std::lock_guard<std::mutex> guard (m_mutex); - gdb_assert (desired_state > m_state); - m_state = desired_state; - m_cond.notify_one (); -#else - /* Without threads, all the work is done immediately on the main - thread, and there is never anything to do. */ -#endif /* CXX_STD_THREAD */ -} - -/* See cooked-index.h. */ - -void -cooked_index_worker::write_to_cache (const cooked_index *idx, - deferred_warnings *warn) const -{ - if (idx != nullptr) - { - /* Writing to the index cache may cause a warning to be emitted. - See PR symtab/30837. This arranges to capture all such - warnings. This is safe because we know the deferred_warnings - object isn't in use by any other thread at this point. */ - scoped_restore_warning_hook defer (warn); - m_cache_store.store (); - } -} - cooked_index::cooked_index (cooked_index_worker_up &&worker) : m_state (std::move (worker)) { @@ -746,12 +78,10 @@ cooked_index::wait (cooked_state desired_state, bool allow_quit) } void -cooked_index::set_contents (std::vector<cooked_index_shard_up> &&shards, - deferred_warnings *warn, - const parent_map_map *parent_maps) +cooked_index::set_contents () { gdb_assert (m_shards.empty ()); - m_shards = std::move (shards); + m_shards = m_state->release_shards (); m_state->set (cooked_state::MAIN_AVAILABLE); @@ -760,16 +90,17 @@ cooked_index::set_contents (std::vector<cooked_index_shard_up> &&shards, finalization. However, that would take a slot in the global thread pool, and if enough such tasks were submitted at once, it would cause a livelock. */ - gdb::task_group finalizers ([this, warn] () + gdb::task_group finalizers ([this] () { m_state->set (cooked_state::FINALIZED); - m_state->write_to_cache (index_for_writing (), warn); + m_state->write_to_cache (index_for_writing ()); m_state->set (cooked_state::CACHE_DONE); }); for (auto &shard : m_shards) { auto this_shard = shard.get (); + const parent_map_map *parent_maps = m_state->get_parent_map_map (); finalizers.add_task ([=] () { this_shard->finalize (parent_maps); }); } @@ -994,10 +325,6 @@ void _initialize_cooked_index (); void _initialize_cooked_index () { -#if GDB_SELF_TEST - selftests::register_test ("cooked_index_entry::compare", test_compare); -#endif - add_cmd ("wait-for-index-cache", class_maintenance, maintenance_wait_for_index_cache, _("\ Wait until all pending writes to the index cache have completed.\n\ |