On Thu, Apr 24, 2025 at 5:48 PM Serhei Makarov <ser...@serhei.io> wrote: > > Changes for v4: > > - Separate out libdwfl_stacktrace, as requested. > > * * * > > 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_stacktrace/libdwfl_stacktrace.h > (Dwflst_Process_Tracker): New struct. > (dwflst_tracker_begin): New function. > (dwflst_tracker_dwfl_begin): New function. > (dwflst_tracker_end): New function. > * libdw/libdw.map: Add new functions. > * libdwfl_stacktrace/libdwfl_stacktraceP.h > (struct Dwflst_Process_Tracker): New struct. > * libdwfl/libdwflP.h (Dwflst_Process_Tracker): Typedef forward decl. > (struct Dwfl): Add 'tracker' field. > * libdwfl_stacktrace/Makefile.am (libdwfl_stacktrace_a_SOURCES): > Add dwflst_process_tracker.c. > * libdwfl_stacktrace/dwflst_process_tracker.c: New file. > (dwflst_tracker_begin): Initialize the tracker. > (dwflst_tracker_dwfl_begin): Initialize Dwfl * with specified tracker. > (dwflst_tracker_end): Deallocate the tracker. > --- > libdw/libdw.map | 4 ++ > libdwfl/libdwflP.h | 4 ++ > libdwfl_stacktrace/Makefile.am | 3 +- > libdwfl_stacktrace/dwflst_process_tracker.c | 66 +++++++++++++++++++++ > libdwfl_stacktrace/libdwfl_stacktrace.h | 20 +++++++ > libdwfl_stacktrace/libdwfl_stacktraceP.h | 6 ++ > 6 files changed, 102 insertions(+), 1 deletion(-) > create mode 100644 libdwfl_stacktrace/dwflst_process_tracker.c > > diff --git a/libdw/libdw.map b/libdw/libdw.map > index 4f3fe6ba..fb69a62a 100644 > --- a/libdw/libdw.map > +++ b/libdw/libdw.map > @@ -396,4 +396,8 @@ ELFUTILS_0.193 { > ELFUTILS_0.193_EXPERIMENTAL { > global: > dwflst_perf_sample_preferred_regs_mask; > + dwflst_perf_sample_preferred_regs_mask; > + dwflst_tracker_begin; > + dwflst_tracker_dwfl_begin; > + dwflst_tracker_end; > }; > diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h > index 2b7eb8da..57305f81 100644 > --- a/libdwfl/libdwflP.h > +++ b/libdwfl/libdwflP.h > @@ -111,9 +111,13 @@ struct Dwfl_User_Core > int fd; /* close if >= 0. */ > }; > > +/* forward decl from ../libdwfl_stacktrace/ */ > +typedef struct Dwflst_Process_Tracker Dwflst_Process_Tracker; > + > struct Dwfl > { > const Dwfl_Callbacks *callbacks; > + Dwflst_Process_Tracker *tracker; > #ifdef ENABLE_LIBDEBUGINFOD > debuginfod_client *debuginfod; > #endif > diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am > index 6364c292..d57431c0 100644 > --- a/libdwfl_stacktrace/Makefile.am > +++ b/libdwfl_stacktrace/Makefile.am > @@ -40,7 +40,8 @@ noinst_LIBRARIES += libdwfl_stacktrace_pic.a > pkginclude_HEADERS = libdwfl_stacktrace.h > > > -libdwfl_stacktrace_a_SOURCES = dwflst_perf_frame.c > +libdwfl_stacktrace_a_SOURCES = dwflst_process_tracker.c \ > + dwflst_perf_frame.c > > libdwfl_stacktrace = $(libdw) > libdw = ../libdw/libdw.so > diff --git a/libdwfl_stacktrace/dwflst_process_tracker.c > b/libdwfl_stacktrace/dwflst_process_tracker.c > new file mode 100644 > index 00000000..057c9f7a > --- /dev/null > +++ b/libdwfl_stacktrace/dwflst_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 "libdwfl_stacktraceP.h" > + > +Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks > *callbacks) > +{ > + Dwflst_Process_Tracker *tracker = calloc (1, sizeof *tracker); > + if (tracker == NULL) > + { > + __libdwfl_seterrno (DWFL_E_NOMEM); > + return tracker; > + } > + > + tracker->callbacks = callbacks; > + return tracker; > +} > + > +Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) > +{ > + Dwfl *dwfl = INTUSE(dwfl_begin) (tracker->callbacks); > + if (dwfl == NULL) > + return dwfl; > + > + /* TODO: Could also share dwfl->debuginfod, but thread-safely? */ > + dwfl->tracker = tracker; > + return dwfl; > +} > + > +void dwflst_tracker_end (Dwflst_Process_Tracker *tracker) > +{ > + if (tracker == NULL) > + return; > + > + /* TODO: Call dwfl_end for each Dwfl connected to this tracker. */ > + free (tracker); > +} > diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h > b/libdwfl_stacktrace/libdwfl_stacktrace.h > index 564f504a..f3a82d18 100644 > --- a/libdwfl_stacktrace/libdwfl_stacktrace.h > +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h > @@ -41,6 +41,26 @@ > extern "C" { > #endif > > +/* Keeps track of and caches Elf structs across multiple libdwfl > + sessions corresponding to different processes. */ > +typedef struct Dwflst_Process_Tracker Dwflst_Process_Tracker; > + > + > +/* Initialize a new tracker for multiple libdwfl sessions. Since Elf > + data will shared between the libdwfl sessions, each Dwfl must use > + the same Dwfl_Callbacks CALLBACKS provided when the tracker is > + created. */ > +extern Dwflst_Process_Tracker *dwflst_tracker_begin (const Dwfl_Callbacks > *callbacks) > + __nonnull_attribute__ (1); > + > +/* Create a new Dwfl linked to this tracker. */ > +extern Dwfl *dwflst_tracker_dwfl_begin (Dwflst_Process_Tracker *tracker) > + __nonnull_attribute__ (1); > + > +/* End all sessions with this tracker. */ > +extern void dwflst_tracker_end (Dwflst_Process_Tracker *tracker); > + > + > /* XXX dwflst_perf_sample_getframes to be added in subsequent patch */ > > /* Returns the linux perf_events register mask describing a set of > diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h > b/libdwfl_stacktrace/libdwfl_stacktraceP.h > index fe6945fc..9313176c 100644 > --- a/libdwfl_stacktrace/libdwfl_stacktraceP.h > +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h > @@ -33,4 +33,10 @@ > > #include "libdwflP.h" > > +struct Dwflst_Process_Tracker > +{ > + const Dwfl_Callbacks *callbacks; > + /* ... */ > +}; > + > #endif /* libdwfl_stacktraceP.h */ > -- > 2.47.0 >
These parts of the Dwflst_Process_Tracker implementation LGTM. Aaron