The Dwfl_Process_Tracker includes a dynamicsizehash cache which maps
file paths to Elf * (or rather, dwfl_tracker_elf_info * storing fd and
Elf *). We provide a dwfl_process_tracker_find_elf callback which
checks the cache for an already-loaded Elf * and, if missing,
populates the cache with the fd returned by dwfl_linux_proc_find_elf.
Later, open_elf updates the cache with the Elf * for that fd. The
commented asserts still catch some cases where a redundant Elf * is
being created without checking the cache.
Since the Elf * outlasts the Dwfl that created it, we use the
(convenient, already-existing) reference count field in Elf * to
retain the data in the table. Then dwfl_end calling elf_end will
decrement the refcount cleanly, and dwfl_process_tracker_end will
issue another elf_end call.
* libdwfl/libdwfl.h (dwfl_process_tracker_find_elf): New function,
serves as a cached version of the dwfl_linux_proc_find_elf callback.
* libdwfl/libdwflP.h (dwfltracker_elf_info): New struct typedef.
(struct Dwfl_Process_Tracker): Add dynamicsizehash table of
dwfltracker_elf_info structs.
* libdwfl/dwfl_process_tracker_elftab.c: New file, instantiates
lib/dynamicsizehash_concurrent.c to store dwfltracker_elf_info
structs.
* libdwfl/dwfl_process_tracker_elftab.h: New file, ditto.
* libdwfl/libdwfl_next_prime.c: New file.
* libdwfl/dwfl_process_tracker.c (dwfl_process_tracker_begin): Init elftab.
(dwfl_process_tracker_end): Clean up elftab. Lock and iterate the
hash to free tracker->elftab.table items.
* libdwfl/dwfl_process_tracker_find_elf.c: New file.
* libdwfl/dwfl_module_getdwarf.c (open_elf): Cache file->elf in
Dwfl_Process_Tracker. Must be done here as dwfl_linux_proc_find_elf
opens an fd but does not yet create the Elf *. Also, increment
Elf * refcount so the table retains the Elf * after caller's
dwfl_end cleanup.
* libdwfl/Makefile.am (libdwfl_a_SOURCES): Add
dwfl_process_tracker_find_elf.c, dwfl_process_tracker_elftab.c,
libdwfl_next_prime.c.
* libdw/libdw.map: Add dwfl_process_tracker_find_elf.
---
libdw/libdw.map | 1 +
libdwfl/Makefile.am | 5 +-
libdwfl/dwfl_module_getdwarf.c | 23 ++++-
libdwfl/dwfl_process_tracker.c | 23 +++++
libdwfl/dwfl_process_tracker_elftab.c | 46 ++++++++++
libdwfl/dwfl_process_tracker_elftab.h | 40 +++++++++
libdwfl/dwfl_process_tracker_find_elf.c | 111 ++++++++++++++++++++++++
libdwfl/libdwfl.h | 7 ++
libdwfl/libdwflP.h | 17 +++-
libdwfl/libdwfl_next_prime.c | 6 ++
10 files changed, 275 insertions(+), 4 deletions(-)
create mode 100644 libdwfl/dwfl_process_tracker_elftab.c
create mode 100644 libdwfl/dwfl_process_tracker_elftab.h
create mode 100644 libdwfl/dwfl_process_tracker_find_elf.c
create mode 100644 libdwfl/libdwfl_next_prime.c
diff --git a/libdw/libdw.map b/libdw/libdw.map
index ad3ec590..1a2a879f 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -393,4 +393,5 @@ ELFUTILS_0.193 {
dwfl_process_tracker_begin;
dwfl_begin_with_tracker;
dwfl_process_tracker_end;
+ dwfl_process_tracker_find_elf;
} ELFUTILS_0.192;
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index b41122e3..f05f12af 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -71,8 +71,9 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c
dwfl_version.c \
link_map.c core-file.c open.c image-header.c \
dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \
linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c \
- dwfl_process_tracker.c \
- dwfl_perf_frame.c \
+ dwfl_process_tracker.c dwfl_process_tracker_find_elf.c \
+ dwfl_process_tracker_elftab.c libdwfl_next_prime.c \
+ dwfl_perf_frame.c \
gzip.c debuginfod-client.c
if BZLIB
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
index 6f98c02b..ec8f76be 100644
--- a/libdwfl/dwfl_module_getdwarf.c
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -1,5 +1,5 @@
/* Find debugging and symbol information for a module in libdwfl.
- Copyright (C) 2005-2012, 2014, 2015 Red Hat, Inc.
+ Copyright (C) 2005-2012, 2014, 2015, 2025 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -79,6 +79,27 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file)
if (error != DWFL_E_NOERROR)
return error;
+ /* Cache file->elf in Dwfl_Process_Tracker if available: */
+ if (mod->dwfl->tracker != NULL && file->name != NULL)
+ {
+ dwfltracker_elf_info *ent = dwfltracker_elftab_find
(&mod->dwfl->tracker->elftab, elf_hash(file->name));
+ if (ent != NULL)
+ {
+ /* TODO(REVIEW): The following assertions are still
+ triggered on certain code paths that acquire fds or
+ create Elf structs without checking the caching mechanism
+ first. This is not a serious problem, and can be fixed
+ gradually. */
+
+ /* assert(ent->elf == NULL || ent->elf == file->elf); */ /* Guard
against redundant/leaked Elf *. */
+ /* assert(ent->fd == file->fd); */ /* Guard against redundant open. */
+
+ ent->elf = file->elf;
+ /* XXX Dwfl_Process_Tracker also holds the Elf * jointly with the
caller: */
+ ent->elf->ref_count++;
+ }
+ }
+
GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem);
if (ehdr == NULL)
{
diff --git a/libdwfl/dwfl_process_tracker.c b/libdwfl/dwfl_process_tracker.c
index c42d8ad2..3a414fc0 100644
--- a/libdwfl/dwfl_process_tracker.c
+++ b/libdwfl/dwfl_process_tracker.c
@@ -32,6 +32,8 @@
#include "libdwflP.h"
+#define HTAB_DEFAULT_SIZE 1021
+
Dwfl_Process_Tracker *dwfl_process_tracker_begin (const Dwfl_Callbacks
*callbacks)
{
Dwfl_Process_Tracker *tracker = calloc (1, sizeof *tracker);
@@ -41,6 +43,8 @@ Dwfl_Process_Tracker *dwfl_process_tracker_begin (const
Dwfl_Callbacks *callback
return tracker;
}
+ dwfltracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE);
+
tracker->callbacks = callbacks;
return tracker;
}
@@ -61,6 +65,25 @@ void dwfl_process_tracker_end (Dwfl_Process_Tracker *tracker)
if (tracker == NULL)
return;
+ /* HACK to allow iteration of dynamicsizehash_concurrent. */
+ /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */
+ pthread_rwlock_destroy(&tracker->elftab.resize_rwl);
+ for (size_t idx = 1; idx <= tracker->elftab.size; idx++)
+ {
+ dwfltracker_elftab_ent *ent = &tracker->elftab.table[idx];
+ if (ent->hashval == 0)
+ continue;
+ dwfltracker_elf_info *t = (dwfltracker_elf_info *) atomic_load_explicit
(&ent->val_ptr,
+
memory_order_relaxed);
+ free(t->module_name);
+ if (t->fd >= 0)
+ close(t->fd);
+ if (t->elf != NULL)
+ elf_end(t->elf);
+ free(t); /* TODO: Check necessity. */
+ }
+ free (tracker->elftab.table);
+
/* TODO: Call dwfl_end for each Dwfl connected to this tracker. */
free (tracker);
}
diff --git a/libdwfl/dwfl_process_tracker_elftab.c
b/libdwfl/dwfl_process_tracker_elftab.c
new file mode 100644
index 00000000..af1f3ce3
--- /dev/null
+++ b/libdwfl/dwfl_process_tracker_elftab.c
@@ -0,0 +1,46 @@
+/* Dwfl_Process_Tracker Elf table implementation.
+ Copyright (C) 2025 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#include <libdwflP.h>
+
+/* Definitions for the Elf table. */
+#define TYPE dwfltracker_elf_info *
+#define NAME dwfltracker_elftab
+#define ITERATE 1
+/* TODO(REVIEW): Omit reverse? */
+#define REVERSE 1
+#define COMPARE(a, b) \
+ strcmp ((a)->module_name, (b)->module_name)
+
+#include "../lib/dynamicsizehash_concurrent.c"
diff --git a/libdwfl/dwfl_process_tracker_elftab.h
b/libdwfl/dwfl_process_tracker_elftab.h
new file mode 100644
index 00000000..d78a7394
--- /dev/null
+++ b/libdwfl/dwfl_process_tracker_elftab.h
@@ -0,0 +1,40 @@
+/* Dwfl_Process_Tracker Elf table.
+ Copyright (C) 2025 Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef DWFL_PROCESS_TRACKER_ELFTAB_H
+#define DWFL_PROCESS_TRACKER_ELFTAB_H 1
+
+/* Definitions for the Elf table. */
+#define TYPE dwfltracker_elf_info *
+#define NAME dwfltracker_elftab
+#define ITERATE 1
+#define COMPARE(a, b) \
+ strcmp ((a)->module_name, (b)->module_name)
+#include <dynamicsizehash_concurrent.h>
+
+#endif
diff --git a/libdwfl/dwfl_process_tracker_find_elf.c
b/libdwfl/dwfl_process_tracker_find_elf.c
new file mode 100644
index 00000000..1fcdf50b
--- /dev/null
+++ b/libdwfl/dwfl_process_tracker_find_elf.c
@@ -0,0 +1,111 @@
+/* Find Elf file from dwfl_linux_proc_report, cached via Dwfl_Process_Tracker.
+ Copyright (C) 2025, Red Hat, Inc.
+ This file is part of elfutils.
+
+ This file is free software; you can redistribute it and/or modify
+ it under the terms of either
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at
+ your option) any later version
+
+ or
+
+ * the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at
+ your option) any later version
+
+ or both in parallel, as here.
+
+ elfutils is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received copies of the GNU General Public License and
+ the GNU Lesser General Public License along with this program. If
+ not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/stat.h>
+#include "../libelf/libelfP.h"
+/* XXX: Private header needed for Elf * ref_count field. */
+/* TODO: Consider dup_elf() rather than direct ref_count access. */
+
+#include "libdwflP.h"
+
+int
+dwfl_process_tracker_find_elf (Dwfl_Module *mod,
+ void **userdata __attribute__ ((unused)),
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **elfp)
+{
+ /* XXX(REVIEW): Assuming this isn't called with elfp already set. */
+ assert (*elfp == NULL);
+ Dwfl_Process_Tracker *tracker = mod->dwfl->tracker;
+ dwfltracker_elf_info *ent = NULL;
+ int rc;
+ struct stat sb;
+
+ if (tracker != NULL)
+ {
+ unsigned long int hval = elf_hash(module_name);
+ ent = dwfltracker_elftab_find(&tracker->elftab, hval);
+ if (ent != NULL)
+ {
+ /* TODO: Also reopen the file when module_name set but fd not set? */
+ rc = fstat(ent->fd, &sb);
+ if (rc < 0 || ent->dev != sb.st_dev || ent->ino != sb.st_ino
+ || ent->last_mtime != sb.st_mtime)
+ ent = NULL; /* file modified, fall back to uncached behaviour */
+ else
+ {
+ /* XXX Caller also holds the Elf * jointly with prior owners: */
+ if (ent->elf != NULL)
+ ent->elf->ref_count++;
+ *elfp = ent->elf;
+ *file_name = strdup(ent->module_name);
+ return ent->fd;
+ }
+ }
+ else
+ {
+ ent = calloc (1, sizeof (dwfltracker_elf_info));
+ ent->module_name = strdup(module_name);
+ if (dwfltracker_elftab_insert(&tracker->elftab, hval, ent) != 0)
+ {
+ free(ent->module_name);
+ free(ent);
+ ent = NULL; /* fall back to uncached behaviour */
+ /* TODO: Could goto and repeat the find operation? */
+ }
+ }
+ }
+
+ int fd = INTUSE(dwfl_linux_proc_find_elf) (mod, userdata, module_name,
+ base, file_name, elfp);
+
+ /* XXX fd < 0 implies elf_from_remote_memory, uses base, not cacheable */
+ if (tracker != NULL && ent != NULL && fd >= 0 && *file_name != NULL)
+ {
+ /* XXX *elfp may be NULL here, will be populated later in open_elf */
+ /* XXX Dwfl_Process_Tracker also holds the Elf * jointly with the
caller: */
+ if (*elfp != NULL)
+ (*elfp)->ref_count++;
+ ent->elf = *elfp;
+ ent->fd = fd;
+ rc = fstat(fd, &sb);
+ if (rc == 0) /* TODO(REVIEW): Report rc != 0 via errno? */
+ {
+ ent->dev = sb.st_dev;
+ ent->ino = sb.st_ino;
+ ent->last_mtime = sb.st_mtime;
+ }
+ }
+
+ return fd;
+}
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 86005515..3613be58 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -409,6 +409,13 @@ extern int dwfl_linux_proc_find_elf (Dwfl_Module *mod,
void **userdata,
const char *module_name, Dwarf_Addr base,
char **file_name, Elf **);
+/* The same callback, except this first attempts to look up a cached
+ Elf* and fd from the Dwfl_Module's Dwfl_Process_Tracker (if any).
+ If a new Elf* has to be created, this saves it to the cache. */
+extern int dwfl_process_tracker_find_elf (Dwfl_Module *mod, void **userdata,
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **);
+
/* Standard argument parsing for using a standard callback set. */
struct argp;
extern const struct argp *dwfl_standard_argp (void) __const_attribute__;
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index c63f7c7d..0ab1c21b 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -101,12 +101,27 @@ typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
extern int __libdwfl_canon_error (Dwfl_Error) internal_function;
extern void __libdwfl_seterrno (Dwfl_Error) internal_function;
+/* Hash table for Elf *. */
+typedef struct
+{
+ char *module_name; /* dwfltracker_elftab_ent is used iff non-NULL. */
+ int fd;
+ Elf *elf;
+ dev_t dev;
+ ino_t ino;
+ time_t last_mtime;
+} dwfltracker_elf_info;
+#include "dwfl_process_tracker_elftab.h"
+
struct Dwfl_Process_Tracker
{
const Dwfl_Callbacks *callbacks;
- /* ... */
+
+ /* Table of cached Elf * including fd, path, fstat info. */
+ dwfltracker_elftab elftab;
};
+
/* Resources we might keep for the user about the core file that the
Dwfl might have been created from. Can currently only be set
through std-argp. */
diff --git a/libdwfl/libdwfl_next_prime.c b/libdwfl/libdwfl_next_prime.c
new file mode 100644
index 00000000..f99d4c6c
--- /dev/null
+++ b/libdwfl/libdwfl_next_prime.c
@@ -0,0 +1,6 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define next_prime attribute_hidden __libdwfl_next_prime
+#include "../lib/next_prime.c"
--
2.47.0