On Thu, Apr 24, 2025 at 5:52 PM Serhei Makarov <ser...@serhei.io> wrote: > > Changes for v4: > > - Separate out libdwfl_stacktrace, as requested. > > Changes for v3: > > - Handle dwfl->process == NULL case in __libdwfl_remove_dwfl_from_tracker. > > * * * > > The Dwflst_Process_Tracker also includes a dynamicsizehash cache which > maps process ids to Dwfl * (or rather, dwflst_tracker_dwfl_info * > allowing the table entry to be replaced). Dwfls created from > the tracker are automatically added to it, and removed on dwfl_end(). > > * libdwfl_stacktrace/libdwfl_stacktraceP.h (dwflst_tracker_dwfl_info): > New typedef, provides indirection to allow a dwfltab entry to be > invalidated. > (struct Dwflst_Process_Tracker): add dwfltab. > (__libdwfl_stacktrace_add_dwfl_to_tracker): New function. > (__libdwfl_stacktrace_remove_dwfl_from_tracker): New function. > * libdwfl_stacktrace/dwflst_process_tracker.c > (dwflst_tracker_begin): Init dwfltab. > (__libdwfl_stacktrace_add_dwfl_to_tracker): New function; add dwfl > to dwfltab. > (__libdwfl_stacktrace_remove_dwfl_from_tracker): New function; > invalidate dwfl entry, since dynamicsizehash doesn't support > outright deletion. > (dwflst_tracker_end): Clean up dwfltab. Lock and iterate the table > to free tracker->dwfltab.table items. > * libdwfl_stacktrace/dwflst_tracker_dwfltab.c: New file, instantiates > lib/dynamicsizehash_concurrent.c to store dwfltracker_dwfl_info > structs. > * libdwfl_stacktrace/dwflst_tracker_dwfltab.h: New file, ditto. > * libdwfl_stacktrace/Makefile.am > (libdwfl_stacktrace_a_SOURCES): Add dwflst_tracker_dwfltab.c. > (noinst_HEADERS): Add dwflst_tracker_dwfltab.h. > * libdwfl/dwfl_frame.c (dwfl_attach_state): > Call __libdwfl_stacktrace_add_dwfl_to_tracker. > * libdwfl/dwfl_end.c (dwfl_end): Add INTDEF. > Call __libdwfl_stacktrace_remove_dwfl_from_tracker. > * libdwfl/libdwflP.h (INTDECLs): Add dwfl_end. > --- > libdwfl/dwfl_end.c | 8 +- > libdwfl/dwfl_frame.c | 7 +- > libdwfl/libdwflP.h | 1 + > libdwfl_stacktrace/Makefile.am | 4 +- > libdwfl_stacktrace/dwflst_process_tracker.c | 87 ++++++++++++++++++++- > libdwfl_stacktrace/dwflst_tracker_dwfltab.c | 46 +++++++++++ > libdwfl_stacktrace/dwflst_tracker_dwfltab.h | 42 ++++++++++ > libdwfl_stacktrace/libdwfl_stacktraceP.h | 23 ++++++ > 8 files changed, 213 insertions(+), 5 deletions(-) > create mode 100644 libdwfl_stacktrace/dwflst_tracker_dwfltab.c > create mode 100644 libdwfl_stacktrace/dwflst_tracker_dwfltab.h > > diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c > index 7b5ac8a1..d9cf569b 100644 > --- a/libdwfl/dwfl_end.c > +++ b/libdwfl/dwfl_end.c > @@ -1,5 +1,5 @@ > /* Finish a session using libdwfl. > - Copyright (C) 2005, 2008, 2012-2013, 2015 Red Hat, Inc. > + Copyright (C) 2005, 2008, 2012-2013, 2015, 2025 Red Hat, Inc. > This file is part of elfutils. > > This file is free software; you can redistribute it and/or modify > @@ -31,6 +31,7 @@ > #endif > > #include "libdwflP.h" > +#include "libdwfl_stacktraceP.h" > > void > dwfl_end (Dwfl *dwfl) > @@ -42,6 +43,9 @@ dwfl_end (Dwfl *dwfl) > __libdwfl_debuginfod_end (dwfl->debuginfod); > #endif > > + if (dwfl->tracker != NULL) > + __libdwfl_stacktrace_remove_dwfl_from_tracker (dwfl); > + > if (dwfl->process) > __libdwfl_process_free (dwfl->process); > > @@ -68,3 +72,5 @@ dwfl_end (Dwfl *dwfl) > } > free (dwfl); > } > +INTDEF(dwfl_end) > + > diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c > index 2e6c6de8..0d619887 100644 > --- a/libdwfl/dwfl_frame.c > +++ b/libdwfl/dwfl_frame.c > @@ -1,5 +1,5 @@ > /* Get Dwarf Frame state for target PID or core file. > - Copyright (C) 2013, 2014, 2024 Red Hat, Inc. > + Copyright (C) 2013, 2014, 2024-2025 Red Hat, Inc. > This file is part of elfutils. > > This file is free software; you can redistribute it and/or modify > @@ -33,6 +33,7 @@ > #include <system.h> > > #include "libdwflP.h" > +#include "libdwfl_stacktraceP.h" > > /* Set STATE->pc_set from STATE->regs according to the backend. Return true > on > success, false on error. */ > @@ -206,6 +207,10 @@ dwfl_attach_state (Dwfl *dwfl, Elf *elf, pid_t pid, > process->pid = pid; > process->callbacks = thread_callbacks; > process->callbacks_arg = arg; > + > + if (dwfl->tracker != NULL) > + __libdwfl_stacktrace_add_dwfl_to_tracker (dwfl); > + > return true; > } > INTDEF(dwfl_attach_state) > diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h > index 57305f81..b622f8aa 100644 > --- a/libdwfl/libdwflP.h > +++ b/libdwfl/libdwflP.h > @@ -757,6 +757,7 @@ extern int dwfl_link_map_report (Dwfl *dwfl, const void > *auxv, size_t auxv_size, > > /* Avoid PLT entries. */ > INTDECL (dwfl_begin) > +INTDECL (dwfl_end) > INTDECL (dwfl_errmsg) > INTDECL (dwfl_errno) > INTDECL (dwfl_addrmodule) > diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am > index ffddec0c..99a80b5c 100644 > --- a/libdwfl_stacktrace/Makefile.am > +++ b/libdwfl_stacktrace/Makefile.am > @@ -43,6 +43,7 @@ pkginclude_HEADERS = libdwfl_stacktrace.h > libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ > dwflst_tracker_find_elf.c \ > dwflst_tracker_elftab.c \ > + dwflst_tracker_dwfltab.c \ > libdwfl_stacktrace_next_prime.c \ > dwflst_perf_frame.c > > @@ -55,7 +56,8 @@ 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 dwflst_tracker_elftab.h > +noinst_HEADERS = libdwfl_stacktraceP.h \ > + dwflst_tracker_elftab.h dwflst_tracker_dwfltab.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 10cd9a7c..f72b72b0 100644 > --- a/libdwfl_stacktrace/dwflst_process_tracker.c > +++ b/libdwfl_stacktrace/dwflst_process_tracker.c > @@ -45,6 +45,8 @@ Dwflst_Process_Tracker *dwflst_tracker_begin (const > Dwfl_Callbacks *callbacks) > > dwflst_tracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE); > rwlock_init (tracker->elftab_lock); > + dwflst_tracker_dwfltab_init (&tracker->dwfltab, HTAB_DEFAULT_SIZE); > + rwlock_init (tracker->dwfltab_lock); > > tracker->callbacks = callbacks; > return tracker; > @@ -58,19 +60,84 @@ Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker > *tracker) > > /* TODO: Could also share dwfl->debuginfod, but thread-safely? */ > dwfl->tracker = tracker; > + > + /* XXX: dwfl added to dwfltab when dwfl->process set in dwfl_attach_state. > */ > + /* XXX: dwfl removed from dwfltab in dwfl_end() */ > + > return dwfl; > } > > + > +void > +internal_function > +__libdwfl_stacktrace_add_dwfl_to_tracker (Dwfl *dwfl) { > + Dwflst_Process_Tracker *tracker = dwfl->tracker; > + assert (tracker != NULL); > + > + /* First try to find an existing entry to replace: */ > + dwflst_tracker_dwfl_info *ent = NULL; > + unsigned long int hval = dwfl->process->pid; > + > + rwlock_wrlock (tracker->dwfltab_lock); > + ent = dwflst_tracker_dwfltab_find(&tracker->dwfltab, hval); > + if (ent != NULL) > + { > + /* TODO: This is a bare-minimum solution. Ideally > + we would clean up the existing ent->dwfl, but > + this needs to be coordinated with any users of > + the dwfl library that might still be holding it. */ > + ent->dwfl = dwfl; > + ent->invalid = false; > + rwlock_unlock (tracker->dwfltab_lock); > + return; > + } > + > + /* Only otherwise try to insert an entry: */ > + ent = calloc (1, sizeof(dwflst_tracker_dwfl_info));
Missing NULL check. > + ent->dwfl = dwfl; > + ent->invalid = false; > + if (dwflst_tracker_dwfltab_insert(&tracker->dwfltab, hval, ent) != 0) > + { > + free(ent); > + rwlock_unlock (tracker->dwfltab_lock); > + assert(false); /* Should not occur due to the wrlock on dwfltab. */ > + } > + rwlock_unlock (tracker->dwfltab_lock); > +} > + > +void > +internal_function > +__libdwfl_stacktrace_remove_dwfl_from_tracker (Dwfl *dwfl) { > + if (dwfl->tracker == NULL) > + return; > + Dwflst_Process_Tracker *tracker = dwfl->tracker; > + dwflst_tracker_dwfl_info *ent = NULL; > + if (dwfl->process == NULL) > + return; > + unsigned long int hval = dwfl->process->pid; > + > + rwlock_wrlock (tracker->dwfltab_lock); > + ent = dwflst_tracker_dwfltab_find(&tracker->dwfltab, hval); > + if (ent != NULL && ent->dwfl == dwfl) > + { > + ent->dwfl = NULL; > + ent->invalid = true; > + } > + rwlock_unlock (tracker->dwfltab_lock); > +} > + > void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) > { > if (tracker == NULL) > return; > > + size_t idx; > + > /* 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++) > + for (idx = 1; idx <= tracker->elftab.size; idx++) > { > dwflst_tracker_elftab_ent *ent = &tracker->elftab.table[idx]; > if (ent->hashval == 0) > @@ -87,6 +154,22 @@ void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) > } > free (tracker->elftab.table); > > - /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */ > + /* XXX Based on lib/dynamicsizehash_concurrent.c free(). */ > + rwlock_fini (tracker->dwfltab_lock); > + pthread_rwlock_destroy(&tracker->dwfltab.resize_rwl); > + for (idx = 1; idx <= tracker->dwfltab.size; idx++) > + { > + dwflst_tracker_dwfltab_ent *ent = &tracker->dwfltab.table[idx]; > + if (ent->hashval == 0) > + continue; > + dwflst_tracker_dwfl_info *t = > + (dwflst_tracker_dwfl_info *) atomic_load_explicit (&ent->val_ptr, > + > memory_order_relaxed); > + if (t->dwfl != NULL) > + INTUSE(dwfl_end) (t->dwfl); > + free(t); > + } > + free (tracker->dwfltab.table); > + > free (tracker); > } > diff --git a/libdwfl_stacktrace/dwflst_tracker_dwfltab.c > b/libdwfl_stacktrace/dwflst_tracker_dwfltab.c > new file mode 100644 > index 00000000..f4749e29 > --- /dev/null > +++ b/libdwfl_stacktrace/dwflst_tracker_dwfltab.c > @@ -0,0 +1,46 @@ > +/* Dwflst_Process_Tracker Dwfl 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 <libdwfl_stacktraceP.h> > + > +/* Definitions for the Dwfl table. */ > +#define TYPE dwflst_tracker_dwfl_info * > +#define NAME dwflst_tracker_dwfltab > +#define ITERATE 1 > +/* TODO(REVIEW): Omit reverse? */ > +#define REVERSE 1 My understanding of lib/dynamicsizehash.c is that REVERSE doesn't need to be defined if you do not require reverse iteration. I'm not sure if you do or not. > +#define COMPARE(a, b) \ > + ((a->invalid && b->invalid) || \ > + (!a->invalid && !b->invalid && \ > + (a)->dwfl->process->pid == (b)->dwfl->process->pid)) > + > +#include "../lib/dynamicsizehash_concurrent.c" > diff --git a/libdwfl_stacktrace/dwflst_tracker_dwfltab.h > b/libdwfl_stacktrace/dwflst_tracker_dwfltab.h > new file mode 100644 > index 00000000..fd687e61 > --- /dev/null > +++ b/libdwfl_stacktrace/dwflst_tracker_dwfltab.h > @@ -0,0 +1,42 @@ > +/* Dwflst_Process_Tracker Dwfl 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_DWFLTAB_H > +#define DWFLST_TRACKER_DWFLTAB_H 1 > + > +/* Definitions for the Dwfl table. */ > +#define TYPE dwflst_tracker_dwfl_info * > +#define NAME dwflst_tracker_dwfltab > +#define ITERATE 1 > +#define COMPARE(a, b) \ > + ((a->invalid && b->invalid) || \ > + (!a->invalid && !b->invalid && \ > + (a)->dwfl->process->pid == (b)->dwfl->process->pid)) > +#include <dynamicsizehash_concurrent.h> > + > +#endif > diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h > b/libdwfl_stacktrace/libdwfl_stacktraceP.h > index e457d35a..0bfc9c83 100644 > --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h > +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h > @@ -45,6 +45,14 @@ typedef struct > } dwflst_tracker_elf_info; > #include "dwflst_tracker_elftab.h" > > +/* Hash table for Dwfl *. */ > +typedef struct > +{ > + Dwfl *dwfl; > + bool invalid; /* Mark when the dwfl has been removed. */ > +} dwflst_tracker_dwfl_info; > +#include "dwflst_tracker_dwfltab.h" > + > struct Dwflst_Process_Tracker > { > const Dwfl_Callbacks *callbacks; > @@ -52,9 +60,24 @@ struct Dwflst_Process_Tracker > /* Table of cached Elf * including fd, path, fstat info. */ > dwflst_tracker_elftab elftab; > rwlock_define(, elftab_lock); > + > + /* Table of cached Dwfl * including pid. */ > + dwflst_tracker_dwfltab dwfltab; > + rwlock_define(, dwfltab_lock); > }; > > > +/* Called when dwfl->process->pid becomes known to add the dwfl to its > + Dwflst_Process_Tracker's dwfltab: */ > +extern void __libdwfl_stacktrace_add_dwfl_to_tracker (Dwfl *dwfl) > + internal_function; > + > +/* Called from dwfl_end() to remove the dwfl from its > + Dwfl_Process_Tracker's dwfltab: */ > +extern void __libdwfl_stacktrace_remove_dwfl_from_tracker (Dwfl *dwfl) > + internal_function; > + > + > /* Avoid PLT entries. */ > INTDECL (dwflst_module_gettracker) > INTDECL (dwflst_tracker_find_cached_elf) > -- > 2.47.0 > Aaron