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;
};
##
## 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
##
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
/* 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
#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"
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)
{
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)
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
#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);
return tracker;
}
+ dwflst_tracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE);
+ rwlock_init (tracker->elftab_lock);
+
tracker->callbacks = callbacks;
return 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);
}
--- /dev/null
+/* 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"
--- /dev/null
+/* 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
--- /dev/null
+/* 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;
+}
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
#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 */
--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#define next_prime attribute_hidden __libdwfl_stacktrace_next_prime
+#include "../lib/next_prime.c"