public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Tom Tromey <tromey@adacore.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tromey@adacore.com>
Subject: [PATCH 2/2] Write the DWARF index in the background
Date: Wed, 13 Apr 2022 13:45:09 -0600	[thread overview]
Message-ID: <20220413194509.2241144-3-tromey@adacore.com> (raw)
In-Reply-To: <20220413194509.2241144-1-tromey@adacore.com>

The new DWARF cooked indexer interacts poorly with the DWARF index
cache.  In particular, the cache will require gdb to wait for the
cooked index to be finalized.  As this happens in the foreground, it
means that users with this setting enabled will see a slowdown.

This patch changes gdb to write the cache entry a worker thread.  (As
usual, in the absence of threads, this work is simply done immediately
in the main thread.)

Some care is taken to ensure that this can't crash, and that gdb will
not exit before the task is complete.

To avoid use-after-free problems, the DWARF per-BFD object explicitly
waits for the index cache task to complete.

To avoid gdb exiting early, an exit observer is used to wait for all
such pending tasks.

In normal use, neither of these waits will be very visible.  For users
using "-batch" to pre-generate the index, though, it would be.
However I don't think there is much to be done about this, as it was
the status quo ante.
---
 gdb/dwarf2/cooked-index.c | 71 +++++++++++++++++++++++++++++++++++++--
 gdb/dwarf2/cooked-index.h | 30 +++++++++++------
 gdb/dwarf2/read.c         | 10 +++---
 3 files changed, 94 insertions(+), 17 deletions(-)

diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index b66ef5a1c64..7084ad18c20 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -20,10 +20,19 @@
 #include "defs.h"
 #include "dwarf2/cooked-index.h"
 #include "dwarf2/read.h"
+#include "dwarf2/index-cache.h"
 #include "cp-support.h"
 #include "ada-lang.h"
 #include "split-name.h"
+#include "observable.h"
+#include "run-on-main-thread.h"
 #include <algorithm>
+#include <unordered_set>
+
+/* We don't want gdb to exit while it is in the process of writing to
+   the index cache.  So, all live cooked index vectors are stored
+   here, and then these are all waited for before exit proceeds.  */
+static std::unordered_set<cooked_index_vector *> active_vectors;
 
 /* Hash function for cooked_index_entry.  */
 
@@ -113,14 +122,45 @@ cooked_index::add (sect_offset die_offset, enum dwarf_tag tag,
   return result;
 }
 
-cooked_index_vector::cooked_index_vector (vec_type &&vec)
+cooked_index_vector::cooked_index_vector (vec_type &&vec,
+					  dwarf2_per_bfd *per_bfd)
   : m_vector (std::move (vec)),
     m_future (gdb::thread_pool::g_thread_pool->post_task
 	      ([this] ()
 	      {
 		finalize ();
-	      }))
+	      })),
+    m_write_future (gdb::thread_pool::g_thread_pool->post_task
+		    ([this, per_bfd] ()
+		    {
+		      maybe_write_index (per_bfd);
+		    }))
 {
+  /* 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);
+}
+
+cooked_index_vector::~cooked_index_vector ()
+{
+  /* 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.  */
+  m_future.wait ();
+
+  /* Likewise for the index-creating future, though this one must also
+     waited for by the per-BFD object to ensure the required data
+     remains live.  */
+  m_write_future.wait ();
+
+  /* Remove our entry from the global list.  See the assert in the
+     constructor to understand this.  */
+  gdb_assert (is_main_thread ());
+  active_vectors.erase (this);
 }
 
 /* See cooked-index.h.  */
@@ -340,3 +380,30 @@ cooked_index_vector::finalize ()
 	       return *a < *b;
 	     });
 }
+
+void
+cooked_index_vector::maybe_write_index (dwarf2_per_bfd *per_bfd)
+{
+  /* Wait for finalization.  */
+  m_future.wait ();
+
+  /* (maybe) store an index in the cache.  */
+  global_index_cache.store (per_bfd);
+}
+
+/* Wait for all the index cache entries to be written before gdb
+   exits.  */
+static void
+wait_for_index_cache (int)
+{
+  gdb_assert (is_main_thread ());
+  for (cooked_index_vector *item : active_vectors)
+    item->wait ();
+}
+
+void _initialize_cooked_index ();
+void
+_initialize_cooked_index ()
+{
+  gdb::observers::gdb_exiting.attach (wait_for_index_cache, "cooked-index");
+}
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 661664d9f84..940f0a79b20 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -33,6 +33,7 @@
 #include "gdbsupport/thread-pool.h"
 
 struct dwarf2_per_cu_data;
