/* DIE indexing Copyright (C) 2022-2023 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "defs.h" #include "dwarf2/cooked-index.h" #include "dwarf2/read.h" #include "cp-support.h" #include "c-lang.h" #include "ada-lang.h" #include "split-name.h" #include /* Hash function for cooked_index_entry. */ static hashval_t hash_entry (const void *e) { const cooked_index_entry *entry = (const cooked_index_entry *) e; return dwarf5_djb_hash (entry->canonical); } /* Equality function for cooked_index_entry. */ static int eq_entry (const void *a, const void *b) { const cooked_index_entry *ae = (const cooked_index_entry *) a; const gdb::string_view *sv = (const gdb::string_view *) b; return (strlen (ae->canonical) == sv->length () && strncasecmp (ae->canonical, sv->data (), sv->length ()) == 0); } /* See cooked-index.h. */ const char * cooked_index_entry::full_name (struct obstack *storage) const { if ((flags & IS_LINKAGE) != 0 || parent_entry == nullptr) return canonical; const char *sep = nullptr; switch (per_cu->lang ()) { case language_cplus: case language_rust: sep = "::"; break; case language_go: case language_d: case language_ada: sep = "."; break; default: return canonical; } parent_entry->write_scope (storage, sep); obstack_grow0 (storage, canonical, strlen (canonical)); return (const char *) obstack_finish (storage); } /* See cooked-index.h. */ void cooked_index_entry::write_scope (struct obstack *storage, const char *sep) const { if (parent_entry != nullptr) parent_entry->write_scope (storage, sep); obstack_grow (storage, canonical, strlen (canonical)); obstack_grow (storage, sep, strlen (sep)); } /* See cooked-index.h. */ const cooked_index_entry * cooked_index::add (sect_offset die_offset, enum dwarf_tag tag, cooked_index_flag flags, const char *name, const cooked_index_entry *parent_entry, dwarf2_per_cu_data *per_cu) { cooked_index_entry *result = create (die_offset, tag, flags, 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 (per_cu->lang () != language_ada && m_main == nullptr && strcmp (name, "main") == 0) m_main = result; return result; } /* See cooked-index.h. */ void cooked_index::finalize () { m_future = gdb::thread_pool::g_thread_pool->post_task ([this] () { do_finalize (); }); } /* See cooked-index.h. */ gdb::unique_xmalloc_ptr cooked_index::handle_gnat_encoded_entry (cooked_index_entry *entry, htab_t gnat_entries) { std::string canonical = ada_decode (entry->name, false, false); if (canonical.empty ()) return {}; std::vector names = split_name (canonical.c_str (), split_style::DOT); gdb::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) { gdb::unique_xmalloc_ptr new_name = make_unique_xstrndup (name.data (), name.length ()); last = create (entry->die_offset, DW_TAG_namespace, 0, new_name.get (), parent, entry->per_cu); last->canonical = last->name; m_names.push_back (std::move (new_name)); *slot = last; } parent = last; } entry->parent_entry = parent; return make_unique_xstrndup (tail.data (), tail.length ()); } /* See cooked-index.h. */ void cooked_index::do_finalize () { auto hash_name_ptr = [] (const void *p) { const cooked_index_entry *entry = (const cooked_index_entry *) p; return htab_hash_pointer (entry->name); }; auto eq_name_ptr = [] (const void *a, const void *b) -> int { const cooked_index_entry *ea = (const cooked_index_entry *) a; const cooked_index_entry *eb = (const cooked_index_entry *) b; return ea->name == eb->name; }; /* 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. */ htab_up seen_names (htab_create_alloc (10, hash_name_ptr, eq_name_ptr, nullptr, xcalloc, xfree)); htab_up gnat_entries (htab_create_alloc (10, hash_entry, eq_entry, nullptr, xcalloc, xfree)); for (cooked_index_entry *entry : m_entries) { gdb_assert (entry->canonical == nullptr); if ((entry->flags & IS_LINKAGE) != 0) entry->canonical = entry->name; else if (entry->per_cu->lang () == language_ada) { gdb::unique_xmalloc_ptr canon_name = handle_gnat_encoded_entry (entry, gnat_entries.get ()); if (canon_name == nullptr) entry->canonical = entry->name; else { entry->canonical = canon_name.get (); m_names.push_back (std::move (canon_name)); } } else if (entry->per_cu->lang () == language_cplus || entry->per_cu->lang () == language_c) { void **slot = htab_find_slot (seen_names.get (), entry, INSERT); if (*slot == nullptr) { gdb::unique_xmalloc_ptr canon_name = (entry->per_cu->lang () == language_cplus ? cp_canonicalize_string (entry->name) : c_canonicalize_name (entry->name)); if (canon_name == nullptr) entry->canonical = entry->name; else { entry->canonical = canon_name.get (); m_names.push_back (std::move (canon_name)); } } else { const cooked_index_entry *other = (const cooked_index_entry *) *slot; entry->canonical = other->canonical; } } else entry->canonical = entry->name; } m_names.shrink_to_fit (); 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::range cooked_index::find (gdb::string_view name, bool completing) { wait (); auto lower = std::lower_bound (m_entries.begin (), m_entries.end (), name, [=] (const cooked_index_entry *entry, const gdb::string_view &n) { int cmp = strncasecmp (entry->canonical, n.data (), n.length ()); if (cmp != 0 || completing) return cmp < 0; return strlen (entry->canonical) < n.length (); }); auto upper = std::upper_bound (m_entries.begin (), m_entries.end (), name, [=] (const gdb::string_view &n, const cooked_index_entry *entry) { int cmp = strncasecmp (n.data (), entry->canonical, n.length ()); if (cmp != 0 || completing) return cmp < 0; return n.length () < strlen (entry->canonical); }); return range (lower, upper); } cooked_index_vector::cooked_index_vector (vec_type &&vec) : m_vector (std::move (vec)) { for (auto &idx : m_vector) idx->finalize (); } /* See cooked-index.h. */ dwarf2_per_cu_data * cooked_index_vector::lookup (CORE_ADDR addr) { for (const auto &index : m_vector) { dwarf2_per_cu_data *result = index->lookup (addr); if (result != nullptr) return result; } return nullptr; } /* See cooked-index.h. */ std::vector cooked_index_vector::get_addrmaps () { std::vector result; for (const auto &index : m_vector) result.push_back (index->m_addrmap); return result; } /* See cooked-index.h. */ cooked_index_vector::range cooked_index_vector::find (gdb::string_view name, bool completing) { std::vector result_range; result_range.reserve (m_vector.size ()); for (auto &entry : m_vector) result_range.push_back (entry->find (name, completing)); return range (std::move (result_range)); } /* See cooked-index.h. */ const cooked_index_entry * cooked_index_vector::get_main () const { const cooked_index_entry *result = nullptr; for (const auto &index : m_vector) { const cooked_index_entry *entry = index->get_main (); if (result == nullptr || ((result->flags & IS_MAIN) == 0 && entry != nullptr && (entry->flags & IS_MAIN) != 0)) result = entry; } return result; }