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

Reply via email to