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));
+  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
+#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

Reply via email to