* gdb_bfd.c (struct gdb_bfd_data): New.
authorTom Tromey <tromey@redhat.com>
Wed, 18 Jul 2012 19:49:33 +0000 (19:49 +0000)
committerTom Tromey <tromey@redhat.com>
Wed, 18 Jul 2012 19:49:33 +0000 (19:49 +0000)
(gdb_bfd_cache): New global.
(struct gdb_bfd_cache_search): New.
(hash_bfd, eq_bfd, gdb_bfd_open): New functions.
(gdb_bfd_ref, gdb_bfd_unref): Use gdb_bfd_data.
* gdb_bfd.h (gdb_bfd_open): Declare.

gdb/ChangeLog
gdb/gdb_bfd.c
gdb/gdb_bfd.h

index 1ce1cf63dd4f86178570b92a2415a79a5b6edcbc..c5430d131e6b141e977c730f712de2a81e270c7d 100644 (file)
@@ -1,3 +1,12 @@
+2012-07-18  Tom Tromey  <tromey@redhat.com>
+
+       * gdb_bfd.c (struct gdb_bfd_data): New.
+       (gdb_bfd_cache): New global.
+       (struct gdb_bfd_cache_search): New.
+       (hash_bfd, eq_bfd, gdb_bfd_open): New functions.
+       (gdb_bfd_ref, gdb_bfd_unref): Use gdb_bfd_data.
+       * gdb_bfd.h (gdb_bfd_open): Declare.
+
 2012-07-18  Tom Tromey  <tromey@redhat.com>
 
        * utils.c (make_cleanup_bfd_unref): Rename from
index 8f40d74305d2f8a47bd8176295800b5c3b1b0537..5ad987887a841689b188a01dafa1d1226f21b43f 100644 (file)
@@ -1,6 +1,6 @@
 /* Definitions for BFD wrappers used by GDB.
 
-   Copyright (C) 2011
+   Copyright (C) 2011, 2012
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -22,6 +22,7 @@
 #include "gdb_bfd.h"
 #include "gdb_assert.h"
 #include "gdb_string.h"
+#include "hashtab.h"
 
 /* See gdb_bfd.h.  */
 
@@ -38,6 +39,116 @@ gdb_bfd_stash_filename (struct bfd *abfd)
   abfd->filename = data;
 }
 
+/* An object of this type is stored in each BFD's user data.  */
+
+struct gdb_bfd_data
+{
+  /* The reference count.  */
+  int refc;
+
+  /* The mtime of the BFD at the point the cache entry was made.  */
+  time_t mtime;
+};
+
+/* A hash table storing all the BFDs maintained in the cache.  */
+
+static htab_t gdb_bfd_cache;
+
+/* The type of an object being looked up in gdb_bfd_cache.  We use
+   htab's capability of storing one kind of object (BFD in this case)
+   and using a different sort of object for searching.  */
+
+struct gdb_bfd_cache_search
+{
+  /* The filename.  */
+  const char *filename;
+  /* The mtime.  */
+  time_t mtime;
+};
+
+/* A hash function for BFDs.  */
+
+static hashval_t
+hash_bfd (const void *b)
+{
+  const bfd *abfd = b;
+
+  /* It is simplest to just hash the filename.  */
+  return htab_hash_string (bfd_get_filename (abfd));
+}
+
+/* An equality function for BFDs.  Note that this expects the caller
+   to search using struct gdb_bfd_cache_search only, not BFDs.  */
+
+static int
+eq_bfd (const void *a, const void *b)
+{
+  const bfd *abfd = a;
+  const struct gdb_bfd_cache_search *s = b;
+  struct gdb_bfd_data *gdata = bfd_usrdata (abfd);
+
+  return (gdata->mtime == s->mtime
+         && strcmp (bfd_get_filename (abfd), s->filename) == 0);
+}
+
+/* See gdb_bfd.h.  */
+
+struct bfd *
+gdb_bfd_open (const char *name, const char *target, int fd)
+{
+  hashval_t hash;
+  void **slot;
+  bfd *abfd;
+  struct gdb_bfd_cache_search search;
+  struct stat st;
+
+  if (gdb_bfd_cache == NULL)
+    gdb_bfd_cache = htab_create_alloc (1, hash_bfd, eq_bfd, NULL,
+                                      xcalloc, xfree);
+
+  if (fd == -1)
+    {
+      fd = open (name, O_RDONLY | O_BINARY);
+      if (fd == -1)
+       {
+         bfd_set_error (bfd_error_system_call);
+         return NULL;
+       }
+    }
+
+  search.filename = name;
+  if (fstat (fd, &st) < 0)
+    {
+      /* Weird situation here.  */
+      search.mtime = 0;
+    }
+  else
+    search.mtime = st.st_mtime;
+
+  /* Note that this must compute the same result as hash_bfd.  */
+  hash = htab_hash_string (name);
+  /* Note that we cannot use htab_find_slot_with_hash here, because
+     opening the BFD may fail; and this would violate hashtab
+     invariants.  */
+  abfd = htab_find_with_hash (gdb_bfd_cache, &search, hash);
+  if (abfd != NULL)
+    {
+      close (fd);
+      return gdb_bfd_ref (abfd);
+    }
+
+  abfd = bfd_fopen (name, target, FOPEN_RB, fd);
+  if (abfd == NULL)
+    return NULL;
+
+  slot = htab_find_slot_with_hash (gdb_bfd_cache, &search, hash, INSERT);
+  gdb_assert (!*slot);
+  *slot = abfd;
+
+  gdb_bfd_stash_filename (abfd);
+  return gdb_bfd_ref (abfd);
+}
+
 /* Close ABFD, and warn if that fails.  */
 
 static int
