New data structure to coordinate caching Elf data among multiple Dwfl
structs attached to different processes. Meant to reduce the overhead
for profilers that use elfutils for unwinding.

The caching is well-justified, as the prior approach (e.g. in
eu-stacktrace, sysprof-live-unwinder) of creating a separate Dwfl per
process was wildly redundant, opening ~hundreds of file descriptors
just for each common library such as libc.so and leaking the data.

Initial patch just introduces the struct, to be filled in by the rest
of the patch series.

* libdwfl/libdwfl.h (Dwfl_Process_Tracker): New struct.
  (dwfl_process_tracker_begin): New function.
  (dwfl_begin_with_tracker): New function.
  (dwfl_process_tracker_end): New function.
* libdw/libdw.map: Add new functions.
* libdwfl/libdwflP.h (struct Dwfl_Process_Tracker): New struct.
  (struct Dwfl): Add 'tracker' field.
* libdwfl/Makefile.am (libdwfl_a_SOURCES): Add dwfl_process_tracker.c.
* libdwfl/dwfl_process_tracker.c: New file.
  (dwfl_process_tracker_begin): Initialize the tracker.
  (dwfl_begin_with_tracker): Initialize Dwfl * with specified tracker.
  (dwfl_process_tracker_end): Deallocate the tracker.
---
 libdw/libdw.map                |  3 ++
 libdwfl/Makefile.am            |  3 +-
 libdwfl/dwfl_process_tracker.c | 66 ++++++++++++++++++++++++++++++++++
 libdwfl/libdwfl.h              | 17 ++++++++-
 libdwfl/libdwflP.h             |  7 ++++
 5 files changed, 94 insertions(+), 2 deletions(-)
 create mode 100644 libdwfl/dwfl_process_tracker.c

diff --git a/libdw/libdw.map b/libdw/libdw.map
index d3376b23..a34ba339 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -391,4 +391,7 @@ ELFUTILS_0.193 {
     dwarf_language;
     dwarf_language_lower_bound;
     dwfl_perf_sample_preferred_regs_mask;
+    dwfl_process_tracker_begin;
+    dwfl_begin_with_tracker;
+    dwfl_process_tracker_end;
 } ELFUTILS_0.192;
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
index 37c57bee..b41122e3 100644
--- a/libdwfl/Makefile.am
+++ b/libdwfl/Makefile.am
@@ -71,7 +71,8 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c 
dwfl_version.c \
                    link_map.c core-file.c open.c image-header.c \
                    dwfl_frame.c frame_unwind.c dwfl_frame_pc.c \
                    linux-pid-attach.c linux-core-attach.c dwfl_frame_regs.c \
-                   dwfl_perf_frame.c \
+                   dwfl_process_tracker.c \
+            dwfl_perf_frame.c \
                    gzip.c debuginfod-client.c
 
 if BZLIB
diff --git a/libdwfl/dwfl_process_tracker.c b/libdwfl/dwfl_process_tracker.c
new file mode 100644
index 00000000..c42d8ad2
--- /dev/null
+++ b/libdwfl/dwfl_process_tracker.c
@@ -0,0 +1,66 @@
+/* Track multiple Dwfl structs for multiple processes.
+   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"
+
+Dwfl_Process_Tracker *dwfl_process_tracker_begin (const Dwfl_Callbacks 
*callbacks)
+{
+  Dwfl_Process_Tracker *tracker = calloc (1, sizeof *tracker);
+  if (tracker == NULL)
+    {
+      __libdwfl_seterrno (DWFL_E_NOMEM);
+      return tracker;
+    }
+
+  tracker->callbacks = callbacks;
+  return tracker;
+}
+
+Dwfl *dwfl_begin_with_tracker (Dwfl_Process_Tracker *tracker)
+{
+  Dwfl *dwfl = dwfl_begin (tracker->callbacks);
+  if (dwfl == NULL)
+    return dwfl;
+
+  /* TODO: Could also share dwfl->debuginfod, but thread-safely? */
+  dwfl->tracker = tracker;
+  return dwfl;
+}
+
+void dwfl_process_tracker_end (Dwfl_Process_Tracker *tracker)
+{
+  if (tracker == NULL)
+    return;
+
+  /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */
+  free (tracker);
+}
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
index 967feb42..0db5b74d 100644
--- a/libdwfl/libdwfl.h
+++ b/libdwfl/libdwfl.h
@@ -32,9 +32,12 @@
 #include "libdw.h"
 #include <stdio.h>
 
-/* Handle for a session using the library.  */
+/* Handle for a session using the library to attach to a single target 
process.  */
 typedef struct Dwfl Dwfl;
 
+/* Handle for a session using the library to attach to more than one process.  
*/
+typedef struct Dwfl_Process_Tracker Dwfl_Process_Tracker;
+
 /* Handle for a module.  */
 typedef struct Dwfl_Module Dwfl_Module;
 
@@ -122,6 +125,18 @@ extern int dwfl_errno (void);
 extern const char *dwfl_errmsg (int err);
 
 
+/* Start a new multi-process session with the library.  */
+extern Dwfl_Process_Tracker *dwfl_process_tracker_begin (const Dwfl_Callbacks 
*callbacks)
+  __nonnull_attribute__ (1);
+
+/* Create a new Dwfl within a multi-process session.  */
+extern Dwfl *dwfl_begin_with_tracker (Dwfl_Process_Tracker *tracker)
+  __nonnull_attribute__ (1);
+
+/* End a multi-process session.  */
+extern void dwfl_process_tracker_end (Dwfl_Process_Tracker *tracker);
+
+
 /* Start reporting the current set of segments and modules to the library.
    All existing segments are wiped.  Existing modules are marked to be
    deleted, and will not be found via dwfl_addrmodule et al if they are not
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 2b7eb8da..885acae0 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -101,6 +101,12 @@ typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
 extern int __libdwfl_canon_error (Dwfl_Error) internal_function;
 extern void __libdwfl_seterrno (Dwfl_Error) internal_function;
 
+struct Dwfl_Process_Tracker
+{
+  const Dwfl_Callbacks *callbacks;
+  /* ... */
+};
+
 /* Resources we might keep for the user about the core file that the
    Dwfl might have been created from.  Can currently only be set
    through std-argp.  */
@@ -114,6 +120,7 @@ struct Dwfl_User_Core
 struct Dwfl
 {
   const Dwfl_Callbacks *callbacks;
+  Dwfl_Process_Tracker *tracker;
 #ifdef ENABLE_LIBDEBUGINFOD
   debuginfod_client *debuginfod;
 #endif
-- 
2.47.0

Reply via email to