On Thu, Apr 24, 2025 at 5:52 PM Serhei Makarov <ser...@serhei.io> wrote: > > Changes for v5: > > - Bugfixes in dwflst_tracker_find_elf.c. > > Changes for v4: > > - Separate out libdwfl_stacktrace, as requested. > > - dwfl_module_getdwarf.c now uses the dwflst_tracker_cache_elf() > interface instead of editing the elftab directly. > > Changes for v3: > > - Reworked elftab to incorporate dev/ino into the caching key > (to allow caching modules from different filesystems > e.g. a main filesystem and a container filesystem) > and guard more carefully against collisions. > > - Add external API for implementing a custom > find_elf callback that reads/writes the cache. > > Changes for v2: > > - Add locking for elftab. This is needed in addition to the > intrinsic locking in dynamicsizehash_concurrent to avoid > having cache_elf expose an incomplete dwfltracker_elf_info* > entry to other threads while its data is being populated / > replaced. > > - Tidy dwfl_process_tracker_find_elf.c into the main find_elf > callback and two functions to consider (in future) making into > a public api for custom cached callbacks. > > * * * > > The Dwflst_Process_Tracker includes a dynamicsizehash cache which maps > file paths to Elf * (or rather, dwflst_tracker_elf_info * storing fd > and Elf *). We provide a dwflst_tracker_linux_proc_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 in dwflst_tracker_cache_elf 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 dwflst_tracker_end will issue > another elf_end call. > > * libdwfl_stacktrace/libdwfl_stacktrace.h > (dwflst_tracker_find_cached_elf): New function. > (dwflst_tracker_cache_elf): New function. > (dwflst_module_gettracker): New function, gives external users > a way to access Dwflst_Process_Tracker given a Dwfl_Module. > (dwflst_tracker_linux_proc_find_elf): New function, serves as a > cached version of the dwfl_linux_proc_find_elf callback. > * libdwfl_stacktrace/libdwfl_stacktraceP.h (dwflst_tracker_elf_info): > New struct typedef. > (struct Dwflst_Process_Tracker): Add dynamicsizehash table of > dwflst_tracker_elf_info structs + associated rwlock. > (INTDECLs): Add INTDECL for dwflst_tracker_find_cached_elf, > dwflst_tracker_cache_elf, dwflst_module_gettracker. > * libdwfl_stacktrace/dwflst_tracker_elftab.c: New file, instantiates > lib/dynamicsizehash_concurrent.c to store dwflst_tracker_elf_info > structs. > * libdwfl_stacktrace/dwflst_tracker_elftab.h: New file, ditto. > * libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c: New file. > * libdwfl_stacktrace/dwflst_process_tracker.c (dwflst_tracker_begin): > Init elftab. > (dwflst_tracker_end): Clean up elftab. Lock and iterate the hash to > free tracker->elftab.table items. > * libdwfl_stacktrace/dwflst_tracker_find_elf.c: New file, implements a > find_elf callback that wraps dwfl_linux_proc_find_elf with > additional caching logic, and an API to access the > Dwflst_Process_Tracker Elf cache when implementing a custom find_elf > callback. > * libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): Add > dwflst_tracker_find_elf.c, dwflst_tracker_elftab.c, > libdwfl_stacktrace_next_prime.c. > (noinst_HEADERS): Add dwflst_tracker_elftab.h. > * libdw/libdw.map: Add dwflst_tracker_find_cached_elf, > dwflst_tracker_cache_elf, > dwflst_module_gettracker, > dwflst_tracker_linux_proc_find_elf. > * libdwfl/Makefile.am (AM_CPPFLAGS): Include headers from > ../libdwfl_stacktrace. > * libdwfl/dwfl_module_getdwarf.c (open_elf): Cache file->elf in > Dwflst_Process_Tracker. Must be done here as > dwfl_linux_proc_find_elf opens an fd but does not yet create the > Elf *. > --- > libdw/libdw.map | 4 + > libdwfl/Makefile.am | 5 +- > libdwfl/dwfl_module_getdwarf.c | 10 +- > libdwfl_stacktrace/Makefile.am | 5 +- > libdwfl_stacktrace/dwflst_process_tracker.c | 26 +++ > libdwfl_stacktrace/dwflst_tracker_elftab.c | 45 ++++ > libdwfl_stacktrace/dwflst_tracker_elftab.h | 40 ++++ > libdwfl_stacktrace/dwflst_tracker_find_elf.c | 218 ++++++++++++++++++ > libdwfl_stacktrace/libdwfl_stacktrace.h | 37 ++- > libdwfl_stacktrace/libdwfl_stacktraceP.h | 24 +- > .../libdwfl_stacktrace_next_prime.c | 6 + > 11 files changed, 414 insertions(+), 6 deletions(-) > create mode 100644 libdwfl_stacktrace/dwflst_tracker_elftab.c > create mode 100644 libdwfl_stacktrace/dwflst_tracker_elftab.h > create mode 100644 libdwfl_stacktrace/dwflst_tracker_find_elf.c > create mode 100644 libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c > > diff --git a/libdw/libdw.map b/libdw/libdw.map > index fb69a62a..46d0878a 100644 > --- a/libdw/libdw.map > +++ b/libdw/libdw.map > @@ -400,4 +400,8 @@ ELFUTILS_0.193_EXPERIMENTAL { > dwflst_tracker_begin; > dwflst_tracker_dwfl_begin; > dwflst_tracker_end; > + dwflst_tracker_find_cached_elf; > + dwflst_tracker_cache_elf; > + dwflst_module_gettracker; > + dwflst_tracker_linux_proc_find_elf; > }; > diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am > index b30b86f0..6ad5ba10 100644 > --- a/libdwfl/Makefile.am > +++ b/libdwfl/Makefile.am > @@ -2,7 +2,7 @@ > ## > ## Process this file with automake to create Makefile.in > ## > -## Copyright (C) 2005-2010, 2013 Red Hat, Inc. > +## Copyright (C) 2005-2010, 2013, 2025 Red Hat, Inc. > ## This file is part of elfutils. > ## > ## This file is free software; you can redistribute it and/or modify > @@ -31,7 +31,8 @@ > ## > include $(top_srcdir)/config/eu.am > AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \ > - -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf > -I$(builddir)/../debuginfod > + -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf > -I$(builddir)/../debuginfod \ > + -I$(srcdir)/../libdwfl_stacktrace > VERSION = 1 > > noinst_LIBRARIES = libdwfl.a > diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c > index 6f98c02b..7fd0d3aa 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 > @@ -35,6 +35,7 @@ > #include <fcntl.h> > #include <string.h> > #include "libdwP.h" /* DWARF_E_* values are here. */ > +#include "libdwfl_stacktraceP.h" /* want the INTDECLS */ > #include "libelfP.h" > #include "system.h" > > @@ -79,6 +80,13 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) > if (error != DWFL_E_NOERROR) > return error; > > + /* Cache file->elf in Dwflst_Process_Tracker if available: */ > + if (mod->dwfl->tracker != NULL && file->name != NULL) > + { > + INTUSE(dwflst_tracker_cache_elf) (mod->dwfl->tracker, file->name, > + file->name, file->elf, file->fd); > + } > + > GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); > if (ehdr == NULL) > { > diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am > index d57431c0..ffddec0c 100644 > --- a/libdwfl_stacktrace/Makefile.am > +++ b/libdwfl_stacktrace/Makefile.am > @@ -41,6 +41,9 @@ pkginclude_HEADERS = libdwfl_stacktrace.h > > > libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ > + dwflst_tracker_find_elf.c \ > + dwflst_tracker_elftab.c \ > + libdwfl_stacktrace_next_prime.c \ > dwflst_perf_frame.c > > libdwfl_stacktrace = $(libdw) > @@ -52,7 +55,7 @@ libeu = ../lib/libeu.a > libdwfl_stacktrace_pic_a_SOURCES = > am_libdwfl_stacktrace_pic_a_OBJECTS = $(libdwfl_stacktrace_a_SOURCES:.c=.os) > > -noinst_HEADERS = libdwfl_stacktraceP.h > +noinst_HEADERS = libdwfl_stacktraceP.h dwflst_tracker_elftab.h > > EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest > > diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c > b/libdwfl_stacktrace/dwflst_process_tracker.c > index 057c9f7a..10cd9a7c 100644 > --- a/libdwfl_stacktrace/dwflst_process_tracker.c > +++ b/libdwfl_stacktrace/dwflst_process_tracker.c > @@ -32,6 +32,8 @@ > > #include "libdwfl_stacktraceP.h" > > +#define HTAB_DEFAULT_SIZE 1021 > + > Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks > *callbacks) > { > Dwflst_Process_Tracker *tracker = calloc (1, sizeof *tracker); > @@ -41,6 +43,9 @@ Dwflst_Process_Tracker *dwflst_tracker_begin (const > Dwfl_Callbacks *callbacks) > return tracker; > } > > + dwflst_tracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE); > + rwlock_init (tracker->elftab_lock); > + > tracker->callbacks = callbacks; > return tracker; > } > @@ -61,6 +66,27 @@ void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) > if (tracker == NULL) > return; > > + /* HACK to allow iteration of dynamicsizehash_concurrent. */ > + /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */ > + rwlock_fini (tracker->elftab_lock); > + pthread_rwlock_destroy(&tracker->elftab.resize_rwl); > + for (size_t idx = 1; idx <= tracker->elftab.size; idx++) > + { > + dwflst_tracker_elftab_ent *ent = &tracker->elftab.table[idx]; > + if (ent->hashval == 0) > + continue; > + dwflst_tracker_elf_info *t = > + (dwflst_tracker_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_stacktrace/dwflst_tracker_elftab.c > b/libdwfl_stacktrace/dwflst_tracker_elftab.c > new file mode 100644 > index 00000000..a7ce452a > --- /dev/null > +++ b/libdwfl_stacktrace/dwflst_tracker_elftab.c > @@ -0,0 +1,45 @@ > +/* Dwflst_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 <libdwfl_stacktraceP.h> > + > +/* Definitions for the Elf table. */ > +#define TYPE dwflst_tracker_elf_info * > +#define NAME dwflst_tracker_elftab > +#define ITERATE 1 > +#define COMPARE(a, b) \ > + (strcmp ((a)->module_name, (b)->module_name) \ > + && (a)->dev == (b)->dev && (a)->ino == (b)->ino) > + > +#include "../lib/dynamicsizehash_concurrent.c" > diff --git a/libdwfl_stacktrace/dwflst_tracker_elftab.h > b/libdwfl_stacktrace/dwflst_tracker_elftab.h > new file mode 100644 > index 00000000..c22ddb53 > --- /dev/null > +++ b/libdwfl_stacktrace/dwflst_tracker_elftab.h > @@ -0,0 +1,40 @@ > +/* Dwflst_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 DWFLST_TRACKER_ELFTAB_H > +#define DWFLST_TRACKER_ELFTAB_H 1 > + > +/* Definitions for the Elf table. */ > +#define TYPE dwflst_tracker_elf_info * > +#define NAME dwflst_tracker_elftab > +#define ITERATE 1 > +#define COMPARE(a, b) \ > + strcmp ((a)->module_name, (b)->module_name) > +#include <dynamicsizehash_concurrent.h> > + > +#endif > diff --git a/libdwfl_stacktrace/dwflst_tracker_find_elf.c > b/libdwfl_stacktrace/dwflst_tracker_find_elf.c > new file mode 100644 > index 00000000..7f9eb569 > --- /dev/null > +++ b/libdwfl_stacktrace/dwflst_tracker_find_elf.c > @@ -0,0 +1,218 @@ > +/* Find Elf file and cache via Dwflst_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 "libdwfl_stacktraceP.h" > + > +unsigned long int > +__libdwfl_stacktrace_elftab_hash_st (const char *module_name, > + dev_t st_dev, > + ino_t st_ino) > +{ > + unsigned long int hval = elf_hash(module_name); > + hval ^= (unsigned long int)st_dev; > + hval ^= (unsigned long int)st_ino; > + return hval; > +} > + > +unsigned long int > +__libdwfl_stacktrace_elftab_hash (const char *module_name, > + const char *module_path, > + int fd) > +{ > + struct stat sb; > + int rc = -1; > + if (fd >= 0) > + rc = fstat(fd, &sb); > + else if (module_path != NULL) > + rc = stat(module_path, &sb); > + if (rc < 0) > + return elf_hash(module_name); > + return __libdwfl_stacktrace_elftab_hash_st > + (module_name, sb.st_dev, sb.st_ino); > +} > + > +int > +dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, > + const char *module_name, > + const char *module_path, > + char **file_name, Elf **elfp) > +{ > + dwflst_tracker_elf_info *ent = NULL; > + int rc = -1; > + struct stat sb; > + > + if (module_path == NULL) > + module_path = module_name; > + unsigned long int hval = > + __libdwfl_stacktrace_elftab_hash (module_name, module_path, -1/* no fd > */); > + > + rwlock_rdlock(tracker->elftab_lock); > + ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); > + rwlock_unlock(tracker->elftab_lock); > + > + /* Guard against collisions. > + TODO: Need proper chaining, dynamicsizehash_concurrent isn't really > + equipped for it. */ > + if (ent != NULL) > + rc = fstat(ent->fd, &sb); > + if (rc < 0 || strcmp (module_name, ent->module_name) != 0 > + || ent->dev != sb.st_dev || ent->ino != sb.st_ino) > + return -1; > + > + /* Verify that ent->fd has not been updated: */ > + if (rc < 0 || ent->dev != sb.st_dev || ent->ino != sb.st_ino > + || ent->last_mtime != sb.st_mtime) > + return -1; > + > + if (ent->elf != NULL) > + ent->elf->ref_count++; > + *elfp = ent->elf; > + *file_name = strdup(ent->module_name); > + return ent->fd; > +} > +INTDEF(dwflst_tracker_find_cached_elf) > + > +bool > +dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, > + const char *module_name, > + const char *file_name __attribute__((unused)), > + Elf *elf, int fd) > +{ > + dwflst_tracker_elf_info *ent = NULL; > + int rc = -1; > + struct stat sb; > + > + if (fd >= 0) > + rc = fstat(fd, &sb); > + if (rc < 0) > + return false; > + unsigned long int hval = > + __libdwfl_stacktrace_elftab_hash_st (module_name, sb.st_dev, sb.st_ino); > + > + rwlock_wrlock(tracker->elftab_lock); > + ent = dwflst_tracker_elftab_find(&tracker->elftab, hval); > + /* Guard against collisions. > + TODO: Need proper chaining, dynamicsizehash_concurrent isn't really > + equipped for it. */ > + if (ent != NULL && (strcmp (module_name, ent->module_name) != 0 > + || ent->dev != sb.st_dev || ent->ino != sb.st_ino)) > + { > + rwlock_unlock(tracker->elftab_lock); > + return false; > + } > + if (ent == NULL) > + { > + ent = calloc (1, sizeof (dwflst_tracker_elf_info));
Missing NULL check. > + ent->module_name = strdup(module_name); > + > + if (dwflst_tracker_elftab_insert(&tracker->elftab, hval, ent) != 0) > + { > + free(ent->module_name); > + free(ent); > + rwlock_unlock(tracker->elftab_lock); > + assert(false); /* Should not occur due to the wrlock on elftab. */ > + } > + } > + else > + { > + /* TODO: 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 incrementally. */ > + > + /* assert(ent->elf == NULL || ent->elf == elf); */ /* Guard against > redundant/leaked Elf *. */ > + /* assert(ent->fd == fd); */ /* Guard against redundant open. */ > + > + /* For now, correct behaviour (from dwfl_module_getdwarf.c open_elf) > + is to replace the existing elf, keep module_name. */ > + if (ent->elf != NULL && ent->elf != elf) > + elf_end(ent->elf); > + } > + if (elf != NULL && ent->elf != elf) > + elf->ref_count++; > + ent->elf = elf; > + ent->fd = fd; > + if (rc == 0) /* TODO(REVIEW): Report rc != 0 via errno? */ If this entry into the tracker cache is still usable and this function is returning true to indicate success then I wouldn't set errno. > + { > + ent->dev = sb.st_dev; > + ent->ino = sb.st_ino; > + ent->last_mtime = sb.st_mtime; > + } > + rwlock_unlock(tracker->elftab_lock); > + return true; > +} > +INTDEF(dwflst_tracker_cache_elf) > + > +Dwflst_Process_Tracker * > +dwflst_module_gettracker (Dwfl_Module *mod) > +{ > + if (mod == NULL) > + return NULL; > + if (mod->dwfl == NULL) > + return NULL; > + return mod->dwfl->tracker; > +} > +INTDEF(dwflst_module_gettracker) > + > +int > +dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, > + void **userdata __attribute__ ((unused)), > + const char *module_name, Dwarf_Addr base, > + char **file_name, Elf **elfp) > +{ > + Dwflst_Process_Tracker *tracker = INTUSE(dwflst_module_gettracker) (mod); > + int fd; > + > + if (tracker != NULL) > + { > + fd = INTUSE(dwflst_tracker_find_cached_elf) > + (tracker, module_name, module_name, file_name, elfp); > + if (fd >= 0) > + return fd; > + } > + > + fd = INTUSE(dwfl_linux_proc_find_elf) (mod, userdata, module_name, > + base, file_name, elfp); > + > + if (tracker != NULL && fd >= 0 && *file_name != NULL) > + { > + INTUSE(dwflst_tracker_cache_elf) > + (tracker, module_name, *file_name, *elfp, fd); > + } > + return fd; > +} > diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h > b/libdwfl_stacktrace/libdwfl_stacktrace.h > index f3a82d18..d29dc640 100644 > --- a/libdwfl_stacktrace/libdwfl_stacktrace.h > +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h > @@ -57,10 +57,45 @@ extern Dwflst_Process_Tracker *dwflst_tracker_begin > (const Dwfl_Callbacks *callb > extern Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) > __nonnull_attribute__ (1); > > -/* End all sessions with this tracker. */ > +/* Try to find a cached Elf corresponding to MODULE_NAME. Verifies > + that the cached Elf has dev/ino/mtime matching the file on disk. > + Non-NULL MODULE_PATH specifies an alternate location for the module > + e.g. /proc/PID/root/MODULE_NAME. Stores FILE_NAME and ELFP values. > + Returns fd similar to the find_elf callbacks, or -1 if cached Elf > + was not found. */ > +extern int dwflst_tracker_find_cached_elf (Dwflst_Process_Tracker *tracker, > + const char *module_name, > + const char *module_path, > + char **file_name, Elf **elfp) > + __nonnull_attribute__ (1, 2, 4, 5); > + > +/* Store an Elf corresponding to MODULE_NAME in the tracker's cache. > + FILE_NAME and FD values must be provided, similar to the output of > + a find_elf callback. Returns TRUE iff the Elf was successfully > + stored in the cache. */ > +extern bool dwflst_tracker_cache_elf (Dwflst_Process_Tracker *tracker, > + const char *module_name, > + const char *file_name, > + Elf *elf, int fd) > + __nonnull_attribute__ (1, 2); > + > +/* For implementing a find_elf callback based on the prior two functions. > + Returns the Dwflst_Process_Tracker corresponding to MOD. */ > +extern Dwflst_Process_Tracker *dwflst_module_gettracker (Dwfl_Module *mod); > + > +/* End all sessions with this tracker. */ > extern void dwflst_tracker_end (Dwflst_Process_Tracker *tracker); > > > +/* Adaptation of the dwfl_linux_proc_find_elf callback from libdwfl, > + except this first attempts to look up a cached Elf* and fd from the > + Dwfl_Module's Dwflst_Process_Tracker (if any). If a new Elf* is > + created, this callback saves it to the tracker's cache. */ > +extern int dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, void > **userdata, > + const char *module_name, > Dwarf_Addr base, > + char **file_name, Elf **); I'd mention in these function comments where applicable that cached elfs are owned by the tracker and the tracker will close them during dwflst_tracker_end. > + > + > /* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ > > /* Returns the linux perf_events register mask describing a set of > diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h > b/libdwfl_stacktrace/libdwfl_stacktraceP.h > index 9313176c..e457d35a 100644 > --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h > +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h > @@ -33,10 +33,32 @@ > > #include "libdwflP.h" > > +/* 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; > +} dwflst_tracker_elf_info; > +#include "dwflst_tracker_elftab.h" > + > struct Dwflst_Process_Tracker > { > const Dwfl_Callbacks *callbacks; > - /* ... */ > + > + /* Table of cached Elf * including fd, path, fstat info. */ > + dwflst_tracker_elftab elftab; > + rwlock_define(, elftab_lock); > }; > > + > +/* Avoid PLT entries. */ > +INTDECL (dwflst_module_gettracker) > +INTDECL (dwflst_tracker_find_cached_elf) > +INTDECL (dwflst_tracker_cache_elf) > + > + > #endif /* libdwfl_stacktraceP.h */ > diff --git a/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c > b/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c > new file mode 100644 > index 00000000..0974ce68 > --- /dev/null > +++ b/libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c > @@ -0,0 +1,6 @@ > +#ifdef HAVE_CONFIG_H > +# include <config.h> > +#endif > + > +#define next_prime attribute_hidden __libdwfl_stacktrace_next_prime > +#include "../lib/next_prime.c" > -- > 2.47.0 > Also if you haven't done so already, please run any manual tests under valgrind memcheck (and helgrind if you are able to test this with multithreading). Aaron