On Thu, Apr 24, 2025 at 5:52 PM Serhei Makarov <[email protected]> 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