summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhei Makarov <[email protected]>2025-04-22 18:30:50 -0400
committerSerhei Makarov <[email protected]>2025-04-25 10:10:01 -0400
commit343dcb44be11649339c8debb9c916408ecd7ee5b (patch)
treea1c7ed4e80433a20de63f1afd2aede117e0bbbd7
parent3dc8d7cc21bcb70e6ea049e621da22b546992429 (diff)
libdwfl_stacktrace [6/12]: Elf* caching via dwflst_process_tracker
Changes for v6: - Minor fixes as requested. 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 *.
-rw-r--r--libdw/libdw.map4
-rw-r--r--libdwfl/Makefile.am5
-rw-r--r--libdwfl/dwfl_module_getdwarf.c10
-rw-r--r--libdwfl_stacktrace/Makefile.am5
-rw-r--r--libdwfl_stacktrace/dwflst_process_tracker.c26
-rw-r--r--libdwfl_stacktrace/dwflst_tracker_elftab.c45
-rw-r--r--libdwfl_stacktrace/dwflst_tracker_elftab.h40
-rw-r--r--libdwfl_stacktrace/dwflst_tracker_find_elf.c227
-rw-r--r--libdwfl_stacktrace/libdwfl_stacktrace.h41
-rw-r--r--libdwfl_stacktrace/libdwfl_stacktraceP.h24
-rw-r--r--libdwfl_stacktrace/libdwfl_stacktrace_next_prime.c6
11 files changed, 427 insertions, 6 deletions
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..56e87787
--- /dev/null
+++ b/libdwfl_stacktrace/dwflst_tracker_find_elf.c
@@ -0,0 +1,227 @@
+/* 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));
+ if (ent == NULL)
+ {
+ rwlock_unlock(tracker->elftab_lock);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return false;
+ }
+ 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)
+ {
+ ent->dev = sb.st_dev;
+ ent->ino = sb.st_ino;
+ ent->last_mtime = sb.st_mtime;
+ }
+ /* else create a cache entry with 0 values for dev/ino/mtime;
+ since dev/ino are hashed, this will not conflict with entries
+ for which fstat was successful */
+ 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..286409b8 100644
--- a/libdwfl_stacktrace/libdwfl_stacktrace.h
+++ b/libdwfl_stacktrace/libdwfl_stacktrace.h
@@ -57,10 +57,49 @@ 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. The tracker will retain the Elf* via libelf's
+ reference counting mechanism, and release its reference during
+ dwflst_tracker_end. */
+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 libdwfl 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.
+ The cache will retain the Elf* via libelf's reference counting
+ mechanism, and release its reference during dwflst_tracker_end. */
+extern int dwflst_tracker_linux_proc_find_elf (Dwfl_Module *mod, void **userdata,
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **);
+
+
/* 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"