346 lines
9 KiB
C
346 lines
9 KiB
C
/* DIE indexing
|
|
|
|
Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "dwarf2/cooked-index.h"
|
|
#include "dwarf2/read.h"
|
|
#include "cp-support.h"
|
|
#include "ada-lang.h"
|
|
#include "split-name.h"
|
|
#include <algorithm>
|
|
|
|
/* 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<char>
|
|
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<gdb::string_view> 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<char> 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->per_cu->lang () != language_cplus
|
|
&& entry->per_cu->lang () != language_ada)
|
|
|| (entry->flags & IS_LINKAGE) != 0)
|
|
entry->canonical = entry->name;
|
|
else
|
|
{
|
|
if (entry->per_cu->lang () == language_ada)
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> 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
|
|
{
|
|
void **slot = htab_find_slot (seen_names.get (), entry,
|
|
INSERT);
|
|
if (*slot == nullptr)
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> canon_name
|
|
= cp_canonicalize_string (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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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<addrmap *>
|
|
cooked_index_vector::get_addrmaps ()
|
|
{
|
|
std::vector<addrmap *> 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<cooked_index::range> 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;
|
|
}
|