@@ -60,22 +171,23 @@ gdb_bfd_close_or_warn (struct bfd *abfd)
 struct bfd *
 gdb_bfd_ref (struct bfd *abfd)
 {
-  int *p_refcount;
+  struct gdb_bfd_data *gdata;
 
   if (abfd == NULL)
     return NULL;
 
-  p_refcount = bfd_usrdata (abfd);
+  gdata = bfd_usrdata (abfd);
 
-  if (p_refcount != NULL)
+  if (gdata != NULL)
     {
-      *p_refcount += 1;
+      gdata->refc += 1;
       return abfd;
     }
 
-  p_refcount = xmalloc (sizeof (*p_refcount));
-  *p_refcount = 1;
-  bfd_usrdata (abfd) = p_refcount;
+  gdata = bfd_zalloc (abfd, sizeof (struct gdb_bfd_data));
+  gdata->refc = 1;
+  gdata->mtime = bfd_get_mtime (abfd);
+  bfd_usrdata (abfd) = gdata;
 
   return abfd;
 }
@@ -85,22 +197,35 @@ gdb_bfd_ref (struct bfd *abfd)
 void
 gdb_bfd_unref (struct bfd *abfd)
 {
-  int *p_refcount;
-  char *name;
+  struct gdb_bfd_data *gdata;
+  struct gdb_bfd_cache_search search;
 
   if (abfd == NULL)
     return;
 
-  p_refcount = bfd_usrdata (abfd);
-  gdb_assert (*p_refcount >= 1);
+  gdata = bfd_usrdata (abfd);
+  gdb_assert (gdata->refc >= 1);
 
-  *p_refcount -= 1;
-  if (*p_refcount > 0)
+  gdata->refc -= 1;
+  if (gdata->refc > 0)
     return;
 
-  xfree (p_refcount);
+  search.filename = bfd_get_filename (abfd);
+
+  if (gdb_bfd_cache && search.filename)
+    {
+      hashval_t hash = htab_hash_string (search.filename);
+      void **slot;
+
+      search.mtime = gdata->mtime;
+      slot = htab_find_slot_with_hash (gdb_bfd_cache, &search, hash,
+                                      NO_INSERT);
+
+      if (slot && *slot)
+       htab_clear_slot (gdb_bfd_cache, slot);
+    }
+
   bfd_usrdata (abfd) = NULL;  /* Paranoia.  */
 
-  name = bfd_get_filename (abfd);
   gdb_bfd_close_or_warn (abfd);
 }
index a1d5b030beb98f05300ba80cda6ffcbe4ca8c3f5..78d303b3a67118d3cb81c80e077cd22e2fd010e6 100644 (file)
 
 void gdb_bfd_stash_filename (struct bfd *abfd);
 
+/* Open a read-only (FOPEN_RB) BFD given arguments like bfd_fopen.
+   Returns NULL on error.  On success, returns a new reference to the
+   BFD, which must be freed with gdb_bfd_unref.  BFDs returned by this
+   call are shared among all callers opening the same file.  If FD is
+   not -1, then after this call it is owned by BFD.  */
+
+struct bfd *gdb_bfd_open (const char *name, const char *target, int fd);
+
 /* Acquire a new reference to ABFD.  Returns ABFD for convenience.
    It is fine for ABFD to be NULL; in this case the function does
    nothing and returns NULL.  */