@@ -66,6 +66,23 @@ language_requires_canonicalization (enum language lang)
|| 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
@@ -242,23 +259,17 @@ cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag,
implicit "main" discovery. */
if ((flags & IS_MAIN) != 0)
m_main = result;
+ else if (parent_entry == nullptr
+ && m_main == nullptr
+ && language_may_use_plain_main (per_cu->lang ())
+ && strcmp (name, "main") == 0)
+ m_main = result;
return result;
}
/* See cooked-index.h. */
-void
-cooked_index_shard::finalize ()
-{
- m_future = gdb::thread_pool::g_thread_pool->post_task ([this] ()
- {
- do_finalize ();
- });
-}
-
-/* See cooked-index.h. */
-
gdb::unique_xmalloc_ptr<char>
cooked_index_shard::handle_gnat_encoded_entry (cooked_index_entry *entry,
htab_t gnat_entries)
@@ -302,7 +313,7 @@ cooked_index_shard::handle_gnat_encoded_entry (cooked_index_entry *entry,
/* See cooked-index.h. */
void
-cooked_index_shard::do_finalize ()
+cooked_index_shard::finalize ()
{
auto hash_name_ptr = [] (const void *p)
{
@@ -426,70 +437,83 @@ cooked_index_shard::find (const std::string &name, bool completing) const
return range (lower, upper);
}
-/* See cooked-index.h. */
-
-void
-cooked_index_shard::wait (bool allow_quit) const
-{
- if (allow_quit)
- {
- std::chrono::milliseconds duration { 15 };
- while (m_future.wait_for (duration) == gdb::future_status::timeout)
- QUIT;
- }
- else
- m_future.wait ();
-}
-cooked_index::cooked_index (vec_type &&vec)
- : m_vector (std::move (vec))
+cooked_index::cooked_index (dwarf2_per_objfile *per_objfile)
+ : m_state (gdb::make_unique<cooked_index_worker> (per_objfile)),
+ m_per_bfd (per_objfile->per_bfd)
{
- for (auto &idx : m_vector)
- idx->finalize ();
-
/* 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);
}
-/* See cooked-index.h. */
+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::start_writing_index (dwarf2_per_bfd *per_bfd)
+cooked_index::set_contents (vec_type &&vec)
{
- index_cache_store_context ctx (global_index_cache, per_bfd);
+ gdb_assert (m_vector.empty ());
+ m_vector = std::move (vec);
- /* This must be set after all the finalization tasks have been
- started, because it may call 'wait'. */
- m_write_future
- = gdb::thread_pool::g_thread_pool->post_task ([this, per_bfd,
+ m_state->set (cooked_state::MAIN_AVAILABLE);
+
+ index_cache_store_context ctx (global_index_cache, m_per_bfd);
+
+ /* 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,
#if __cplusplus >= 201402L
- ctx = std::move (ctx)
+ ctx = std::move (ctx)
#else
- ctx
+ ctx
#endif
- ] ()
- {
- maybe_write_index (per_bfd, ctx);
- });
+ ] ()
+ {
+ m_state->set (cooked_state::FINALIZED);
+ maybe_write_index (m_per_bfd, ctx);
+ });
+
+ for (auto &idx : m_vector)
+ {
+ auto this_index = idx.get ();
+ finalizers.add_task ([=] () { this_index->finalize (); });
+ }
+
+ finalizers.start ();
}
cooked_index::~cooked_index ()
{
- /* The 'finalize' method may be run in a different thread. If
- this object is destroyed before this completes, then the method
- will end up writing to freed memory. Waiting for this to
- complete avoids this problem; and the cost seems ignorable
- because creating and immediately destroying the debug info is a
- relatively rare thing to do. */
- for (auto &item : m_vector)
- item->wait (false);
-
- /* Likewise for the index-creating future, though this one must also
+ /* 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_completely ();
+ wait (cooked_state::CACHE_DONE);
/* Remove our entry from the global list. See the assert in the
constructor to understand this. */
@@ -502,6 +526,8 @@ cooked_index::~cooked_index ()
dwarf2_per_cu_data *
cooked_index::lookup (CORE_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);
@@ -514,8 +540,10 @@ cooked_index::lookup (CORE_ADDR addr)
/* See cooked-index.h. */
std::vector<const addrmap *>
-cooked_index::get_addrmaps () const
+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);
@@ -525,9 +553,9 @@ cooked_index::get_addrmaps () const
/* See cooked-index.h. */
cooked_index::range
-cooked_index::find (const std::string &name, bool completing) const
+cooked_index::find (const std::string &name, bool completing)
{
- wait ();
+ wait (cooked_state::FINALIZED, true);
std::vector<cooked_index_shard::range> result_range;
result_range.reserve (m_vector.size ());
for (auto &entry : m_vector)
@@ -537,38 +565,65 @@ cooked_index::find (const std::string &name, bool completing) const
/* 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->per_cu->lang ();
+ return entry->full_name (obstack, true);
+}
+
+/* See cooked_index.h. */
+
const cooked_index_entry *
cooked_index::get_main () const
{
- const cooked_index_entry *result = nullptr;
-
+ 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. The choice among several is
- arbitrary. See the comment by the sole caller to understand
- the rationale for filtering by language. */
- if (entry != nullptr
- && !language_requires_canonicalization (entry->per_cu->lang ()))
+ /* 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)
{
- result = entry;
- break;
+ if ((entry->flags & IS_MAIN) != 0)
+ {
+ if (!language_requires_canonicalization (entry->per_cu->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 result;
+ return best_entry;
}
/* See cooked-index.h. */
void
-cooked_index::dump (gdbarch *arch) const
+cooked_index::dump (gdbarch *arch)
{
auto_obstack temp_storage;
- /* Ensure the index is done building. */
- this->wait ();
-
gdb_printf (" entries:\n");
gdb_printf ("\n");
@@ -641,11 +696,9 @@ void
cooked_index::maybe_write_index (dwarf2_per_bfd *per_bfd,
const index_cache_store_context &ctx)
{
- /* Wait for finalization. */
- wait ();
-
/* (maybe) store an index in the cache. */
- global_index_cache.store (per_bfd, ctx);
+ global_index_cache.store (m_per_bfd, ctx);
+ m_state->set (cooked_state::CACHE_DONE);
}
/* Wait for all the index cache entries to be written before gdb
@@ -32,9 +32,18 @@
#include "gdbsupport/iterator-range.h"
#include "gdbsupport/thread-pool.h"
#include "dwarf2/mapped-index.h"
+#include "dwarf2/read.h"
#include "dwarf2/tag.h"
#include "dwarf2/abbrev-cache.h"
#include "gdbsupport/range-chain.h"
+#include "gdbsupport/task-group.h"
+#include "complaints.h"
+#include "run-on-main-thread.h"
+
+#if CXX_STD_THREAD
+#include <mutex>
+#include <condition_variable>
+#endif /* CXX_STD_THREAD */
struct dwarf2_per_cu_data;
struct dwarf2_per_bfd;
@@ -64,7 +73,7 @@ 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. */
+ cooked_index_shard::finalize. */
extern bool language_requires_canonicalization (enum language lang);
@@ -270,14 +279,6 @@ class cooked_index_shard
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. */
@@ -334,8 +335,11 @@ class cooked_index_shard
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 ();
+ /* 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. This may be invoked in a worker
+ thread. */
+ void finalize ();
/* Storage for the entries. */
auto_obstack m_storage;
@@ -348,10 +352,6 @@ class cooked_index_shard
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;
};
class cutu_reader;
@@ -423,10 +423,158 @@ class cooked_index_storage
addrmap_mutable m_addrmap;
};
-/* 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. */
+/* 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. */
+
+class cooked_index_worker
+{
+public:
+
+ explicit cooked_index_worker (dwarf2_per_objfile *per_objfile);
+ 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);
+
+private:
+
+ /* Let cooked_index call the 'set' method. */
+ friend class cooked_index;
+ void set (cooked_state desired_state);
+
+ /* Start reading DWARF. This can be run in a worker thread without
+ problems. */
+ void start_reading ();
+
+ /* Helper function that does most of the work for start_reading. */
+ void do_reading ();
+
+ /* After the last DWARF-reading task has finished, this function
+ does the remaining work to finish the scan. */
+ void done_reading ();
+
+ /* An iterator for the comp units. */
+ typedef std::vector<dwarf2_per_cu_data_up>::iterator unit_iterator;
+
+ /* Process a batch of CUs. This may be called multiple times in
+ separate threads. TASK_NUMBER indicates which task this is --
+ the result is stored in that slot of M_RESULTS. */
+ void process_cus (size_t task_number, unit_iterator first,
+ unit_iterator end);
+
+ /* Each thread returns a tuple holding a cooked index, any collected
+ complaints, and a vector of errors that should be printed. The
+ latter is done 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>>;
+
+ /* The per-objfile object. */
+ dwarf2_per_objfile *m_per_objfile;
+ /* A storage object for "leftovers" -- see the 'start' method, but
+ essentially things not parsed during the normal CU parsing
+ passes. */
+ cooked_index_storage m_index_storage;
+ /* Result of each worker task. */
+ std::vector<result_type> m_results;
+
+#if CXX_STD_THREAD
+ /* Current state of this object. */
+ cooked_state m_state = cooked_state::INITIAL;
+ /* 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;
+ /* Mutex and condition variable used to synchronize. */
+ std::mutex m_mutex;
+ std::condition_variable m_cond;
+ /* If set, an exception occurred during start_reading; in this case
+ the scanning is stopped and this exception will later be reported
+ by the 'wait' method. */
+ gdb::optional<gdb_exception> m_failed;
+#endif /* CXX_STD_THREAD */
+};
+
+/* 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
{
@@ -436,17 +584,17 @@ class cooked_index : public dwarf_scanner_base
object. */
using vec_type = std::vector<std::unique_ptr<cooked_index_shard>>;
- explicit cooked_index (vec_type &&vec);
+ explicit cooked_index (dwarf2_per_objfile *per_objfile);
~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 ();
- }
+ /* 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. */
+ void set_contents (vec_type &&vec);
/* A range over a vector of subranges. */
using range = range_chain<cooked_index_shard::range>;
@@ -454,12 +602,12 @@ class cooked_index : public dwarf_scanner_base
/* 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;
+ range find (const std::string &name, bool completing);
/* Return a range of all the entries. */
- range all_entries () const
+ range all_entries ()
{
- wait ();
+ wait (cooked_state::FINALIZED, true);
std::vector<cooked_index_shard::range> result_range;
result_range.reserve (m_vector.size ());
for (auto &entry : m_vector)
@@ -474,12 +622,15 @@ class cooked_index : public dwarf_scanner_base
/* Return a new vector of all the addrmaps used by all the indexes
held by this object. */
- std::vector<const addrmap *> get_addrmaps () const;
+ 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
{
return this;
@@ -488,20 +639,20 @@ class cooked_index : public dwarf_scanner_base
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;
+ 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);
- /* 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);
+ { wait (cooked_state::CACHE_DONE); }
private:
@@ -513,8 +664,12 @@ class cooked_index : public dwarf_scanner_base
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;
+ /* 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;
};
#endif /* GDB_DWARF2_COOKED_INDEX_H */
@@ -94,8 +94,8 @@
#include "dwarf2/abbrev-cache.h"
#include "cooked-index.h"
#include "split-name.h"
-#include "gdbsupport/parallel-for.h"
#include "gdbsupport/thread-pool.h"
+#include "run-on-main-thread.h"
/* When == 1, print basic high level tracing messages.
When > 1, be more verbose.
@@ -758,8 +758,6 @@ static void dwarf2_find_base_address (struct die_info *die,
static void build_type_psymtabs_reader (cutu_reader *reader,
cooked_index_storage *storage);
-static void dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile);
-
static void var_decode_location (struct attribute *attr,
struct symbol *sym,
struct dwarf2_cu *cu);
@@ -3208,7 +3206,8 @@ get_gdb_index_contents_from_cache_dwz (objfile *obj, dwz_file *dwz)
return global_index_cache.lookup_gdb_index (build_id, &dwz->index_cache_res);
}
-static quick_symbol_functions_up make_cooked_index_funcs ();
+static quick_symbol_functions_up make_cooked_index_funcs
+ (dwarf2_per_objfile *);
/* See dwarf2/public.h. */
@@ -3287,31 +3286,11 @@ dwarf2_initialize_objfile (struct objfile *objfile)
}
global_index_cache.miss ();
- objfile->qf.push_front (make_cooked_index_funcs ());
+ objfile->qf.push_front (make_cooked_index_funcs (per_objfile));
}
-/* Build a partial symbol table. */
-
-static void
-dwarf2_build_psymtabs (struct objfile *objfile)
-{
- dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
-
- if (per_objfile->per_bfd->index_table != nullptr)
- return;
-
- try
- {
- dwarf2_build_psymtabs_hard (per_objfile);
- }
- catch (const gdb_exception_error &except)
- {
- exception_print (gdb_stderr, except);
- }
-}
-
/* Find the base address of the compilation unit for range lists and
location lists. It will normally be specified by DW_AT_low_pc.
In DWARF-3 draft 4, the base address could be overridden by
@@ -3945,7 +3924,7 @@ static struct dwo_unit *
lookup_dwo_unit (dwarf2_cu *cu, die_info *comp_unit_die, const char *dwo_name)
{
#if CXX_STD_THREAD
- /* We need a lock here both to handle the DWO hash table. */
+ /* We need a lock here to handle the DWO hash table. */
static std::mutex dwo_lock;
std::lock_guard<std::mutex> guard (dwo_lock);
@@ -4877,12 +4856,11 @@ process_skeletonless_type_units (dwarf2_per_objfile *per_objfile,
}
}
-/* Build the partial symbol table by doing a quick pass through the
- .debug_info and .debug_abbrev sections. */
-
-static void
-dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
+cooked_index_worker::cooked_index_worker (dwarf2_per_objfile *per_objfile)
+ : m_per_objfile (per_objfile)
{
+ gdb_assert (is_main_thread ());
+
struct objfile *objfile = per_objfile->objfile;
dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
@@ -4890,109 +4868,248 @@ dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
objfile_name (objfile));
per_bfd->map_info_sections (objfile);
+}
- cooked_index_storage index_storage;
- create_all_units (per_objfile);
- build_type_psymtabs (per_objfile, &index_storage);
+void
+cooked_index_worker::start ()
+{
+ gdb::thread_pool::g_thread_pool->post_task ([=] ()
+ {
+ this->start_reading ();
+ });
+}
+
+void
+cooked_index_worker::process_cus (size_t task_number, unit_iterator first,
+ unit_iterator end)
+{
+ SCOPE_EXIT { bfd_thread_cleanup (); };
+
+ /* Ensure that complaints are handled correctly. */
+ complaint_interceptor complaint_handler;
+
+ std::vector<gdb_exception> errors;
+ cooked_index_storage thread_storage;
+ for (auto inner = first; inner != end; ++inner)
+ {
+ dwarf2_per_cu_data *per_cu = inner->get ();
+ try
+ {
+ process_psymtab_comp_unit (per_cu, m_per_objfile, &thread_storage);
+ }
+ catch (gdb_exception &except)
+ {
+ errors.push_back (std::move (except));
+ }
+ }
+
+ m_results[task_number] = result_type (thread_storage.release (),
+ complaint_handler.release (),
+ std::move (errors));
+}
+
+void
+cooked_index_worker::done_reading ()
+{
+ /* Only handle the scanning results here. Complaints and exceptions
+ can only be dealt with on the main thread. */
std::vector<std::unique_ptr<cooked_index_shard>> indexes;
+ for (auto &one_result : m_results)
+ indexes.push_back (std::move (std::get<0> (one_result)));
+
+ /* This has to wait until we read the CUs, we need the list of DWOs. */
+ process_skeletonless_type_units (m_per_objfile, &m_index_storage);
+
+ indexes.push_back (m_index_storage.release ());
+ indexes.shrink_to_fit ();
+
+ dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
+ cooked_index *table
+ = (gdb::checked_static_cast<cooked_index *>
+ (per_bfd->index_table.get ()));
+ table->set_contents (std::move (indexes));
+}
+
+void
+cooked_index_worker::start_reading ()
+{
+ SCOPE_EXIT { bfd_thread_cleanup (); };
+
+ try
+ {
+ do_reading ();
+ }
+ catch (const gdb_exception &exc)
+ {
+ m_failed = exc;
+ set (cooked_state::CACHE_DONE);
+ }
+}
+
+void
+cooked_index_worker::do_reading ()
+{
+ dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
+
+ create_all_units (m_per_objfile);
+ build_type_psymtabs (m_per_objfile, &m_index_storage);
per_bfd->quick_file_names_table
= create_quick_file_names_table (per_bfd->all_units.size ());
if (!per_bfd->debug_aranges.empty ())
- read_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges,
- index_storage.get_addrmap ());
+ read_addrmap_from_aranges (m_per_objfile, &per_bfd->debug_aranges,
+ m_index_storage.get_addrmap ());
+
+ /* We want to balance the load between the worker threads. This is
+ done by using the size of each CU as a rough estimate of how
+ difficult it will be to operate on. This isn't ideal -- for
+ example if dwz is used, the early CUs will all tend to be
+ "included" and won't be parsed independently. However, this
+ heuristic works well for typical compiler output. */
+
+ size_t total_size = 0;
+ for (const auto &per_cu : per_bfd->all_units)
+ total_size += per_cu->length ();
+
+ /* How many worker threads we plan to use. We may not actually use
+ this many. We use 1 as the minimum to avoid division by zero,
+ and anyway in the N==0 case the work will be done
+ synchronously. */
+ const size_t n_worker_threads
+ = std::max (gdb::thread_pool::g_thread_pool->thread_count (), (size_t) 1);
+
+ /* How much effort should be put into each worker. */
+ const size_t size_per_thread = total_size / n_worker_threads;
+
+ /* Work is done in a task group. */
+ gdb::task_group workers ([this] ()
+ {
+ this->done_reading ();
+ });
+
+ auto end = per_bfd->all_units.end ();
+ size_t task_count = 0;
+ for (auto iter = per_bfd->all_units.begin (); iter != end; )
+ {
+ auto last = iter;
+ /* Put all remaining CUs into the last task. */
+ if (task_count == n_worker_threads - 1)
+ last = end;
+ else
+ {
+ size_t chunk_size = 0;
+ for (; last != end && chunk_size < size_per_thread; ++last)
+ chunk_size += (*last)->length ();
+ }
+
+ gdb_assert (iter != last);
+ workers.add_task ([=] ()
+ {
+ process_cus (task_count, iter, last);
+ });
+
+ ++task_count;
+ iter = last;
+ }
+
+ m_results.resize (task_count);
+ workers.start ();
+}
+bool
+cooked_index_worker::wait (cooked_state desired_state, bool allow_quit)
+{
+ bool done;
+#if CXX_STD_THREAD
{
- using iter_type = decltype (per_bfd->all_units.begin ());
+ std::unique_lock<std::mutex> lock (m_mutex);
- auto task_size_ = [] (iter_type iter)
- {
- dwarf2_per_cu_data *per_cu = iter->get ();
- return (size_t)per_cu->length ();
- };
- auto task_size = gdb::make_function_view (task_size_);
-
- /* Each thread returns a tuple holding a cooked index, any
- collected complaints, and a vector of errors that should be
- printed. The latter is done 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>>;
- std::vector<result_type> results
- = gdb::parallel_for_each (1, per_bfd->all_units.begin (),
- per_bfd->all_units.end (),
- [=] (iter_type iter, iter_type end)
- {
- /* Ensure that complaints are handled correctly. */
- complaint_interceptor complaint_handler;
+ /* 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);
- std::vector<gdb_exception> errors;
- cooked_index_storage thread_storage;
- for (; iter != end; ++iter)
+ while (desired_state > m_state)
+ {
+ if (allow_quit)
{
- dwarf2_per_cu_data *per_cu = iter->get ();
- try
- {
- process_psymtab_comp_unit (per_cu, per_objfile,
- &thread_storage);
- }
- catch (gdb_exception &except)
- {
- errors.push_back (std::move (except));
- }
+ std::chrono::milliseconds duration { 15 };
+ if (m_cond.wait_for (lock, duration) == std::cv_status::timeout)
+ QUIT;
}
- return result_type (thread_storage.release (),
- complaint_handler.release (),
- std::move (errors));
- }, task_size);
-
- /* Only show a given exception a single time. */
- std::unordered_set<gdb_exception> seen_exceptions;
- for (auto &one_result : results)
- {
- indexes.push_back (std::move (std::get<0> (one_result)));
- 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);
+ 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 = true;
+#endif /* CXX_STD_THREAD */
+
+ /* Only the main thread is allowed to report complaints and the
+ like. */
+ if (!is_main_thread ())
+ return false;
- /* This has to wait until we read the CUs, we need the list of DWOs. */
- process_skeletonless_type_units (per_objfile, &index_storage);
+ if (m_reported)
+ return done;
+ m_reported = true;
- if (dwarf_read_debug > 0)
- print_tu_stats (per_objfile);
+ if (m_failed.has_value ())
+ {
+ /* start_reading failed -- report it. */
+ exception_print (gdb_stderr, *m_failed);
+ m_failed.reset ();
+ return done;
+ }
- indexes.push_back (index_storage.release ());
- indexes.shrink_to_fit ();
+ /* 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);
+ }
- cooked_index *vec = new cooked_index (std::move (indexes));
- per_bfd->index_table.reset (vec);
+ if (dwarf_read_debug > 0)
+ print_tu_stats (m_per_objfile);
- /* Cannot start writing the index entry until after the
- 'index_table' member has been set. */
- vec->start_writing_index (per_bfd);
+ 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 ()));
- const cooked_index_entry *main_entry = vec->get_main ();
- if (main_entry != nullptr)
- {
- /* We only do this for names not requiring canonicalization. At
- this point in the process names have not 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. */
- enum language lang = main_entry->per_cu->lang ();
- gdb_assert (!language_requires_canonicalization (lang));
- const char *full_name = main_entry->full_name (&per_bfd->obstack, true);
- set_objfile_main_name (objfile, full_name, lang);
- }
+ 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;
+}
+
+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 */
}
static void
@@ -16476,26 +16593,60 @@ cooked_indexer::make_index (cutu_reader *reader)
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;
+ }
+
dwarf2_per_cu_data *find_per_cu (dwarf2_per_bfd *per_bfd,
CORE_ADDR adjusted_pc) override;
struct compunit_symtab *find_compunit_symtab_by_address
(struct objfile *objfile, CORE_ADDR address) override;
- void dump (struct objfile *objfile) override
+ bool has_unexpanded_symtabs (struct objfile *objfile) override
{
- dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
- cooked_index *index
- = (gdb::checked_static_cast<cooked_index *>
- (per_objfile->per_bfd->index_table.get ()));
- if (index == nullptr)
- return;
+ 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);
+ }
+
void expand_matching_symbols
(struct objfile *,
const lookup_name_info &lookup_name,
@@ -16513,55 +16664,28 @@ struct cooked_index_functions : public dwarf2_base_index_functions
domain_enum domain,
enum search_domain kind) override;
- bool can_lazily_read_symbols () override
+ struct compunit_symtab *find_pc_sect_compunit_symtab
+ (struct objfile *objfile, struct bound_minimal_symbol msymbol,
+ CORE_ADDR pc, struct obj_section *section, int warn_if_readin) override
{
- return true;
+ wait (objfile, true);
+ return (dwarf2_base_index_functions::find_pc_sect_compunit_symtab
+ (objfile, msymbol, pc, section, warn_if_readin));
}
- void read_partial_symbols (struct objfile *objfile) override
+ void map_symbol_filenames
+ (struct objfile *objfile,
+ gdb::function_view<symbol_filename_ftype> fun,
+ bool need_fullname) override
{
- if (dwarf2_has_info (objfile, nullptr))
- dwarf2_build_psymtabs (objfile);
+ wait (objfile, true);
+ return (dwarf2_base_index_functions::map_symbol_filenames
+ (objfile, fun, need_fullname));
}
- enum language lookup_global_symbol_language (struct objfile *objfile,
- const char *name,
- domain_enum domain,
- bool *symbol_found_p) override
+ void compute_main_name (struct objfile *objfile) override
{
- *symbol_found_p = false;
-
- if (!(domain == VAR_DOMAIN && streq (name, "main")))
- return language_unknown;
-
- dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
- struct dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
- if (per_bfd->index_table == nullptr)
- return language_unknown;
-
- /* Expansion of large CUs can be slow. By returning the language of main
- here for C and C++, we avoid CU expansion during set_initial_language.
- But by doing a symbol lookup in the cooked index, we are forced to wait
- for finalization to complete. See PR symtab/30174 for ideas how to
- bypass that as well. */
- cooked_index *table
- = (gdb::checked_static_cast<cooked_index *>
- (per_bfd->index_table.get ()));
-
- for (const cooked_index_entry *entry : table->find (name, false))
- {
- if (entry->tag != DW_TAG_subprogram)
- continue;
-
- enum language lang = entry->per_cu->lang ();
- if (!(lang == language_c || lang == language_cplus))
- continue;
-
- *symbol_found_p = true;
- return lang;
- }
-
- return language_unknown;
+ wait (objfile, false);
}
};
@@ -16572,8 +16696,6 @@ cooked_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd,
cooked_index *table
= (gdb::checked_static_cast<cooked_index *>
(per_bfd->index_table.get ()));
- if (table == nullptr)
- return nullptr;
return table->lookup (adjusted_pc);
}
@@ -16585,11 +16707,7 @@ cooked_index_functions::find_compunit_symtab_by_address
return nullptr;
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 ()));
- if (table == nullptr)
- return nullptr;
+ cooked_index *table = wait (objfile, true);
CORE_ADDR baseaddr = objfile->data_section_offset ();
dwarf2_per_cu_data *per_cu = table->lookup (address - baseaddr);
@@ -16608,11 +16726,7 @@ cooked_index_functions::expand_matching_symbols
symbol_compare_ftype *ordered_compare)
{
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 ()));
- if (table == nullptr)
- return;
+ cooked_index *table = wait (objfile, true);
const block_search_flags search_flags = (global
? SEARCH_GLOBAL_BLOCK
@@ -16650,13 +16764,7 @@ cooked_index_functions::expand_symtabs_matching
{
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 ()));
- if (table == nullptr)
- return true;
-
- table->wait ();
+ cooked_index *table = wait (objfile, true);
dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
@@ -16776,15 +16884,27 @@ cooked_index_functions::expand_symtabs_matching
/* Return a new cooked_index_functions object. */
static quick_symbol_functions_up
-make_cooked_index_funcs ()
+make_cooked_index_funcs (dwarf2_per_objfile *per_objfile)
{
+ /* Set the index table early so that sharing works even while
+ scanning; and then start the scanning. */
+ dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
+ cooked_index *idx = new cooked_index (per_objfile);
+ per_bfd->index_table.reset (idx);
+ /* Don't start reading until after 'index_table' is set. This
+ avoids races. */
+ idx->start_reading ();
+
+ if (dwarf_synchronous)
+ idx->wait_completely ();
+
return quick_symbol_functions_up (new cooked_index_functions);
}
quick_symbol_functions_up
cooked_index::make_quick_functions () const
{
- return make_cooked_index_funcs ();
+ return quick_symbol_functions_up (new cooked_index_functions);
}
@@ -881,7 +881,7 @@ struct dwarf2_base_index_functions : public quick_symbol_functions
struct compunit_symtab *find_pc_sect_compunit_symtab
(struct objfile *objfile, struct bound_minimal_symbol msymbol,
CORE_ADDR pc, struct obj_section *section, int warn_if_readin)
- override final;
+ override;
struct compunit_symtab *find_compunit_symtab_by_address
(struct objfile *objfile, CORE_ADDR address) override
@@ -31,6 +31,7 @@ if {[build_executable $testfile.exp $testfile $srcfile {nodebug quiet}]} {
clean_restart
gdb_test_no_output "set breakpoint pending off"
+gdb_test_no_output "maint set dwarf synchronous on"
set host_binfile [gdb_remote_download host $binfile]
@@ -49,6 +49,8 @@ set host_binfile [gdb_remote_download host $binfile]
# Restart with no executable.
clean_restart
+gdb_test_no_output "maint set dwarf synchronous on"
+
# This pattern is hit when GDB does not use -readnow (i.e. the default
# behaviour).
set pattern1 \
@@ -25,6 +25,8 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" object {}] != ""
clean_restart
+gdb_test_no_output "maint set dwarf synchronous on"
+
set host_binfile [gdb_remote_download host $binfile]
gdb_test_no_output "set complaints 100"
set w1 0
@@ -128,6 +128,8 @@ if {[run_on_host "objcopy" [gdb_find_objcopy] "$args"]} {
# executable we're going to get an error, which we check for below.
clean_restart
+gdb_test_no_output "maint set dwarf synchronous on"
+
set line1 "Reading symbols from \[^\r\n\]+"
set dwarf_error "Dwarf Error: DW_FORM_strp used without required section"
@@ -124,7 +124,7 @@ foreach_with_prefix ranges_sect {ranges rnglists} {
with_complaints 1 {
set test "Zero address complaint - relocated - psymtab"
set have_complaint 0
- gdb_test_multiple "sharedlibrary [file tail $lib1]" $test {
+ gdb_test_multiple "maint with dwarf synchronous on -- sharedlibrary [file tail $lib1]" $test {
-re -wrap $re {
set have_complaint 1
}
@@ -144,12 +144,14 @@ foreach_with_prefix ranges_sect {ranges rnglists} {
clean_restart
# Test for presence of complaint, with lib1 unrelocated.
+ gdb_test_no_output "maint set dwarf synchronous on"
with_complaints 1 {
gdb_load $lib1
set test "Zero address complaint - unrelocated - psymtab"
set have_complaint [regexp $re.* $gdb_file_cmd_msg]
gdb_assert { $have_complaint } $test
}
+ gdb_test_no_output "maint set dwarf synchronous off"
if { ! $readnow_p } {
with_complaints 1 {
@@ -60,7 +60,10 @@ pass "$testfile - unload"
# Test-case for PR24620: Delete the .dwo file and verify that
# save gdb-index doesn't crash.
remote_file target delete $dwo
-clean_restart $binfile
+save_vars { GDBFLAGS } {
+ append GDBFLAGS " -iex \"maint set dwarf synchronous on\""
+ clean_restart $binfile
+}
set output_dir [standard_output_file ""]
set cmd "save gdb-index"
gdb_test_multiple "$cmd $output_dir" $cmd {
@@ -36,6 +36,7 @@ if { [build_executable $testfile.exp $testfile [list $srcfile $asm_file]] } {
}
clean_restart
+gdb_test_no_output "maint set dwarf synchronous on"
set msg "\r\ncould not find '\.gnu_debugaltlink' file for \[^\r\n\]*"
gdb_test "file $binfile" "$msg" "file command"
@@ -119,9 +119,13 @@ Dwarf::assemble $asm_file {
}
}
-if { [prepare_for_testing "failed to prepare" ${testfile} \
- [list $srcfile $asm_file] {nodebug}] } {
- return -1
+save_vars { GDBFLAGS } {
+ append GDBFLAGS " -iex \"maint set dwarf synchronous on\""
+
+ if { [prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+ }
}
set re "Dwarf Error: .debug_types section not supported in dwz file"