mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 10:03:47 +08:00
ced885161b
When building gdb with -std=c++20 I run into: ... gdb/dwarf2/cooked-index.c: In lambda function: gdb/dwarf2/cooked-index.c:471:47: error: implicit capture of ‘this’ via \ ‘[=]’ is deprecated in C++20 [-Werror=deprecated] 471 | gdb::thread_pool::g_thread_pool->post_task ([=] () | ^ gdb/dwarf2/cooked-index.c:471:47: note: add explicit ‘this’ or ‘*this’ capture ... Fix this and two more spots by removing the capture default, and explicitly listing all captures. Tested on x86_64-linux.
900 lines
25 KiB
C
900 lines
25 KiB
C
/* DIE indexing
|
|
|
|
Copyright (C) 2022-2024 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 "dwarf2/cooked-index.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 <unordered_set>
|
|
#include "cli/cli-cmds.h"
|
|
|
|
/* We don't want gdb to exit while it is in the process of writing to
|
|
the index cache. So, all live cooked index vectors are stored
|
|
here, and then these are all waited for before exit proceeds. */
|
|
static std::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. */
|
|
|
|
bool
|
|
language_requires_canonicalization (enum language lang)
|
|
{
|
|
return (lang == language_ada
|
|
|| lang == language_c
|
|
|| 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)
|
|
{
|
|
auto munge = [] (char c) -> unsigned char
|
|
{
|
|
/* We want to sort '<' before any other printable character.
|
|
So, rewrite '<' to something just before ' '. */
|
|
if (c == '<')
|
|
return '\x1f';
|
|
return TOLOWER ((unsigned char) c);
|
|
};
|
|
|
|
while (*stra != '\0'
|
|
&& *strb != '\0'
|
|
&& (munge (*stra) == munge (*strb)))
|
|
{
|
|
++stra;
|
|
++strb;
|
|
}
|
|
|
|
unsigned char c1 = munge (*stra);
|
|
unsigned char c2 = munge (*strb);
|
|
|
|
if (c1 == c2)
|
|
return 0;
|
|
|
|
/* When completing, if STRB ends earlier than STRA, consider them as
|
|
equal. When comparing, if STRB ends earlier and STRA ends with
|
|
'<', consider them as equal. */
|
|
if (mode == COMPLETE || (mode == MATCH && c1 == munge ('<')))
|
|
{
|
|
if (c2 == '\0')
|
|
return 0;
|
|
}
|
|
|
|
return c1 < c2 ? -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", "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, bool for_main,
|
|
const char *default_sep) const
|
|
{
|
|
const char *local_name = for_main ? 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:
|
|
sep = "::";
|
|
break;
|
|
|
|
case language_go:
|
|
case language_d:
|
|
case language_ada:
|
|
sep = ".";
|
|
break;
|
|
|
|
default:
|
|
if (sep == nullptr)
|
|
return local_name;
|
|
break;
|
|
}
|
|
|
|
get_parent ()->write_scope (storage, sep, for_main);
|
|
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,
|
|
bool for_main) const
|
|
{
|
|
if (get_parent () != nullptr)
|
|
get_parent ()->write_scope (storage, sep, for_main);
|
|
const char *local_name = for_main ? 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::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_data *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)
|
|
{
|
|
/* 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)
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> new_name
|
|
= make_unique_xstrndup (name.data (), name.length ());
|
|
last = create (entry->die_offset, DW_TAG_namespace,
|
|
0, language_ada, new_name.get (), parent,
|
|
entry->per_cu);
|
|
last->canonical = last->name;
|
|
m_names.push_back (std::move (new_name));
|
|
*slot = last;
|
|
}
|
|
|
|
parent = last;
|
|
}
|
|
|
|
entry->set_parent (parent);
|
|
auto new_canon = make_unique_xstrndup (tail.data (), tail.length ());
|
|
entry->canonical = new_canon.get ();
|
|
m_names.push_back (std::move (new_canon));
|
|
}
|
|
|
|
/* See cooked-index.h. */
|
|
|
|
void
|
|
cooked_index_shard::finalize (const parent_map_map *parent_maps)
|
|
{
|
|
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));
|
|
|
|
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));
|
|
|
|
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)
|
|
handle_gnat_encoded_entry (entry, gnat_entries.get ());
|
|
else if (entry->lang == language_cplus || entry->lang == language_c)
|
|
{
|
|
void **slot = htab_find_slot (seen_names.get (), entry,
|
|
INSERT);
|
|
if (*slot == nullptr)
|
|
{
|
|
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 = canon_name.get ();
|
|
m_names.push_back (std::move (canon_name));
|
|
}
|
|
*slot = entry;
|
|
}
|
|
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_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. */
|
|
std::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 (dwarf2_per_objfile *per_objfile,
|
|
std::unique_ptr<cooked_index_worker> &&worker)
|
|
: m_state (std::move (worker)),
|
|
m_per_bfd (per_objfile->per_bfd)
|
|
{
|
|
/* ACTIVE_VECTORS is not locked, and this assert ensures that this
|
|
will be caught if ever moved to the background. */
|
|
gdb_assert (is_main_thread ());
|
|
active_vectors.insert (this);
|
|
}
|
|
|
|
void
|
|
cooked_index::start_reading ()
|
|
{
|
|
m_state->start ();
|
|
}
|
|
|
|
void
|
|
cooked_index::wait (cooked_state desired_state, bool allow_quit)
|
|
{
|
|
gdb_assert (desired_state != cooked_state::INITIAL);
|
|
|
|
/* If the state object has been deleted, then that means waiting is
|
|
completely done. */
|
|
if (m_state == nullptr)
|
|
return;
|
|
|
|
if (m_state->wait (desired_state, allow_quit))
|
|
{
|
|
/* Only the main thread can modify this. */
|
|
gdb_assert (is_main_thread ());
|
|
m_state.reset (nullptr);
|
|
}
|
|
}
|
|
|
|
void
|
|
cooked_index::set_contents (vec_type &&vec, deferred_warnings *warn,
|
|
const parent_map_map *parent_maps)
|
|
{
|
|
gdb_assert (m_vector.empty ());
|
|
m_vector = std::move (vec);
|
|
|
|
m_state->set (cooked_state::MAIN_AVAILABLE);
|
|
|
|
/* This is run after finalization is done -- but not before. If
|
|
this task were submitted earlier, it would have to wait for
|
|
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] ()
|
|
{
|
|
m_state->set (cooked_state::FINALIZED);
|
|
m_state->write_to_cache (index_for_writing (), warn);
|
|
m_state->set (cooked_state::CACHE_DONE);
|
|
});
|
|
|
|
for (auto &idx : m_vector)
|
|
{
|
|
auto this_index = idx.get ();
|
|
finalizers.add_task ([=] () { this_index->finalize (parent_maps); });
|
|
}
|
|
|
|
finalizers.start ();
|
|
}
|
|
|
|
cooked_index::~cooked_index ()
|
|
{
|
|
/* Wait for index-creation to be done, though this one must also
|
|
waited for by the per-BFD object to ensure the required data
|
|
remains live. */
|
|
wait (cooked_state::CACHE_DONE);
|
|
|
|
/* Remove our entry from the global list. See the assert in the
|
|
constructor to understand this. */
|
|
gdb_assert (is_main_thread ());
|
|
active_vectors.erase (this);
|
|
}
|
|
|
|
/* See cooked-index.h. */
|
|
|
|
dwarf2_per_cu_data *
|
|
cooked_index::lookup (unrelocated_addr addr)
|
|
{
|
|
/* Ensure that the address maps are ready. */
|
|
wait (cooked_state::MAIN_AVAILABLE, true);
|
|
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<const addrmap *>
|
|
cooked_index::get_addrmaps ()
|
|
{
|
|
/* Ensure that the address maps are ready. */
|
|
wait (cooked_state::MAIN_AVAILABLE, true);
|
|
std::vector<const addrmap *> result;
|
|
for (const auto &index : m_vector)
|
|
result.push_back (index->m_addrmap);
|
|
return result;
|
|
}
|
|
|
|
/* See cooked-index.h. */
|
|
|
|
cooked_index::range
|
|
cooked_index::find (const std::string &name, bool completing)
|
|
{
|
|
wait (cooked_state::FINALIZED, true);
|
|
std::vector<cooked_index_shard::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 char *
|
|
cooked_index::get_main_name (struct obstack *obstack, enum language *lang)
|
|
const
|
|
{
|
|
const cooked_index_entry *entry = get_main ();
|
|
if (entry == nullptr)
|
|
return nullptr;
|
|
|
|
*lang = entry->lang;
|
|
return entry->full_name (obstack, true);
|
|
}
|
|
|
|
/* See cooked_index.h. */
|
|
|
|
const cooked_index_entry *
|
|
cooked_index::get_main () const
|
|
{
|
|
const cooked_index_entry *best_entry = nullptr;
|
|
for (const auto &index : m_vector)
|
|
{
|
|
const cooked_index_entry *entry = index->get_main ();
|
|
/* Choose the first "main" we see. We only do this for names
|
|
not requiring canonicalization. At this point in the process
|
|
names might not have been canonicalized. However, currently,
|
|
languages that require this step also do not use
|
|
DW_AT_main_subprogram. An assert is appropriate here because
|
|
this filtering is done in get_main. */
|
|
if (entry != nullptr)
|
|
{
|
|
if ((entry->flags & IS_MAIN) != 0)
|
|
{
|
|
if (!language_requires_canonicalization (entry->lang))
|
|
{
|
|
/* There won't be one better than this. */
|
|
return entry;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* This is one that is named "main". Here we don't care
|
|
if the language requires canonicalization, due to how
|
|
the entry is detected. Entries like this have worse
|
|
priority than IS_MAIN entries. */
|
|
if (best_entry == nullptr)
|
|
best_entry = entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
return best_entry;
|
|
}
|
|
|
|
quick_symbol_functions_up
|
|
cooked_index::make_quick_functions () const
|
|
{
|
|
return quick_symbol_functions_up (new cooked_index_functions);
|
|
}
|
|
|
|
/* See cooked-index.h. */
|
|
|
|
void
|
|
cooked_index::dump (gdbarch *arch)
|
|
{
|
|
auto_obstack temp_storage;
|
|
|
|
gdb_printf (" entries:\n");
|
|
gdb_printf ("\n");
|
|
|
|
size_t i = 0;
|
|
for (const cooked_index_entry *entry : this->all_entries ())
|
|
{
|
|
QUIT;
|
|
|
|
gdb_printf (" [%zu] ((cooked_index_entry *) %p)\n", i++, entry);
|
|
gdb_printf (" name: %s\n", entry->name);
|
|
gdb_printf (" canonical: %s\n", entry->canonical);
|
|
gdb_printf (" qualified: %s\n",
|
|
entry->full_name (&temp_storage, false, "::"));
|
|
gdb_printf (" DWARF tag: %s\n", dwarf_tag_name (entry->tag));
|
|
gdb_printf (" flags: %s\n", to_string (entry->flags).c_str ());
|
|
gdb_printf (" DIE offset: %s\n", sect_offset_str (entry->die_offset));
|
|
|
|
if ((entry->flags & IS_PARENT_DEFERRED) != 0)
|
|
gdb_printf (" parent: deferred (%" PRIx64 ")\n",
|
|
entry->get_deferred_parent ());
|
|
else if (entry->get_parent () != nullptr)
|
|
gdb_printf (" parent: ((cooked_index_entry *) %p) [%s]\n",
|
|
entry->get_parent (), entry->get_parent ()->name);
|
|
else
|
|
gdb_printf (" parent: ((cooked_index_entry *) 0)\n");
|
|
|
|
gdb_printf ("\n");
|
|
}
|
|
|
|
const cooked_index_entry *main_entry = this->get_main ();
|
|
if (main_entry != nullptr)
|
|
gdb_printf (" main: ((cooked_index_entry *) %p) [%s]\n", main_entry,
|
|
main_entry->name);
|
|
else
|
|
gdb_printf (" main: ((cooked_index_entry *) 0)\n");
|
|
|
|
gdb_printf ("\n");
|
|
gdb_printf (" address maps:\n");
|
|
gdb_printf ("\n");
|
|
|
|
std::vector<const addrmap *> addrmaps = this->get_addrmaps ();
|
|
for (i = 0; i < addrmaps.size (); ++i)
|
|
{
|
|
const addrmap &addrmap = *addrmaps[i];
|
|
|
|
gdb_printf (" [%zu] ((addrmap *) %p)\n", i, &addrmap);
|
|
gdb_printf ("\n");
|
|
|
|
addrmap.foreach ([arch] (CORE_ADDR start_addr, const void *obj)
|
|
{
|
|
QUIT;
|
|
|
|
const char *start_addr_str = paddress (arch, start_addr);
|
|
|
|
if (obj != nullptr)
|
|
{
|
|
const dwarf2_per_cu_data *per_cu
|
|
= static_cast<const dwarf2_per_cu_data *> (obj);
|
|
gdb_printf (" [%s] ((dwarf2_per_cu_data *) %p)\n",
|
|
start_addr_str, per_cu);
|
|
}
|
|
else
|
|
gdb_printf (" [%s] ((dwarf2_per_cu_data *) 0)\n",
|
|
start_addr_str);
|
|
|
|
return 0;
|
|
});
|
|
|
|
gdb_printf ("\n");
|
|
}
|
|
}
|
|
|
|
/* Wait for all the index cache entries to be written before gdb
|
|
exits. */
|
|
static void
|
|
wait_for_index_cache (int)
|
|
{
|
|
gdb_assert (is_main_thread ());
|
|
for (cooked_index *item : active_vectors)
|
|
item->wait_completely ();
|
|
}
|
|
|
|
/* A maint command to wait for the cache. */
|
|
|
|
static void
|
|
maintenance_wait_for_index_cache (const char *args, int from_tty)
|
|
{
|
|
wait_for_index_cache (0);
|
|
}
|
|
|
|
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\
|
|
Usage: maintenance wait-for-index-cache"),
|
|
&maintenancelist);
|
|
|
|
gdb::observers::gdb_exiting.attach (wait_for_index_cache, "cooked-index");
|
|
}
|