+struct dwarf2_per_bfd;
 
 /* Flags that describe an entry in the index.  */
 enum cooked_index_flag_enum : unsigned char
@@ -251,19 +252,10 @@ class cooked_index_vector
      object.  */
   typedef std::vector<std::unique_ptr<cooked_index>> vec_type;
 
-  explicit cooked_index_vector (vec_type &&vec);
+  explicit cooked_index_vector (vec_type &&vec, dwarf2_per_bfd *per_bfd);
   DISABLE_COPY_AND_ASSIGN (cooked_index_vector);
 
-  ~cooked_index_vector ()
-  {
-    /* 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.  */
-    m_future.wait ();
-  }
+  ~cooked_index_vector ();
 
   /* A simple range over part of m_entries.  */
   typedef iterator_range<std::vector<cooked_index_entry *>::iterator> range;
@@ -293,6 +285,16 @@ class cooked_index_vector
      "main".  This will return NULL if no such entry is available.  */
   const cooked_index_entry *get_main () const;
 
+  /* Wait for the index to be completely finished.  For ordinary uses,
+     the index code ensures this itself -- e.g., 'all_entries' will
+     wait on the 'finalize' future.  However, on destruction, if an
+     index is being written, it's also necessary to wait for that to
+     complete.  */
+  void wait ()
+  {
+    m_write_future.wait ();
+  }
+
 private:
 
   /* GNAT only emits mangled ("encoded") names in the DWARF, and does
@@ -308,6 +310,9 @@ class cooked_index_vector
      into the internal hash table.  */
   void finalize ();
 
+  /* Maybe write the index to the index cache.  */
+  void maybe_write_index (dwarf2_per_bfd *per_bfd);
+
   /* The vector of cooked_index objects.  This is stored because the
      entries are stored on the obstacks in those objects.  */
   vec_type m_vector;
@@ -322,6 +327,9 @@ class cooked_index_vector
      that the 'get' method is never called on this future, only
      'wait'.  */
   std::future<void> m_future;
+
+  /* A future that tracks when the 'index_write' method is done.  */
+  std::future<void> m_write_future;
 };
 
 #endif /* GDB_DWARF2_COOKED_INDEX_H */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 954ef11b19e..5f98cb31c84 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1523,6 +1523,11 @@ dwarf2_per_bfd::dwarf2_per_bfd (bfd *obfd, const dwarf2_debug_sections *names,
 
 dwarf2_per_bfd::~dwarf2_per_bfd ()
 {
+  /* Data from the per-BFD may be needed when finalizing the cooked
+     index table, so wait here while this happens.  */
+  if (cooked_index_table != nullptr)
+    cooked_index_table->wait ();
+
   for (auto &per_cu : all_comp_units)
     {
       per_cu->imported_symtabs_free ();
@@ -5417,9 +5422,6 @@ dwarf2_build_psymtabs (struct objfile *objfile, bool already_attached)
   try
     {
       dwarf2_build_psymtabs_hard (per_objfile);
-
-      /* (maybe) store an index in the cache.  */
-      global_index_cache.store (per_objfile->per_bfd);
     }
   catch (const gdb_exception_error &except)
     {
@@ -7189,7 +7191,7 @@ dwarf2_build_psymtabs_hard (dwarf2_per_objfile *per_objfile)
      indexes.end ());
   indexes.shrink_to_fit ();
   per_bfd->cooked_index_table.reset
-    (new cooked_index_vector (std::move (indexes)));
+    (new cooked_index_vector (std::move (indexes), per_bfd));
 
   const cooked_index_entry *main_entry
     = per_bfd->cooked_index_table->get_main ();
-- 
2.34.1


      parent reply	other threads:[~2022-04-13 19:45 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-04-13 19:45 [PATCH 0/2] Write DWARF index cache files in background Tom Tromey
2022-04-13 19:45 ` [PATCH 1/2] Only use the per-BFD object to write a DWARF index Tom Tromey
2022-04-13 19:45 ` Tom Tromey [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220413194509.2241144-3-tromey@adacore.com \
    --to=tromey@adacore.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).