[08/15] Do more DWARF reading in the background

Message ID 20231029173839.471514-9-tom@tromey.com
State New
Headers
Series Index DWARF in the background |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_gdb_build--master-arm fail Testing failed

Commit Message

Tom Tromey Oct. 29, 2023, 5:35 p.m. UTC
  This patch rearranges the DWARF reader so that more work is done in
the background.  This is PR symtab/29942.

The idea here is that there is only a small amount of work that must
be done on the main thread when scanning DWARF -- before the main
scan, the only part is mapping the section data.

Currently, the DWARF reader uses the quick_symbol_functions "lazy"
functionality to defer even starting to read.  This patch instead
changes the reader to start reading immediately, but doing more in
worker tasks.

Before this patch, "file" on my machine:

    (gdb) file /tmp/gdb
    2023-10-23 12:29:56.885 - command started
    Reading symbols from /tmp/gdb...
    2023-10-23 12:29:58.047 - command finished
    Command execution time: 5.867228 (cpu), 1.162444 (wall)

After the patch, more work is done in the background and so this takes
a bit less time:

    (gdb) file /tmp/gdb
    2023-10-23 13:25:51.391 - command started
    Reading symbols from /tmp/gdb...
    2023-10-23 13:25:51.712 - command finished
    Command execution time: 1.894500 (cpu), 0.320306 (wall)

I think this could be further sped up by using the shared library load
map to avoid objfile loops like the one in expand_symtab_containing_pc
-- it seems like the correct objfile could be chosen more directly.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29942
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30174
---
 gdb/dwarf2/cooked-index.c                     | 205 +++++---
 gdb/dwarf2/cooked-index.h                     | 245 +++++++--
 gdb/dwarf2/read.c                             | 482 +++++++++++-------
 gdb/dwarf2/read.h                             |   2 +-
 gdb/testsuite/gdb.dwarf2/dw2-error.exp        |   1 +
 .../gdb.dwarf2/dw2-missing-cu-tag.exp         |   2 +
 .../gdb.dwarf2/dw2-stack-boundary.exp         |   2 +
 .../gdb.dwarf2/dw2-using-debug-str.exp        |   2 +
 gdb/testsuite/gdb.dwarf2/dw2-zero-range.exp   |   4 +-
 gdb/testsuite/gdb.dwarf2/fission-reread.exp   |   5 +-
 gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp |   1 +
 .../gdb.dwarf2/struct-with-sig-2.exp          |  10 +-
 12 files changed, 653 insertions(+), 308 deletions(-)
  

Patch

diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index a474691cfb4..078d8d8c140 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -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
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 14352c1e139..df27f1214b8 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -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 */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 8ee6c6d4762..3dbe65432d2 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -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);
 }
 
 
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index da907729320..c679e47a91f 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -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
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-error.exp b/gdb/testsuite/gdb.dwarf2/dw2-error.exp
index 76886d5c1b6..f8dc08d47aa 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-error.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-error.exp
@@ -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]
 
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-missing-cu-tag.exp b/gdb/testsuite/gdb.dwarf2/dw2-missing-cu-tag.exp
index f57e8086a7c..f24c7938568 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-missing-cu-tag.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-missing-cu-tag.exp
@@ -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 \
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-stack-boundary.exp b/gdb/testsuite/gdb.dwarf2/dw2-stack-boundary.exp
index a72564c075c..65b91b3a504 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-stack-boundary.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-stack-boundary.exp
@@ -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
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp b/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp
index 7974cb7f20b..ec5d3183f60 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-using-debug-str.exp
@@ -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"
 
diff --git a/gdb/testsuite/gdb.dwarf2/dw2-zero-range.exp b/gdb/testsuite/gdb.dwarf2/dw2-zero-range.exp
index 7bef05684f3..38c85a47b80 100644
--- a/gdb/testsuite/gdb.dwarf2/dw2-zero-range.exp
+++ b/gdb/testsuite/gdb.dwarf2/dw2-zero-range.exp
@@ -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 {
diff --git a/gdb/testsuite/gdb.dwarf2/fission-reread.exp b/gdb/testsuite/gdb.dwarf2/fission-reread.exp
index 01e9eada575..884a8359fa5 100644
--- a/gdb/testsuite/gdb.dwarf2/fission-reread.exp
+++ b/gdb/testsuite/gdb.dwarf2/fission-reread.exp
@@ -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 {
diff --git a/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp b/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
index 0c4e808e553..c300e9b0637 100644
--- a/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
+++ b/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
@@ -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"
diff --git a/gdb/testsuite/gdb.dwarf2/struct-with-sig-2.exp b/gdb/testsuite/gdb.dwarf2/struct-with-sig-2.exp
index f6877fc6a17..8bf200e8902 100644
--- a/gdb/testsuite/gdb.dwarf2/struct-with-sig-2.exp
+++ b/gdb/testsuite/gdb.dwarf2/struct-with-sig-2.exp
@@ -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"