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

Reply via email to