forked from Imagelibrary/binutils-gdb
With gdb build with -fsanitize=thread and test-case gdb.base/index-cache.exp I
run into:
...
(gdb) file build/gdb/testsuite/outputs/gdb.base/index-cache/index-cache
Reading symbols from build/gdb/testsuite/outputs/gdb.base/index-cache/index-cache...
(gdb) show index-cache enabled
The index cache is off.
(gdb) PASS: gdb.base/index-cache.exp: test_basic_stuff: index-cache is disabled by default
set index-cache enabled on
==================
WARNING: ThreadSanitizer: data race (pid=32248)
Write of size 1 at 0x00000321f540 by main thread:
#0 index_cache::enable() gdb/dwarf2/index-cache.c:76 (gdb+0x82cfdd)
#1 set_index_cache_enabled_command gdb/dwarf2/index-cache.c:270 (gdb+0x82d9af)
#2 bool setting::set<bool>(bool const&) gdb/command.h:353 (gdb+0x6fe5f2)
#3 do_set_command(char const*, int, cmd_list_element*) gdb/cli/cli-setshow.c:414 (gdb+0x6fcd21)
#4 execute_command(char const*, int) gdb/top.c:567 (gdb+0xff2e64)
#5 command_handler(char const*) gdb/event-top.c:552 (gdb+0x94acc0)
#6 command_line_handler(std::unique_ptr<char, gdb::xfree_deleter<char> >&&) gdb/event-top.c:788 (gdb+0x94b37d)
#7 tui_command_line_handler gdb/tui/tui-interp.c:104 (gdb+0x103467e)
#8 gdb_rl_callback_handler gdb/event-top.c:259 (gdb+0x94a265)
#9 rl_callback_read_char readline/readline/callback.c:290 (gdb+0x11bdd3f)
#10 gdb_rl_callback_read_char_wrapper_noexcept gdb/event-top.c:195 (gdb+0x94a064)
#11 gdb_rl_callback_read_char_wrapper gdb/event-top.c:234 (gdb+0x94a125)
#12 stdin_event_handler gdb/ui.c:155 (gdb+0x1074922)
#13 handle_file_event gdbsupport/event-loop.cc:573 (gdb+0x1d94de4)
#14 gdb_wait_for_event gdbsupport/event-loop.cc:694 (gdb+0x1d9551c)
#15 gdb_do_one_event(int) gdbsupport/event-loop.cc:264 (gdb+0x1d93908)
#16 start_event_loop gdb/main.c:412 (gdb+0xb5a256)
#17 captured_command_loop gdb/main.c:476 (gdb+0xb5a445)
#18 captured_main gdb/main.c:1320 (gdb+0xb5c5c5)
#19 gdb_main(captured_main_args*) gdb/main.c:1339 (gdb+0xb5c674)
#20 main gdb/gdb.c:32 (gdb+0x416776)
Previous read of size 1 at 0x00000321f540 by thread T12:
#0 index_cache::enabled() const gdb/dwarf2/index-cache.h:48 (gdb+0x82e1a6)
#1 index_cache::store(dwarf2_per_bfd*) gdb/dwarf2/index-cache.c:94 (gdb+0x82d0bc)
#2 cooked_index::maybe_write_index(dwarf2_per_bfd*) gdb/dwarf2/cooked-index.c:638 (gdb+0x7f1b97)
#3 operator() gdb/dwarf2/cooked-index.c:468 (gdb+0x7f0f24)
#4 _M_invoke /usr/include/c++/7/bits/std_function.h:316 (gdb+0x7f285b)
#5 std::function<void ()>::operator()() const /usr/include/c++/7/bits/std_function.h:706 (gdb+0x700952)
#6 void std::__invoke_impl<void, std::function<void ()>&>(std::__invoke_other, std::function<void ()>&) /usr/include/c++/7/bits/invoke.h:60 (gdb+0x7381a0)
#7 std::__invoke_result<std::function<void ()>&>::type std::__invoke<std::function<void ()>&>(std::function<void ()>&) /usr/include/c++/7/bits/invoke.h:95 (gdb+0x737e91)
#8 std::__future_base::_Task_state<std::function<void ()>, std::allocator<int>, void ()>::_M_run()::{lambda()#1}::operator()() const /usr/include/c++/7/future:1421 (gdb+0x737b59)
#9 std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_state<std::function<void ()>, std::allocator<int>, void ()>::_M_run()::{lambda()#1}, void>::operator()() const /usr/include/c++/7/future:1362 (gdb+0x738660)
#10 std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_Task_setter<std::unique_ptr<std::__future_base::_Result<void>, std::__future_base::_Result_base::_Deleter>, std::__future_base::_Task_state<std::function<void ()>, std::allocator<int>, void ()>::_M_run()::{lambda()#1}, void> >::_M_invoke(std::_Any_data const&) /usr/include/c++/7/bits/std_function.h:302 (gdb+0x73825c)
#11 std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>::operator()() const /usr/include/c++/7/bits/std_function.h:706 (gdb+0x733623)
#12 std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) /usr/include/c++/7/future:561 (gdb+0x732bdf)
#13 void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) /usr/include/c++/7/bits/invoke.h:73 (gdb+0x734c4f)
#14 std::__invoke_result<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>::type std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) /usr/include/c++/7/bits/invoke.h:95 (gdb+0x733bc5)
#15 std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}::operator()() const /usr/include/c++/7/mutex:672 (gdb+0x73300d)
#16 std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#2}::operator()() const /usr/include/c++/7/mutex:677 (gdb+0x7330b2)
#17 std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#2}::_FUN() /usr/include/c++/7/mutex:677 (gdb+0x7330f2)
#18 pthread_once <null> (libtsan.so.0+0x4457c)
#19 __gthread_once /usr/include/c++/7/x86_64-suse-linux/bits/gthr-default.h:699 (gdb+0x72f5dd)
#20 void std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) /usr/include/c++/7/mutex:684 (gdb+0x733224)
#21 std::__future_base::_State_baseV2::_M_set_result(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>, bool) /usr/include/c++/7/future:401 (gdb+0x732852)
#22 std::__future_base::_Task_state<std::function<void ()>, std::allocator<int>, void ()>::_M_run() /usr/include/c++/7/future:1423 (gdb+0x737bef)
#23 std::packaged_task<void ()>::operator()() /usr/include/c++/7/future:1556 (gdb+0x1dac492)
#24 gdb::thread_pool::thread_function() gdbsupport/thread-pool.cc:242 (gdb+0x1dabdb4)
#25 void std::__invoke_impl<void, void (gdb::thread_pool::*)(), gdb::thread_pool*>(std::__invoke_memfun_deref, void (gdb::thread_pool::*&&)(), gdb::thread_pool*&&) /usr/include/c++/7/bits/invoke.h:73 (gdb+0x1dace63)
#26 std::__invoke_result<void (gdb::thread_pool::*)(), gdb::thread_pool*>::type std::__invoke<void (gdb::thread_pool::*)(), gdb::thread_pool*>(void (gdb::thread_pool::*&&)(), gdb::thread_pool*&&) /usr/include/c++/7/bits/invoke.h:95 (gdb+0x1dac294)
#27 decltype (__invoke((_S_declval<0ul>)(), (_S_declval<1ul>)())) std:🧵:_Invoker<std::tuple<void (gdb::thread_pool::*)(), gdb::thread_pool*> >::_M_invoke<0ul, 1ul>(std::_Index_tuple<0ul, 1ul>) /usr/include/c++/7/thread:234 (gdb+0x1daf5c6)
#28 std:🧵:_Invoker<std::tuple<void (gdb::thread_pool::*)(), gdb::thread_pool*> >::operator()() /usr/include/c++/7/thread:243 (gdb+0x1daf551)
#29 std:🧵:_State_impl<std:🧵:_Invoker<std::tuple<void (gdb::thread_pool::*)(), gdb::thread_pool*> > >::_M_run() /usr/include/c++/7/thread:186 (gdb+0x1daf506)
#30 <null> <null> (libstdc++.so.6+0xdcac2)
Location is global 'global_index_cache' of size 48 at 0x00000321f520 (gdb+0x00000321f540)
...
SUMMARY: ThreadSanitizer: data race gdb/dwarf2/index-cache.c:76 in index_cache::enable()
...
The race happens when issuing a "file $exec" command followed by a
"set index-cache enabled on" command.
The race is between:
- a worker thread reading index_cache::m_enabled to determine whether an
index-cache entry for $exec needs to be written
(due to command "file $exec"), and
- the main thread setting index_cache::m_enabled
(due to command "set index-cache enabled on").
Fix this by capturing the value of index_cache::m_enabled in the main thread,
and using the captured value in the worker thread.
Tested on x86_64-linux.
PR symtab/30392
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30392
451 lines
15 KiB
C++
451 lines
15 KiB
C++
/* 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 <http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef GDB_DWARF2_COOKED_INDEX_H
|
|
#define GDB_DWARF2_COOKED_INDEX_H
|
|
|
|
#include "dwarf2.h"
|
|
#include "dwarf2/types.h"
|
|
#include "symtab.h"
|
|
#include "hashtab.h"
|
|
#include "dwarf2/index-common.h"
|
|
#include "gdbsupport/gdb_string_view.h"
|
|
#include "quick-symbol.h"
|
|
#include "gdbsupport/gdb_obstack.h"
|
|
#include "addrmap.h"
|
|
#include "gdbsupport/iterator-range.h"
|
|
#include "gdbsupport/thread-pool.h"
|
|
#include "dwarf2/mapped-index.h"
|
|
#include "dwarf2/tag.h"
|
|
#include "gdbsupport/range-chain.h"
|
|
|
|
struct dwarf2_per_cu_data;
|
|
struct dwarf2_per_bfd;
|
|
struct index_cache_store_context;
|
|
|
|
/* Flags that describe an entry in the index. */
|
|
enum cooked_index_flag_enum : unsigned char
|
|
{
|
|
/* True if this entry is the program's "main". */
|
|
IS_MAIN = 1,
|
|
/* True if this entry represents a "static" object. */
|
|
IS_STATIC = 2,
|
|
/* True if this entry is an "enum class". */
|
|
IS_ENUM_CLASS = 4,
|
|
/* True if this entry uses the linkage name. */
|
|
IS_LINKAGE = 8,
|
|
/* True if this entry is just for the declaration of a type, not the
|
|
definition. */
|
|
IS_TYPE_DECLARATION = 16,
|
|
};
|
|
DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag);
|
|
|
|
/* Return a string representation of FLAGS. */
|
|
|
|
std::string to_string (cooked_index_flag flags);
|
|
|
|
/* 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::do_finalize. */
|
|
|
|
extern bool language_requires_canonicalization (enum language lang);
|
|
|
|
/* A cooked_index_entry represents a single item in the index. Note
|
|
that two entries can be created for the same DIE -- one using the
|
|
name, and another one using the linkage name, if any.
|
|
|
|
This is an "open" class and the members are all directly
|
|
accessible. It is read-only after the index has been fully read
|
|
and processed. */
|
|
struct cooked_index_entry : public allocate_on_obstack
|
|
{
|
|
cooked_index_entry (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_)
|
|
: name (name_),
|
|
tag (tag_),
|
|
flags (flags_),
|
|
die_offset (die_offset_),
|
|
parent_entry (parent_entry_),
|
|
per_cu (per_cu_)
|
|
{
|
|
}
|
|
|
|
/* Return true if this entry matches SEARCH_FLAGS. */
|
|
bool matches (block_search_flags search_flags) const
|
|
{
|
|
/* Just reject type declarations. */
|
|
if ((flags & IS_TYPE_DECLARATION) != 0)
|
|
return false;
|
|
|
|
if ((search_flags & SEARCH_STATIC_BLOCK) != 0
|
|
&& (flags & IS_STATIC) != 0)
|
|
return true;
|
|
if ((search_flags & SEARCH_GLOBAL_BLOCK) != 0
|
|
&& (flags & IS_STATIC) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/* Return true if this entry matches DOMAIN. */
|
|
bool matches (domain_enum domain) const
|
|
{
|
|
/* Just reject type declarations. */
|
|
if ((flags & IS_TYPE_DECLARATION) != 0)
|
|
return false;
|
|
|
|
switch (domain)
|
|
{
|
|
case LABEL_DOMAIN:
|
|
return false;
|
|
|
|
case MODULE_DOMAIN:
|
|
return tag == DW_TAG_module;
|
|
|
|
case COMMON_BLOCK_DOMAIN:
|
|
return tag == DW_TAG_common_block;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Return true if this entry matches KIND. */
|
|
bool matches (enum search_domain kind) const
|
|
{
|
|
/* Just reject type declarations. */
|
|
if ((flags & IS_TYPE_DECLARATION) != 0)
|
|
return false;
|
|
|
|
switch (kind)
|
|
{
|
|
case VARIABLES_DOMAIN:
|
|
return (tag == DW_TAG_variable
|
|
|| tag == DW_TAG_constant
|
|
|| tag == DW_TAG_enumerator);
|
|
case FUNCTIONS_DOMAIN:
|
|
return tag == DW_TAG_subprogram;
|
|
case TYPES_DOMAIN:
|
|
return tag_is_type (tag);
|
|
case MODULES_DOMAIN:
|
|
return tag == DW_TAG_module;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Construct the fully-qualified name of this entry and return a
|
|
pointer to it. If allocation is needed, it will be done on
|
|
STORAGE. FOR_MAIN is true if we are computing the name of the
|
|
"main" entry -- one marked DW_AT_main_subprogram. This matters
|
|
for avoiding name canonicalization and also a related race (if
|
|
"main" computation is done during finalization). */
|
|
const char *full_name (struct obstack *storage, bool for_main = false) const;
|
|
|
|
/* Comparison modes for the 'compare' function. See the function
|
|
for a description. */
|
|
enum comparison_mode
|
|
{
|
|
MATCH,
|
|
SORT,
|
|
COMPLETE,
|
|
};
|
|
|
|
/* Compare two strings, case-insensitively. Return -1 if STRA is
|
|
less than STRB, 0 if they are equal, and 1 if STRA is greater.
|
|
|
|
When comparing, '<' is considered to be less than all other
|
|
printable characters. This ensures that "t<x>" sorts before
|
|
"t1", which is necessary when looking up "t". This '<' handling
|
|
is to ensure that certain C++ lookups work correctly. It is
|
|
inexact, and applied regardless of the search language, but this
|
|
is ok because callers of this code do more precise filtering
|
|
according to their needs. This is also why using a
|
|
case-insensitive comparison works even for languages that are
|
|
case sensitive.
|
|
|
|
MODE controls how the comparison proceeds.
|
|
|
|
MODE==SORT is used when sorting and the only special '<' handling
|
|
that it does is to ensure that '<' sorts before all other
|
|
printable characters. This ensures that the resulting ordering
|
|
will be binary-searchable.
|
|
|
|
MODE==MATCH is used when searching for a symbol. In this case,
|
|
STRB must always be the search name, and STRA must be the name in
|
|
the index that is under consideration. In compare mode, early
|
|
termination of STRB may match STRA -- for example, "t<int>" and
|
|
"t" will be considered to be equal. (However, if A=="t" and
|
|
B=="t<int>", then this will not consider them as equal.)
|
|
|
|
MODE==COMPLETE is used when searching for a symbol for
|
|
completion. In this case, STRB must always be the search name,
|
|
and STRA must be the name in the index that is under
|
|
consideration. In completion mode, early termination of STRB
|
|
always results in a match. */
|
|
static int compare (const char *stra, const char *strb,
|
|
comparison_mode mode);
|
|
|
|
/* Compare two entries by canonical name. */
|
|
bool operator< (const cooked_index_entry &other) const
|
|
{
|
|
return compare (canonical, other.canonical, SORT) < 0;
|
|
}
|
|
|
|
/* 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
|
|
attributes. */
|
|
const char *name;
|
|
/* The canonical name. For C++ names, this may differ from NAME.
|
|
In all other cases, this is equal to NAME. */
|
|
const char *canonical = nullptr;
|
|
/* The DWARF tag. */
|
|
enum dwarf_tag tag;
|
|
/* Any flags attached to this entry. */
|
|
cooked_index_flag flags;
|
|
/* The offset of this DIE. */
|
|
sect_offset die_offset;
|
|
/* The parent entry. This is NULL for top-level entries.
|
|
Otherwise, it points to the parent entry, such as a namespace or
|
|
class. */
|
|
const cooked_index_entry *parent_entry;
|
|
/* The CU from which this entry originates. */
|
|
dwarf2_per_cu_data *per_cu;
|
|
|
|
private:
|
|
|
|
/* A helper method for full_name. Emits the full scope of this
|
|
object, followed by the separator, to STORAGE. If this entry has
|
|
a parent, its write_scope method is called first. */
|
|
void write_scope (struct obstack *storage, const char *sep,
|
|
bool for_name) const;
|
|
};
|
|
|
|
class cooked_index;
|
|
|
|
/* An index of interesting DIEs. This is "cooked", in contrast to a
|
|
mapped .debug_names or .gdb_index, which are "raw". An entry in
|
|
the index is of type cooked_index_entry.
|
|
|
|
Operations on the index are described below. They are chosen to
|
|
make it relatively simple to implement the symtab "quick"
|
|
methods. */
|
|
class cooked_index_shard
|
|
{
|
|
public:
|
|
cooked_index_shard () = default;
|
|
DISABLE_COPY_AND_ASSIGN (cooked_index_shard);
|
|
|
|
/* Create a new cooked_index_entry and register it with this object.
|
|
Entries are owned by this object. The new item is returned. */
|
|
const cooked_index_entry *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);
|
|
|
|
/* Install a new fixed addrmap from the given mutable addrmap. */
|
|
void install_addrmap (addrmap_mutable *map)
|
|
{
|
|
gdb_assert (m_addrmap == nullptr);
|
|
m_addrmap = new (&m_storage) addrmap_fixed (&m_storage, map);
|
|
}
|
|
|
|
/* Finalize the index. This should be called a single time, when
|
|
the index has been fully populated. It enters all the entries
|
|
into the internal table. */
|
|
void finalize ();
|
|
|
|
/* Wait for this index's finalization to be complete. */
|
|
void wait (bool allow_quit = true) const;
|
|
|
|
friend class cooked_index;
|
|
|
|
/* A simple range over part of m_entries. */
|
|
typedef iterator_range<std::vector<cooked_index_entry *>::const_iterator>
|
|
range;
|
|
|
|
/* Return a range of all the entries. */
|
|
range all_entries () const
|
|
{
|
|
wait ();
|
|
return { m_entries.cbegin (), m_entries.cend () };
|
|
}
|
|
|
|
/* Look up an entry by name. Returns a range of all matching
|
|
results. If COMPLETING is true, then a larger range, suitable
|
|
for completion, will be returned. */
|
|
range find (const std::string &name, bool completing) const;
|
|
|
|
private:
|
|
|
|
/* Return the entry that is believed to represent the program's
|
|
"main". This will return NULL if no such entry is available. */
|
|
const cooked_index_entry *get_main () const
|
|
{
|
|
return m_main;
|
|
}
|
|
|
|
/* Look up ADDR in the address map, and return either the
|
|
corresponding CU, or nullptr if the address could not be
|
|
found. */
|
|
dwarf2_per_cu_data *lookup (CORE_ADDR addr)
|
|
{
|
|
return static_cast<dwarf2_per_cu_data *> (m_addrmap->find (addr));
|
|
}
|
|
|
|
/* Create a new cooked_index_entry and register it with this object.
|
|
Entries are owned by this object. The new item is returned. */
|
|
cooked_index_entry *create (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)
|
|
{
|
|
return new (&m_storage) cooked_index_entry (die_offset, tag, flags,
|
|
name, parent_entry,
|
|
per_cu);
|
|
}
|
|
|
|
/* GNAT only emits mangled ("encoded") names in the DWARF, and does
|
|
not emit the module structure. However, we need this structure
|
|
to do lookups. This function recreates that structure for an
|
|
existing entry. It returns the base name (last element) of the
|
|
full decoded name. */
|
|
gdb::unique_xmalloc_ptr<char> handle_gnat_encoded_entry
|
|
(cooked_index_entry *entry, htab_t gnat_entries);
|
|
|
|
/* A helper method that does the work of 'finalize'. */
|
|
void do_finalize ();
|
|
|
|
/* Storage for the entries. */
|
|
auto_obstack m_storage;
|
|
/* List of all entries. */
|
|
std::vector<cooked_index_entry *> m_entries;
|
|
/* If we found an entry with 'is_main' set, store it here. */
|
|
cooked_index_entry *m_main = nullptr;
|
|
/* The addrmap. This maps address ranges to dwarf2_per_cu_data
|
|
objects. */
|
|
addrmap *m_addrmap = nullptr;
|
|
/* Storage for canonical names. */
|
|
std::vector<gdb::unique_xmalloc_ptr<char>> m_names;
|
|
/* A future that tracks when the 'finalize' method is done. Note
|
|
that the 'get' method is never called on this future, only
|
|
'wait'. */
|
|
gdb::future<void> m_future;
|
|
};
|
|
|
|
/* The main index of DIEs. The parallel DIE indexers create
|
|
cooked_index_shard objects. Then, these are all handled to a
|
|
cooked_index for storage and final indexing. The index is
|
|
made by iterating over the entries previously created. */
|
|
|
|
class cooked_index : public dwarf_scanner_base
|
|
{
|
|
public:
|
|
|
|
/* A convenience typedef for the vector that is contained in this
|
|
object. */
|
|
using vec_type = std::vector<std::unique_ptr<cooked_index_shard>>;
|
|
|
|
explicit cooked_index (vec_type &&vec);
|
|
~cooked_index () override;
|
|
DISABLE_COPY_AND_ASSIGN (cooked_index);
|
|
|
|
/* Wait until the finalization of the entire cooked_index is
|
|
done. */
|
|
void wait () const
|
|
{
|
|
for (auto &item : m_vector)
|
|
item->wait ();
|
|
}
|
|
|
|
/* A range over a vector of subranges. */
|
|
using range = range_chain<cooked_index_shard::range>;
|
|
|
|
/* Look up an entry by name. Returns a range of all matching
|
|
results. If COMPLETING is true, then a larger range, suitable
|
|
for completion, will be returned. */
|
|
range find (const std::string &name, bool completing) const;
|
|
|
|
/* Return a range of all the entries. */
|
|
range all_entries () const
|
|
{
|
|
std::vector<cooked_index_shard::range> result_range;
|
|
result_range.reserve (m_vector.size ());
|
|
for (auto &entry : m_vector)
|
|
result_range.push_back (entry->all_entries ());
|
|
return range (std::move (result_range));
|
|
}
|
|
|
|
/* Look up ADDR in the address map, and return either the
|
|
corresponding CU, or nullptr if the address could not be
|
|
found. */
|
|
dwarf2_per_cu_data *lookup (CORE_ADDR addr);
|
|
|
|
/* Return a new vector of all the addrmaps used by all the indexes
|
|
held by this object. */
|
|
std::vector<const addrmap *> get_addrmaps () const;
|
|
|
|
/* Return the entry that is believed to represent the program's
|
|
"main". This will return NULL if no such entry is available. */
|
|
const cooked_index_entry *get_main () const;
|
|
|
|
cooked_index *index_for_writing () override
|
|
{
|
|
return this;
|
|
}
|
|
|
|
quick_symbol_functions_up make_quick_functions () const override;
|
|
|
|
/* Dump a human-readable form of the contents of the index. */
|
|
void dump (gdbarch *arch) const;
|
|
|
|
/* Wait for the index to be completely finished. For ordinary uses,
|
|
the index code ensures this itself -- e.g., 'all_entries' will
|
|
wait on the 'finalize' future. However, on destruction, if an
|
|
index is being written, it's also necessary to wait for that to
|
|
complete. */
|
|
void wait_completely () override
|
|
{
|
|
m_write_future.wait ();
|
|
}
|
|
|
|
/* Start writing to the index cache, if the user asked for this. */
|
|
void start_writing_index (dwarf2_per_bfd *per_bfd);
|
|
|
|
private:
|
|
|
|
/* Maybe write the index to the index cache. */
|
|
void maybe_write_index (dwarf2_per_bfd *per_bfd,
|
|
const index_cache_store_context &);
|
|
|
|
/* The vector of cooked_index objects. This is stored because the
|
|
entries are stored on the obstacks in those objects. */
|
|
vec_type m_vector;
|
|
|
|
/* A future that tracks when the 'index_write' method is done. */
|
|
gdb::future<void> m_write_future;
|
|
};
|
|
|
|
#endif /* GDB_DWARF2_COOKED_INDEX_H */
|