mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-23 10:03:47 +08:00
3e0c29b24a
While looking at the cooked index entry for local variable l4 of function test in test-case gdb.fortran/logical.exp: ... $ gdb -q -batch outputs/gdb.fortran/logical/logical \ -ex "maint print objfiles" ... [9] ((cooked_index_entry *) 0x7fc6e0003010) name: l4 canonical: l4 qualified: l4 DWARF tag: DW_TAG_variable flags: 0x2 [IS_STATIC] DIE offset: 0x17c parent: ((cooked_index_entry *) 0x7fc6e0002f20) [test] ... I noticed that while the entry does have a parent, that's not reflected in the qualified name. This makes it harder to write test-cases that check the parent of a cooked index entry. This is due to the implementation of full_name, which skips printing parents if the language does not specify an appropriate separator. Fix this by using "::" as default separator, getting us instead: ... [9] ((cooked_index_entry *) 0x7f94ec0040c0) name: l4 canonical: l4 qualified: test::l4 DWARF tag: DW_TAG_variable flags: 0x2 [IS_STATIC] DIE offset: 0x17c parent: ((cooked_index_entry *) 0x7f94ec003fd0) [test] ... Tested on x86_64-linux. Approved-By: Tom Tromey <tom@tromey.com>
814 lines
27 KiB
C++
814 lines
27 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/>. */
|
|
|
|
#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 "quick-symbol.h"
|
|
#include "gdbsupport/gdb_obstack.h"
|
|
#include "addrmap.h"
|
|
#include "gdbsupport/iterator-range.h"
|
|
#include "dwarf2/mapped-index.h"
|
|
#include "dwarf2/read.h"
|
|
#include "dwarf2/abbrev-cache.h"
|
|
#include "dwarf2/parent-map.h"
|
|
#include "gdbsupport/range-chain.h"
|
|
#include "complaints.h"
|
|
|
|
#if CXX_STD_THREAD
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#endif /* CXX_STD_THREAD */
|
|
|
|
struct dwarf2_per_cu_data;
|
|
struct dwarf2_per_bfd;
|
|
struct index_cache_store_context;
|
|
struct cooked_index_entry;
|
|
|
|
/* 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 uses the linkage name. */
|
|
IS_LINKAGE = 4,
|
|
/* True if this entry is just for the declaration of a type, not the
|
|
definition. */
|
|
IS_TYPE_DECLARATION = 8,
|
|
/* True is parent_entry.deferred has a value rather than parent_entry
|
|
.resolved. */
|
|
IS_PARENT_DEFERRED = 16,
|
|
};
|
|
DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag);
|
|
|
|
/* Type representing either a resolved or deferred cooked_index_entry. */
|
|
|
|
union cooked_index_entry_ref
|
|
{
|
|
cooked_index_entry_ref (parent_map::addr_type deferred_)
|
|
{
|
|
deferred = deferred_;
|
|
}
|
|
|
|
cooked_index_entry_ref (const cooked_index_entry *resolved_)
|
|
{
|
|
resolved = resolved_;
|
|
}
|
|
|
|
const cooked_index_entry *resolved;
|
|
parent_map::addr_type deferred;
|
|
};
|
|
|
|
/* 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::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>
|
|
{
|
|
cooked_index_entry (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_)
|
|
: name (name_),
|
|
tag (tag_),
|
|
flags (flags_),
|
|
lang (lang_),
|
|
die_offset (die_offset_),
|
|
per_cu (per_cu_),
|
|
m_parent_entry (parent_entry_)
|
|
{
|
|
}
|
|
|
|
/* 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 KIND. */
|
|
bool matches (domain_search_flags kind) const;
|
|
|
|
/* 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). If the language
|
|
doesn't prescribe a separator, one can be specified using
|
|
DEFAULT_SEP. */
|
|
const char *full_name (struct obstack *storage, bool for_main = false,
|
|
const char *default_sep = nullptr) 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;
|
|
}
|
|
|
|
/* Set parent entry to PARENT. */
|
|
void set_parent (const cooked_index_entry *parent)
|
|
{
|
|
gdb_assert ((flags & IS_PARENT_DEFERRED) == 0);
|
|
m_parent_entry.resolved = parent;
|
|
}
|
|
|
|
/* Resolve deferred parent entry to PARENT. */
|
|
void resolve_parent (const cooked_index_entry *parent)
|
|
{
|
|
gdb_assert ((flags & IS_PARENT_DEFERRED) != 0);
|
|
flags = flags & ~IS_PARENT_DEFERRED;
|
|
m_parent_entry.resolved = parent;
|
|
}
|
|
|
|
/* Return parent entry. */
|
|
const cooked_index_entry *get_parent () const
|
|
{
|
|
gdb_assert ((flags & IS_PARENT_DEFERRED) == 0);
|
|
return m_parent_entry.resolved;
|
|
}
|
|
|
|
/* Return deferred parent entry. */
|
|
parent_map::addr_type get_deferred_parent () const
|
|
{
|
|
gdb_assert ((flags & IS_PARENT_DEFERRED) != 0);
|
|
return m_parent_entry.deferred;
|
|
}
|
|
|
|
/* 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. This may be 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 language of this symbol. */
|
|
ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
|
|
/* The offset of this DIE. */
|
|
sect_offset die_offset;
|
|
/* 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. FOR_MAIN is
|
|
true when computing the name of 'main'; see full_name. */
|
|
void write_scope (struct obstack *storage, const char *sep,
|
|
bool for_main) const;
|
|
|
|
/* The parent entry. This is NULL for top-level entries.
|
|
Otherwise, it points to the parent entry, such as a namespace or
|
|
class. */
|
|
cooked_index_entry_ref m_parent_entry;
|
|
};
|
|
|
|
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. */
|
|
cooked_index_entry *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);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
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
|
|
{
|
|
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 (unrelocated_addr addr)
|
|
{
|
|
return (static_cast<dwarf2_per_cu_data *>
|
|
(m_addrmap->find ((CORE_ADDR) 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,
|
|
enum language lang,
|
|
const char *name,
|
|
cooked_index_entry_ref parent_entry,
|
|
dwarf2_per_cu_data *per_cu)
|
|
{
|
|
return new (&m_storage) cooked_index_entry (die_offset, tag, flags,
|
|
lang, 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, modifying ENTRY as appropriate. */
|
|
void handle_gnat_encoded_entry
|
|
(cooked_index_entry *entry, htab_t gnat_entries);
|
|
|
|
/* 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 and fixes up all missing parent links.
|
|
This may be invoked in a worker thread. */
|
|
void finalize (const parent_map_map *parent_maps);
|
|
|
|
/* 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_fixed *m_addrmap = nullptr;
|
|
/* Storage for canonical names. */
|
|
std::vector<gdb::unique_xmalloc_ptr<char>> m_names;
|
|
};
|
|
|
|
class cutu_reader;
|
|
|
|
/* An instance of this is created when scanning DWARF to create a
|
|
cooked index. */
|
|
|
|
class cooked_index_storage
|
|
{
|
|
public:
|
|
|
|
cooked_index_storage ();
|
|
DISABLE_COPY_AND_ASSIGN (cooked_index_storage);
|
|
|
|
/* Return the current abbrev cache. */
|
|
abbrev_cache *get_abbrev_cache ()
|
|
{
|
|
return &m_abbrev_cache;
|
|
}
|
|
|
|
/* Return the DIE reader corresponding to PER_CU. If no such reader
|
|
has been registered, return NULL. */
|
|
cutu_reader *get_reader (dwarf2_per_cu_data *per_cu);
|
|
|
|
/* Preserve READER by storing it in the local hash table. */
|
|
cutu_reader *preserve (std::unique_ptr<cutu_reader> reader);
|
|
|
|
/* Add an entry to the index. The arguments describe the entry; see
|
|
cooked-index.h. The new entry is returned. */
|
|
cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
|
|
cooked_index_flag flags,
|
|
const char *name,
|
|
cooked_index_entry_ref parent_entry,
|
|
dwarf2_per_cu_data *per_cu)
|
|
{
|
|
return m_index->add (die_offset, tag, flags, per_cu->lang (),
|
|
name, parent_entry, per_cu);
|
|
}
|
|
|
|
/* Install the current addrmap into the shard being constructed,
|
|
then transfer ownership of the index to the caller. */
|
|
std::unique_ptr<cooked_index_shard> release ()
|
|
{
|
|
m_index->install_addrmap (&m_addrmap);
|
|
return std::move (m_index);
|
|
}
|
|
|
|
/* Return the mutable addrmap that is currently being created. */
|
|
addrmap_mutable *get_addrmap ()
|
|
{
|
|
return &m_addrmap;
|
|
}
|
|
|
|
/* Return the parent_map that is currently being created. */
|
|
parent_map *get_parent_map ()
|
|
{
|
|
return &m_parent_map;
|
|
}
|
|
|
|
/* Return the parent_map that is currently being created. Ownership
|
|
is passed to the caller. */
|
|
parent_map release_parent_map ()
|
|
{
|
|
return std::move (m_parent_map);
|
|
}
|
|
|
|
private:
|
|
|
|
/* Hash function for a cutu_reader. */
|
|
static hashval_t hash_cutu_reader (const void *a);
|
|
|
|
/* Equality function for cutu_reader. */
|
|
static int eq_cutu_reader (const void *a, const void *b);
|
|
|
|
/* The abbrev cache used by this indexer. */
|
|
abbrev_cache m_abbrev_cache;
|
|
/* A hash table of cutu_reader objects. */
|
|
htab_up m_reader_hash;
|
|
/* The index shard that is being constructed. */
|
|
std::unique_ptr<cooked_index_shard> m_index;
|
|
|
|
/* Parent map for each CU that is read. */
|
|
parent_map m_parent_map;
|
|
|
|
/* A writeable addrmap being constructed by this scanner. */
|
|
addrmap_mutable m_addrmap;
|
|
};
|
|
|
|
/* The possible states of the index. See the explanatory comment
|
|
before cooked_index for more details. */
|
|
enum class cooked_state
|
|
{
|
|
/* The default state. This is not a valid argument to 'wait'. */
|
|
INITIAL,
|
|
/* The initial scan has completed. The name of "main" is now
|
|
available (if known). The addrmaps are usable now.
|
|
Finalization has started but is not complete. */
|
|
MAIN_AVAILABLE,
|
|
/* Finalization has completed. This means the index is fully
|
|
available for queries. */
|
|
FINALIZED,
|
|
/* Writing to the index cache has finished. */
|
|
CACHE_DONE,
|
|
};
|
|
|
|
/* An object of this type controls the scanning of the DWARF. It
|
|
schedules the worker tasks and tracks the current state. Once
|
|
scanning is done, this object is discarded.
|
|
|
|
This is an abstract base class that defines the basic behavior of
|
|
scanners. Separate concrete implementations exist for scanning
|
|
.debug_names and .debug_info. */
|
|
|
|
class cooked_index_worker
|
|
{
|
|
public:
|
|
|
|
explicit cooked_index_worker (dwarf2_per_objfile *per_objfile)
|
|
: m_per_objfile (per_objfile),
|
|
m_cache_store (global_index_cache, per_objfile->per_bfd)
|
|
{ }
|
|
virtual ~cooked_index_worker ()
|
|
{ }
|
|
DISABLE_COPY_AND_ASSIGN (cooked_index_worker);
|
|
|
|
/* Start reading. */
|
|
void start ();
|
|
|
|
/* Wait for a particular state to be achieved. If ALLOW_QUIT is
|
|
true, then the loop will check the QUIT flag. Normally this
|
|
method may only be called from the main thread; however, it can
|
|
be called from a worker thread provided that the desired state
|
|
has already been attained. (This oddity is used by the index
|
|
cache writer.) */
|
|
bool wait (cooked_state desired_state, bool allow_quit);
|
|
|
|
protected:
|
|
|
|
/* Let cooked_index call the 'set' and 'write_to_cache' methods. */
|
|
friend class cooked_index;
|
|
|
|
/* Set the current state. */
|
|
void set (cooked_state desired_state);
|
|
|
|
/* Write to the index cache. */
|
|
void write_to_cache (const cooked_index *idx,
|
|
deferred_warnings *warn) const;
|
|
|
|
/* Helper function that does the work of reading. This must be able
|
|
to be run in a worker thread without problems. */
|
|
virtual void do_reading () = 0;
|
|
|
|
/* A callback that can print stats, if needed. This is called when
|
|
transitioning to the 'MAIN_AVAILABLE' state. */
|
|
virtual void print_stats ()
|
|
{ }
|
|
|
|
/* Each thread returns a tuple holding a cooked index, any collected
|
|
complaints, a vector of errors that should be printed, and a
|
|
vector of parent maps.
|
|
|
|
The errors are retained because GDB's I/O system is not
|
|
thread-safe. run_on_main_thread could be used, but that would
|
|
mean the messages are printed after the prompt, which looks
|
|
weird. */
|
|
using result_type = std::tuple<std::unique_ptr<cooked_index_shard>,
|
|
complaint_collection,
|
|
std::vector<gdb_exception>,
|
|
parent_map>;
|
|
|
|
/* The per-objfile object. */
|
|
dwarf2_per_objfile *m_per_objfile;
|
|
/* Result of each worker task. */
|
|
std::vector<result_type> m_results;
|
|
/* Any warnings emitted. This is not in 'result_type' because (for
|
|
the time being at least), it's only needed in do_reading, not in
|
|
every worker. Note that deferred_warnings uses gdb_stderr in its
|
|
constructor, and this should only be done from the main thread.
|
|
This is enforced in the cooked_index_worker constructor. */
|
|
deferred_warnings m_warnings;
|
|
|
|
/* A map of all parent maps. Used during finalization to fix up
|
|
parent relationships. */
|
|
parent_map_map m_all_parents_map;
|
|
|
|
#if CXX_STD_THREAD
|
|
/* Current state of this object. */
|
|
cooked_state m_state = cooked_state::INITIAL;
|
|
/* Mutex and condition variable used to synchronize. */
|
|
std::mutex m_mutex;
|
|
std::condition_variable m_cond;
|
|
#endif /* CXX_STD_THREAD */
|
|
/* This flag indicates whether any complaints or exceptions that
|
|
arose during scanning have been reported by 'wait'. This may
|
|
only be modified on the main thread. */
|
|
bool m_reported = false;
|
|
/* If set, an exception occurred during reading; in this case the
|
|
scanning is stopped and this exception will later be reported by
|
|
the 'wait' method. */
|
|
std::optional<gdb_exception> m_failed;
|
|
/* An object used to write to the index cache. */
|
|
index_cache_store_context m_cache_store;
|
|
};
|
|
|
|
/* The main index of DIEs.
|
|
|
|
The index is created by multiple threads. The overall process is
|
|
somewhat complicated, so here's a diagram to help sort it out.
|
|
|
|
The basic idea behind this design is (1) to do as much work as
|
|
possible in worker threads, and (2) to start the work as early as
|
|
possible. This combination should help hide the effort from the
|
|
user to the maximum possible degree.
|
|
|
|
. Main Thread | Worker Threads
|
|
============================================================
|
|
. dwarf2_initialize_objfile
|
|
. |
|
|
. v
|
|
. cooked index ------------> cooked_index_worker::start
|
|
. | / | \
|
|
. v / | \
|
|
. install / | \
|
|
. cooked_index_functions scan CUs in workers
|
|
. | create cooked_index_shard objects
|
|
. | \ | /
|
|
. v \|/
|
|
. return to caller v
|
|
. initial scan is done
|
|
. state = MAIN_AVAILABLE
|
|
. "main" name now available
|
|
. |
|
|
. |
|
|
. if main thread calls... v
|
|
. compute_main_name cooked_index::set_contents
|
|
. | / | \
|
|
. v / | \
|
|
. wait (MAIN_AVAILABLE) finalization
|
|
. | \ | /
|
|
. v \ | /
|
|
. done state = FINALIZED
|
|
. |
|
|
. v
|
|
. maybe write to index cache
|
|
. state = CACHE_DONE
|
|
.
|
|
.
|
|
. if main thread calls...
|
|
. any other "quick" API
|
|
. |
|
|
. v
|
|
. wait (FINALIZED)
|
|
. |
|
|
. v
|
|
. use the index
|
|
*/
|
|
|
|
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>>;
|
|
|
|
cooked_index (dwarf2_per_objfile *per_objfile,
|
|
std::unique_ptr<cooked_index_worker> &&worker);
|
|
~cooked_index () override;
|
|
|
|
DISABLE_COPY_AND_ASSIGN (cooked_index);
|
|
|
|
/* Start reading the DWARF. */
|
|
void start_reading ();
|
|
|
|
/* Called by cooked_index_worker to set the contents of this index
|
|
and transition to the MAIN_AVAILABLE state. WARN is used to
|
|
collect any warnings that may arise when writing to the cache.
|
|
PARENT_MAPS is used when resolving pending parent links.
|
|
PARENT_MAPS may be NULL if there are no IS_PARENT_DEFERRED
|
|
entries in VEC. */
|
|
void set_contents (vec_type &&vec, deferred_warnings *warn,
|
|
const parent_map_map *parent_maps);
|
|
|
|
/* 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);
|
|
|
|
/* Return a range of all the entries. */
|
|
range all_entries ()
|
|
{
|
|
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->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 (unrelocated_addr addr) override;
|
|
|
|
/* Return a new vector of all the addrmaps used by all the indexes
|
|
held by this object. */
|
|
std::vector<const addrmap *> get_addrmaps ();
|
|
|
|
/* 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;
|
|
|
|
const char *get_main_name (struct obstack *obstack, enum language *lang)
|
|
const;
|
|
|
|
cooked_index *index_for_writing () override
|
|
{
|
|
wait (cooked_state::FINALIZED, true);
|
|
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);
|
|
|
|
/* Wait until this object reaches the desired state. Note that
|
|
DESIRED_STATE may not be INITIAL -- it does not make sense to
|
|
wait for this. If ALLOW_QUIT is true, timed waits will be done
|
|
and the quit flag will be checked in a loop. This may normally
|
|
only be called from the main thread; however, it is ok to call
|
|
from a worker as long as the desired state has already been
|
|
attained. (This property is needed by the index cache
|
|
writer.) */
|
|
void wait (cooked_state desired_state, bool allow_quit = false);
|
|
|
|
void wait_completely () override
|
|
{ wait (cooked_state::CACHE_DONE); }
|
|
|
|
private:
|
|
|
|
/* The vector of cooked_index objects. This is stored because the
|
|
entries are stored on the obstacks in those objects. */
|
|
vec_type m_vector;
|
|
|
|
/* This tracks the current state. When this is nullptr, it means
|
|
that the state is CACHE_DONE -- it's important to note that only
|
|
the main thread may change the value of this pointer. */
|
|
std::unique_ptr<cooked_index_worker> m_state;
|
|
|
|
dwarf2_per_bfd *m_per_bfd;
|
|
};
|
|
|
|
/* An implementation of quick_symbol_functions for the cooked DWARF
|
|
index. */
|
|
|
|
struct cooked_index_functions : public dwarf2_base_index_functions
|
|
{
|
|
cooked_index *wait (struct objfile *objfile, bool allow_quit)
|
|
{
|
|
dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
|
|
cooked_index *table
|
|
= (gdb::checked_static_cast<cooked_index *>
|
|
(per_objfile->per_bfd->index_table.get ()));
|
|
table->wait (cooked_state::MAIN_AVAILABLE, allow_quit);
|
|
return table;
|
|
}
|
|
|
|
struct compunit_symtab *find_compunit_symtab_by_address
|
|
(struct objfile *objfile, CORE_ADDR address) override;
|
|
|
|
bool has_unexpanded_symtabs (struct objfile *objfile) override
|
|
{
|
|
wait (objfile, true);
|
|
return dwarf2_base_index_functions::has_unexpanded_symtabs (objfile);
|
|
}
|
|
|
|
struct symtab *find_last_source_symtab (struct objfile *objfile) override
|
|
{
|
|
wait (objfile, true);
|
|
return dwarf2_base_index_functions::find_last_source_symtab (objfile);
|
|
}
|
|
|
|
void forget_cached_source_info (struct objfile *objfile) override
|
|
{
|
|
wait (objfile, true);
|
|
dwarf2_base_index_functions::forget_cached_source_info (objfile);
|
|
}
|
|
|
|
void print_stats (struct objfile *objfile, bool print_bcache) override
|
|
{
|
|
wait (objfile, true);
|
|
dwarf2_base_index_functions::print_stats (objfile, print_bcache);
|
|
}
|
|
|
|
void dump (struct objfile *objfile) override
|
|
{
|
|
cooked_index *index = wait (objfile, true);
|
|
gdb_printf ("Cooked index in use:\n");
|
|
gdb_printf ("\n");
|
|
index->dump (objfile->arch ());
|
|
}
|
|
|
|
void expand_all_symtabs (struct objfile *objfile) override
|
|
{
|
|
wait (objfile, true);
|
|
dwarf2_base_index_functions::expand_all_symtabs (objfile);
|
|
}
|
|
|
|
bool expand_symtabs_matching
|
|
(struct objfile *objfile,
|
|
gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
|
|
const lookup_name_info *lookup_name,
|
|
gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
|
|
gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
|
|
block_search_flags search_flags,
|
|
domain_search_flags domain,
|
|
gdb::function_view<expand_symtabs_lang_matcher_ftype> lang_matcher)
|
|
override;
|
|
|
|
struct compunit_symtab *find_pc_sect_compunit_symtab
|
|
(struct objfile *objfile, bound_minimal_symbol msymbol,
|
|
CORE_ADDR pc, struct obj_section *section, int warn_if_readin) override
|
|
{
|
|
wait (objfile, true);
|
|
return (dwarf2_base_index_functions::find_pc_sect_compunit_symtab
|
|
(objfile, msymbol, pc, section, warn_if_readin));
|
|
}
|
|
|
|
void map_symbol_filenames
|
|
(struct objfile *objfile,
|
|
gdb::function_view<symbol_filename_ftype> fun,
|
|
bool need_fullname) override
|
|
{
|
|
wait (objfile, true);
|
|
return (dwarf2_base_index_functions::map_symbol_filenames
|
|
(objfile, fun, need_fullname));
|
|
}
|
|
|
|
void compute_main_name (struct objfile *objfile) override
|
|
{
|
|
wait (objfile, false);
|
|
}
|
|
};
|
|
|
|
#endif /* GDB_DWARF2_COOKED_INDEX_H */
|