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