From 6ec53d052b63163944019f5e17d73fafb9c57c2d Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 18 Jul 2012 19:49:33 +0000 Subject: [PATCH] * 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. --- gdb/ChangeLog | 9 +++ gdb/gdb_bfd.c | 157 +++++++++++++++++++++++++++++++++++++++++++++----- gdb/gdb_bfd.h | 8 +++ 3 files changed, 158 insertions(+), 16 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1ce1cf63dd4..c5430d131e6 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2012-07-18 Tom Tromey + + * 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 * utils.c (make_cleanup_bfd_unref): Rename from diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c index 8f40d74305d..5ad987887a8 100644 --- a/gdb/gdb_bfd.c +++ b/gdb/gdb_bfd.c @@ -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); } diff --git a/gdb/gdb_bfd.h b/gdb/gdb_bfd.h index a1d5b030beb..78d303b3a67 100644 --- a/gdb/gdb_bfd.h +++ b/gdb/gdb_bfd.h @@ -27,6 +27,14 @@ 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. */ -- 2.30.2