Changes for v3: - Handle dwfl->process == NULL case in __libdwfl_remove_dwfl_from_tracker.
* * * The Dwfl_Process_Tracker also includes a dynamicsizehash cache which maps process ids to Dwfl * (or rather, dwfltracker_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/libdwflP.h (dwfltracker_dwfl_info): New typedef, provides indirection to allow a dwfltab entry to be invalidated. (struct Dwfl_Process_Tracker): add dwfltab. (__libdwfl_add_dwfl_to_tracker): New function. (__libdwfl_remove_dwfl_from_tracker): New function. * libdwfl/dwfl_process_tracker_dwfltab.c: New file, instantiates lib/dynamicsizehash_concurrent.c to store dwfltracker_dwfl_info structs. * libdwfl/dwfl_process_tracker_dwfltab.h: New file, ditto. * libdwfl/dwfl_process_tracker.c (dwfl_process_tracker_begin): Init dwfltab. (__libdwfl_add_dwfl_to_tracker): New function; add dwfl to dwfltab. (__libdwfl_remove_dwfl_from_tracker): New function; invalidate dwfl entry, since dynamicsizehash doesn't support outright deletion. (dwfl_process_tracker_end): Clean up dwfltab. Lock and iterate the table to free tracker->dwfltab.table items. * libdwfl/dwfl_frame.c (dwfl_attach_state): Call __libdwfl_add_dwfl_to_tracker. * libdwfl/dwfl_end.c (dwfl_end): Call __libdwfl_remove_dwfl_from_tracker. * libdwfl/Makefile.am (libdwfl_a_SOURCES): Add dwfl_process_tracker_dwfltab.c. (noinst_HEADERS): Add dwfl_process_tracker_dwfltab.h. --- libdwfl/Makefile.am | 3 +- libdwfl/dwfl_end.c | 5 +- libdwfl/dwfl_frame.c | 6 +- libdwfl/dwfl_process_tracker.c | 81 +++++++++++++++++++++++++- libdwfl/dwfl_process_tracker_dwfltab.c | 46 +++++++++++++++ libdwfl/dwfl_process_tracker_dwfltab.h | 42 +++++++++++++ libdwfl/libdwflP.h | 19 ++++++ 7 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 libdwfl/dwfl_process_tracker_dwfltab.c create mode 100644 libdwfl/dwfl_process_tracker_dwfltab.h diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index dd99db46..11886742 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -73,6 +73,7 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c \ dwfl_process_tracker.c dwfl_process_tracker_find_elf.c \ dwfl_process_tracker_elftab.c libdwfl_next_prime.c \ + dwfl_process_tracker_dwfltab.c \ dwfl_perf_frame.c \ gzip.c debuginfod-client.c @@ -95,7 +96,7 @@ libeu = ../lib/libeu.a libdwfl_pic_a_SOURCES = am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os) -noinst_HEADERS = libdwflP.h dwfl_process_tracker_elftab.h +noinst_HEADERS = libdwflP.h dwfl_process_tracker_elftab.h dwfl_process_tracker_dwfltab.h EXTRA_libdwfl_a_DEPENDENCIES = libdwfl.manifest diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c index 7b5ac8a1..a9e7983c 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 @@ -42,6 +42,9 @@ dwfl_end (Dwfl *dwfl) __libdwfl_debuginfod_end (dwfl->debuginfod); #endif + if (dwfl->tracker != NULL) + __libdwfl_remove_dwfl_from_tracker (dwfl); + if (dwfl->process) __libdwfl_process_free (dwfl->process); diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c index 2e6c6de8..0ce9b8a8 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 @@ -206,6 +206,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_add_dwfl_to_tracker (dwfl); + return true; } INTDEF(dwfl_attach_state) diff --git a/libdwfl/dwfl_process_tracker.c b/libdwfl/dwfl_process_tracker.c index 24ee2da7..200b622b 100644 --- a/libdwfl/dwfl_process_tracker.c +++ b/libdwfl/dwfl_process_tracker.c @@ -45,6 +45,8 @@ Dwfl_Process_Tracker *dwfl_process_tracker_begin (const Dwfl_Callbacks *callback dwfltracker_elftab_init (&tracker->elftab, HTAB_DEFAULT_SIZE); rwlock_init (tracker->elftab_lock); + dwfltracker_dwfltab_init (&tracker->dwfltab, HTAB_DEFAULT_SIZE); + rwlock_init (tracker->dwfltab_lock); tracker->callbacks = callbacks; return tracker; @@ -58,19 +60,79 @@ Dwfl *dwfl_begin_with_tracker (Dwfl_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 __libdwfl_add_dwfl_to_tracker (Dwfl *dwfl) { + Dwfl_Process_Tracker *tracker = dwfl->tracker; + assert (tracker != NULL); + + /* First try to find an existing entry to replace: */ + dwfltracker_dwfl_info *ent = NULL; + unsigned long int hval = dwfl->process->pid; + + rwlock_wrlock (tracker->dwfltab_lock); + ent = dwfltracker_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(dwfltracker_dwfl_info)); + ent->dwfl = dwfl; + ent->invalid = false; + if (dwfltracker_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 __libdwfl_remove_dwfl_from_tracker (Dwfl *dwfl) { + if (dwfl->tracker == NULL) + return; + Dwfl_Process_Tracker *tracker = dwfl->tracker; + dwfltracker_dwfl_info *ent = NULL; + if (dwfl->process == NULL) + return; + unsigned long int hval = dwfl->process->pid; + + rwlock_wrlock (tracker->dwfltab_lock); + ent = dwfltracker_dwfltab_find(&tracker->dwfltab, hval); + if (ent != NULL && ent->dwfl == dwfl) + { + ent->dwfl = NULL; + ent->invalid = true; + } + rwlock_unlock (tracker->dwfltab_lock); +} + void dwfl_process_tracker_end (Dwfl_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++) { dwfltracker_elftab_ent *ent = &tracker->elftab.table[idx]; if (ent->hashval == 0) @@ -86,6 +148,21 @@ void dwfl_process_tracker_end (Dwfl_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++) + { + dwfltracker_dwfltab_ent *ent = &tracker->dwfltab.table[idx]; + if (ent->hashval == 0) + continue; + dwfltracker_dwfl_info *t = (dwfltracker_dwfl_info *) atomic_load_explicit (&ent->val_ptr, + memory_order_relaxed); + if (t->dwfl != NULL) + dwfl_end(t->dwfl); + free(t); + } + free (tracker->dwfltab.table); + free (tracker); } diff --git a/libdwfl/dwfl_process_tracker_dwfltab.c b/libdwfl/dwfl_process_tracker_dwfltab.c new file mode 100644 index 00000000..0a10f13c --- /dev/null +++ b/libdwfl/dwfl_process_tracker_dwfltab.c @@ -0,0 +1,46 @@ +/* Dwfl_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 <libdwflP.h> + +/* Definitions for the Dwfl table. */ +#define TYPE dwfltracker_dwfl_info * +#define NAME dwfltracker_dwfltab +#define ITERATE 1 +/* TODO(REVIEW): Omit reverse? */ +#define REVERSE 1 +#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/dwfl_process_tracker_dwfltab.h b/libdwfl/dwfl_process_tracker_dwfltab.h new file mode 100644 index 00000000..de559a57 --- /dev/null +++ b/libdwfl/dwfl_process_tracker_dwfltab.h @@ -0,0 +1,42 @@ +/* Dwfl_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 DWFL_PROCESS_TRACKER_DWFLTAB_H +#define DWFL_PROCESS_TRACKER_DWFLTAB_H 1 + +/* Definitions for the Dwfl table. */ +#define TYPE dwfltracker_dwfl_info * +#define NAME dwfltracker_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/libdwflP.h b/libdwfl/libdwflP.h index 2c6b1669..57b0ed27 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -113,6 +113,14 @@ typedef struct } dwfltracker_elf_info; #include "dwfl_process_tracker_elftab.h" +/* Hash table for Dwfl *. */ +typedef struct +{ + Dwfl *dwfl; + bool invalid; /* Mark when the dwfl has been removed. */ +} dwfltracker_dwfl_info; +#include "dwfl_process_tracker_dwfltab.h" + struct Dwfl_Process_Tracker { const Dwfl_Callbacks *callbacks; @@ -120,8 +128,19 @@ struct Dwfl_Process_Tracker /* Table of cached Elf * including fd, path, fstat info. */ dwfltracker_elftab elftab; rwlock_define(, elftab_lock); + + /* Table of cached Dwfl * including pid. */ + dwfltracker_dwfltab dwfltab; + rwlock_define(, dwfltab_lock); }; +/* Call when dwfl->process->pid becomes known to add the dwfl to its + Dwfl_Process_Tracker's dwfltab: */ +void __libdwfl_add_dwfl_to_tracker (Dwfl *dwfl); + +/* Call from dwfl_end() to remove the dwfl from its + Dwfl_Process_Tracker's dwfltab: */ +void __libdwfl_remove_dwfl_from_tracker (Dwfl *dwfl); /* Resources we might keep for the user about the core file that the Dwfl might have been created from. Can currently only be set -- 2.47.0