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

Reply via email to