mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
This introduces a new file, gdbsupport/cxx-thread.h, which provides stubs for the C++ threading functionality on systems that don't support it. On fully-working ports, this header just supplies a number of aliases in the gdb namespace. So, for instance, gdb::mutex is just an alias for std::mutex. For non-working ports, compatibility stubs are provided for the subset of threading functionality that's used in gdb. These generally do nothing and assume single-threaded operation. The idea behind this is to reduce the number of checks of CXX_STD_THREAD, making the code cleaner. Not all spots using CXX_STD_THREAD could readily be converted. In particular: * Unit tests * --config output * Code manipulating threads themselves * The extension interrupting handling code These all seem fine to me. Note there's also a check in py-dap.c. This one is perhaps slightly subtle: DAP starts threads on the Python side, but it relies on gdb itself being thread-savvy, for instance in gdb.post_event. Approved-By: Simon Marchi <simon.marchi@efficios.com>
338 lines
11 KiB
C++
338 lines
11 KiB
C++
/* DWARF index storage
|
|
|
|
Copyright (C) 2022-2025 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_WORKER_H
|
|
#define GDB_DWARF2_COOKED_INDEX_WORKER_H
|
|
|
|
#include "dwarf2/abbrev-table-cache.h"
|
|
#include "dwarf2/cooked-index-entry.h"
|
|
#include "dwarf2/cooked-index-shard.h"
|
|
#include "dwarf2/types.h"
|
|
#include "dwarf2/read.h"
|
|
#include "maint.h"
|
|
#include "run-on-main-thread.h"
|
|
#include "gdbsupport/cxx-thread.h"
|
|
|
|
using cutu_reader_up = std::unique_ptr<cutu_reader>;
|
|
|
|
/* An instance of this is created when scanning DWARF to create a
|
|
cooked index. This class is the result of a single task to store
|
|
results while working -- that is, it is an implementation detail of
|
|
the threads managed by cooked_index_worker. Once scanning is done,
|
|
selected parts of the state here are stored into the shard, and
|
|
then these temporary objects are destroyed. */
|
|
|
|
class cooked_index_worker_result
|
|
{
|
|
public:
|
|
|
|
cooked_index_worker_result ();
|
|
DISABLE_COPY_AND_ASSIGN (cooked_index_worker_result);
|
|
|
|
cooked_index_worker_result (cooked_index_worker_result &&) = default;
|
|
cooked_index_worker_result &operator= (cooked_index_worker_result &&)
|
|
= default;
|
|
|
|
/* Return the current abbrev table_cache. */
|
|
const abbrev_table_cache &get_abbrev_table_cache () const
|
|
{ return m_abbrev_table_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 *per_cu);
|
|
|
|
/* Preserve READER by storing it in the local hash table. */
|
|
cutu_reader *preserve (cutu_reader_up 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 *per_cu)
|
|
{
|
|
return m_shard->add (die_offset, tag, flags, per_cu->lang (),
|
|
name, parent_entry, per_cu);
|
|
}
|
|
|
|
/* Overload that allows the language to be specified. */
|
|
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 *per_cu)
|
|
{
|
|
return m_shard->add (die_offset, tag, flags, lang,
|
|
name, parent_entry, per_cu);
|
|
}
|
|
|
|
/* Add a copy of NAME to the index. Return a pointer to the
|
|
copy. */
|
|
const char *add (std::string_view name)
|
|
{
|
|
return m_shard->add (name);
|
|
}
|
|
|
|
/* Install the current addrmap into the shard being constructed,
|
|
then transfer ownership of the index to the caller. */
|
|
cooked_index_shard_up release_shard ()
|
|
{
|
|
m_shard->install_addrmap (&m_addrmap);
|
|
/* This isn't needed any more. */
|
|
m_addrmap.clear ();
|
|
return std::move (m_shard);
|
|
}
|
|
|
|
/* Return the mutable addrmap that is currently being created. */
|
|
addrmap_mutable *get_addrmap ()
|
|
{
|
|
return &m_addrmap;
|
|
}
|
|
|
|
/* Set the mutable addrmap. */
|
|
void set_addrmap (addrmap_mutable new_map)
|
|
{
|
|
m_addrmap = std::move (new_map);
|
|
}
|
|
|
|
/* Return the parent_map that is currently being created. */
|
|
parent_map *get_parent_map ()
|
|
{
|
|
return &m_parent_map;
|
|
}
|
|
|
|
/* Catch exceptions from calling F (), and add them to the list of caught
|
|
exceptions. These are passed forward and printed by the main thread. */
|
|
template <typename F>
|
|
void
|
|
catch_error (F &&f)
|
|
{
|
|
try
|
|
{
|
|
f ();
|
|
}
|
|
catch (gdb_exception &ex)
|
|
{
|
|
m_exceptions.push_back (std::move (ex));
|
|
}
|
|
}
|
|
|
|
/* Called when the thread using this object is done with its work.
|
|
This stores any complaints for later emission, and it clears some
|
|
data that won't be needed again. */
|
|
void done_reading (complaint_collection &&complaints)
|
|
{
|
|
/* Hang on to the complaints. */
|
|
m_complaints = std::move (complaints);
|
|
/* Discard things that are no longer needed. */
|
|
m_reader_hash.clear ();
|
|
}
|
|
|
|
/* Called to emit any stored complaints or exceptions. This can
|
|
only be called on the main thread. */
|
|
void emit_complaints_and_exceptions
|
|
(gdb::unordered_set<gdb_exception> &seen_exceptions);
|
|
|
|
private:
|
|
/* The abbrev table cache used by this indexer. */
|
|
abbrev_table_cache m_abbrev_table_cache;
|
|
|
|
/* Hash function for a cutu_reader. */
|
|
struct cutu_reader_hash
|
|
{
|
|
using is_transparent = void;
|
|
|
|
std::uint64_t operator() (const cutu_reader_up &reader) const noexcept;
|
|
std::uint64_t operator() (const dwarf2_per_cu &per_cu) const noexcept;
|
|
};
|
|
|
|
/* Equality function for cutu_reader. */
|
|
struct cutu_reader_eq
|
|
{
|
|
using is_transparent = void;
|
|
|
|
bool operator() (const cutu_reader_up &a,
|
|
const cutu_reader_up &b) const noexcept;
|
|
|
|
bool operator() (const dwarf2_per_cu &per_cu,
|
|
const cutu_reader_up &reader) const noexcept;
|
|
};
|
|
|
|
/* A hash table of cutu_reader objects. */
|
|
gdb::unordered_set<cutu_reader_up, cutu_reader_hash, cutu_reader_eq>
|
|
m_reader_hash;
|
|
|
|
/* The index shard that is being constructed. */
|
|
cooked_index_shard_up m_shard;
|
|
|
|
/* 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 issued complaints. Only set after done_reading is
|
|
called. */
|
|
complaint_collection m_complaints;
|
|
|
|
/* Exceptions that we're storing to emit later. */
|
|
std::vector<gdb_exception> m_exceptions;
|
|
};
|
|
|
|
/* 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, .gdb_index, 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),
|
|
m_per_command_time (per_command_time)
|
|
{
|
|
/* Make sure we capture per_command_time from the main thread. */
|
|
gdb_assert (is_main_thread ());
|
|
}
|
|
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);
|
|
|
|
/* Release all shards from the results. */
|
|
std::vector<cooked_index_shard_up> release_shards ()
|
|
{
|
|
std::vector<cooked_index_shard_up> result;
|
|
for (auto &one_result : m_results)
|
|
result.push_back (one_result.release_shard ());
|
|
result.shrink_to_fit ();
|
|
return result;
|
|
}
|
|
|
|
/* Return the object holding all the parent maps. */
|
|
const parent_map_map *get_parent_map_map () const
|
|
{
|
|
return &m_all_parents_map;
|
|
}
|
|
|
|
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);
|
|
|
|
/* 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;
|
|
|
|
/* Helper function that should be called when done reading. This
|
|
assumes that m_results is filled in, and will initialize
|
|
m_all_parents_map and end by calling
|
|
cooked_index::set_contents. */
|
|
virtual void done_reading ();
|
|
|
|
/* A callback that can print stats, if needed. This is called when
|
|
transitioning to the 'MAIN_AVAILABLE' state. */
|
|
virtual void print_stats ()
|
|
{ }
|
|
|
|
/* The per-objfile object. */
|
|
dwarf2_per_objfile *m_per_objfile;
|
|
|
|
/* Result of each worker task. */
|
|
std::vector<cooked_index_worker_result> m_results;
|
|
|
|
/* Mutex to synchronize access to M_RESULTS when workers append their
|
|
result. */
|
|
gdb::mutex m_results_mutex;
|
|
|
|
/* Any warnings emitted. For the time being at least, this 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;
|
|
|
|
/* Current state of this object. */
|
|
cooked_state m_state = cooked_state::INITIAL;
|
|
/* Mutex and condition variable used to synchronize. */
|
|
gdb::mutex m_mutex;
|
|
gdb::condition_variable m_cond;
|
|
|
|
/* 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;
|
|
|
|
/* Captured value of per_command_time. */
|
|
bool m_per_command_time;
|
|
};
|
|
|
|
using cooked_index_worker_up = std::unique_ptr<cooked_index_worker>;
|
|
|
|
#endif /* GDB_DWARF2_COOKED_INDEX_WORKER_H */
|