[v5,04/12] libdwfl_stacktrace [4/12]: intro library, add dwflst_perf_sample_preferred_regs_mask

Message ID 20250424214715.306147-4-serhei@serhei.io
State Superseded
Headers
Series [v5,01/12] libebl [1/12]: api for perf register handling, start with x86_64 |

Commit Message

Serhei Makarov April 24, 2025, 9:47 p.m. UTC
  Changes for v5:

- Separate ELFUTILS_0.193_EXPERIMENTAL namespace, mark library experimental.

Changes for v4:

- Separate out libdwfl_stacktrace, as requested.

Changes for v2:

- guard the linux/perf_events.h include properly with an #if defined __linux__

* * *

Subsequent patches in the series introduce a new library for
tracking/caching Elf structs across multiple processes and wrapping
the libebl perf register handling (since libebl is a private
interface).

In this patch, add the library and an interface to access the set of
registers that libdwfl needs to allow proper unwinding of stack sample
data.  (Unwinding with a smaller set of registers can be attempted,
but will yield a truncated call chain in most cases.)

(Accessing the set of registers feels like an implementation detail,
but the profiler invoking perf_event_open and elfutils unwinding code
need to agree on the exact set of registers being requested.  So it's
best for the profiler to ask elfutils for this detail.)

* libdwfl_stacktrace/Makefile.am: New file.
* libdwfl_stacktrace/libdwfl_stacktrace.h: New file, library for
  tracking/caching Elf structs and unwinding across multiple
  processes.
* libdwfl_stacktrace/libdwfl_stacktraceP.h: New file.
* libdwfl_stacktrace/dwflst_perf_frame.c: New file.
  (dwflst_perf_sample_preferred_regs_mask): New function.
* libdw/libdw.map: Add dwflst_perf_sample_preferred_regs_mask.
* NEWS: Add NEWS item about libdwfl_stacktrace.
* configure.ac: Add libdwfl_stacktrace/Makefile.
* Makefile.am (SUBDIRS): Add libdwfl_stacktrace.
* libdw/Makefile.am (libdw_so_LIBS): Add libdwfl_stacktrace.
  (libdwfl_stacktrace_objects): Add libdwfl_stacktrace.manifest.
  (libdw_a_LIBADD): Add libdwfl_stacktrace_objects.
* config/elfutils.spec.in (%files devel): Add libdwfl_stacktrace.h.
---
 Makefile.am                              |  6 +--
 NEWS                                     |  6 +++
 config/elfutils.spec.in                  |  1 +
 configure.ac                             |  3 ++
 libdw/Makefile.am                        |  7 ++-
 libdw/libdw.map                          |  6 +++
 libdwfl_stacktrace/Makefile.am           | 63 ++++++++++++++++++++++++
 libdwfl_stacktrace/dwflst_perf_frame.c   | 63 ++++++++++++++++++++++++
 libdwfl_stacktrace/libdwfl_stacktrace.h  | 58 ++++++++++++++++++++++
 libdwfl_stacktrace/libdwfl_stacktraceP.h | 36 ++++++++++++++
 10 files changed, 244 insertions(+), 5 deletions(-)
 create mode 100644 libdwfl_stacktrace/Makefile.am
 create mode 100644 libdwfl_stacktrace/dwflst_perf_frame.c
 create mode 100644 libdwfl_stacktrace/libdwfl_stacktrace.h
 create mode 100644 libdwfl_stacktrace/libdwfl_stacktraceP.h
  

Comments

Aaron Merey April 25, 2025, 5:54 a.m. UTC | #1
On Thu, Apr 24, 2025 at 5:50 PM Serhei Makarov <serhei@serhei.io> wrote:
>
> Changes for v5:
>
> - Separate ELFUTILS_0.193_EXPERIMENTAL namespace, mark library experimental.
>
> Changes for v4:
>
> - Separate out libdwfl_stacktrace, as requested.
>
> Changes for v2:
>
> - guard the linux/perf_events.h include properly with an #if defined __linux__
>
> * * *
>
> Subsequent patches in the series introduce a new library for
> tracking/caching Elf structs across multiple processes and wrapping
> the libebl perf register handling (since libebl is a private
> interface).
>
> In this patch, add the library and an interface to access the set of
> registers that libdwfl needs to allow proper unwinding of stack sample
> data.  (Unwinding with a smaller set of registers can be attempted,
> but will yield a truncated call chain in most cases.)
>
> (Accessing the set of registers feels like an implementation detail,
> but the profiler invoking perf_event_open and elfutils unwinding code
> need to agree on the exact set of registers being requested.  So it's
> best for the profiler to ask elfutils for this detail.)
>
> * libdwfl_stacktrace/Makefile.am: New file.
> * libdwfl_stacktrace/libdwfl_stacktrace.h: New file, library for
>   tracking/caching Elf structs and unwinding across multiple
>   processes.
> * libdwfl_stacktrace/libdwfl_stacktraceP.h: New file.
> * libdwfl_stacktrace/dwflst_perf_frame.c: New file.
>   (dwflst_perf_sample_preferred_regs_mask): New function.
> * libdw/libdw.map: Add dwflst_perf_sample_preferred_regs_mask.
> * NEWS: Add NEWS item about libdwfl_stacktrace.
> * configure.ac: Add libdwfl_stacktrace/Makefile.
> * Makefile.am (SUBDIRS): Add libdwfl_stacktrace.
> * libdw/Makefile.am (libdw_so_LIBS): Add libdwfl_stacktrace.
>   (libdwfl_stacktrace_objects): Add libdwfl_stacktrace.manifest.
>   (libdw_a_LIBADD): Add libdwfl_stacktrace_objects.
> * config/elfutils.spec.in (%files devel): Add libdwfl_stacktrace.h.
> ---
>  Makefile.am                              |  6 +--
>  NEWS                                     |  6 +++
>  config/elfutils.spec.in                  |  1 +
>  configure.ac                             |  3 ++
>  libdw/Makefile.am                        |  7 ++-
>  libdw/libdw.map                          |  6 +++
>  libdwfl_stacktrace/Makefile.am           | 63 ++++++++++++++++++++++++
>  libdwfl_stacktrace/dwflst_perf_frame.c   | 63 ++++++++++++++++++++++++
>  libdwfl_stacktrace/libdwfl_stacktrace.h  | 58 ++++++++++++++++++++++
>  libdwfl_stacktrace/libdwfl_stacktraceP.h | 36 ++++++++++++++
>  10 files changed, 244 insertions(+), 5 deletions(-)
>  create mode 100644 libdwfl_stacktrace/Makefile.am
>  create mode 100644 libdwfl_stacktrace/dwflst_perf_frame.c
>  create mode 100644 libdwfl_stacktrace/libdwfl_stacktrace.h
>  create mode 100644 libdwfl_stacktrace/libdwfl_stacktraceP.h
>
> diff --git a/Makefile.am b/Makefile.am
> index 3a181d75..76e98f60 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -1,7 +1,7 @@
>  ## Process this file with automake to create Makefile.in
>  ## Configure input file for elfutils.
>  ##
> -## Copyright (C) 1996-2006, 2008, 2009, 2015 Red Hat, Inc.
> +## Copyright (C) 1996-2006, 2008, 2009, 2015, 2025 Red Hat, Inc.
>  ##
>  ## This file is part of elfutils.
>  ##
> @@ -28,8 +28,8 @@ AM_MAKEFLAGS = --no-print-directory
>
>  pkginclude_HEADERS = version.h
>
> -SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl libdw \
> -         libasm debuginfod src po doc tests
> +SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl \
> +         libdwfl_stacktrace libdw libasm debuginfod src po doc tests
>
>  EXTRA_DIST = elfutils.spec GPG-KEY NOTES CONTRIBUTING SECURITY \
>              COPYING COPYING-GPLV2 COPYING-LGPLV3 CONDUCT
> diff --git a/NEWS b/NEWS
> index 664c125b..c3c611e3 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -4,6 +4,12 @@ debuginfod: Add CORS (webapp access) support to webapi.
>
>  libdw: Add dwarf_language and dwarf_language_lower_bound functions.
>
> +libdwfl_stacktrace: Experimental new library interface for unwinding
> +                    stack samples into call chains, and tracking and
> +                    caching Elf data for multiple processes, building
> +                    on libdwfl.  Initially supports perf_events stack
> +                    sample data.
> +
>  Version 0.192 "New rules, faster tools"
>
>  CONDUCT: A new code of conduct has been adopted.  See the
> diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in
> index 96934514..37077365 100644
> --- a/config/elfutils.spec.in
> +++ b/config/elfutils.spec.in
> @@ -300,6 +300,7 @@ fi
>  #%%{_includedir}/elfutils/libasm.h
>  %{_includedir}/elfutils/libdw.h
>  %{_includedir}/elfutils/libdwfl.h
> +%{_includedir}/elfutils/libdwfl_stacktrace.h
>  %{_includedir}/elfutils/libdwelf.h
>  %{_includedir}/elfutils/version.h
>  #%%{_libdir}/libasm.so
> diff --git a/configure.ac b/configure.ac
> index 27488e3f..92108b7f 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -700,6 +700,9 @@ AC_CONFIG_FILES([libdw/Makefile])
>  dnl Higher-level DWARF support library.
>  AC_CONFIG_FILES([libdwfl/Makefile])
>
> +dnl Higher-level DWARF stacktrace support library
> +AC_CONFIG_FILES([libdwfl_stacktrace/Makefile])
> +
>  dnl CPU handling library.
>  AC_CONFIG_FILES([libcpu/Makefile])
>
> diff --git a/libdw/Makefile.am b/libdw/Makefile.am
> index 9dadc19b..f024d652 100644
> --- a/libdw/Makefile.am
> +++ b/libdw/Makefile.am
> @@ -1,6 +1,6 @@
>  ## Process this file with automake to create Makefile.in
>  ##
> -## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018 Red Hat, Inc.
> +## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018, 2025 Red Hat, Inc.
>  ## This file is part of elfutils.
>  ##
>  ## This file is free software; you can redistribute it and/or modify
> @@ -107,7 +107,7 @@ am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os)
>
>  libdw_so_LIBS = ../libebl/libebl_pic.a ../backends/libebl_backends_pic.a \
>                 ../libcpu/libcpu_pic.a libdw_pic.a ../libdwelf/libdwelf_pic.a \
> -               ../libdwfl/libdwfl_pic.a
> +               ../libdwfl/libdwfl_pic.a ../libdwfl_stacktrace/libdwfl_stacktrace_pic.a
>  libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so
>  libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(fts_LIBS) $(obstack_LIBS) $(zip_LIBS) -pthread
>  libdw.so: $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS)
> @@ -135,6 +135,9 @@ uninstall: uninstall-am
>  libdwfl_objects = $(shell cat ../libdwfl/libdwfl.manifest)
>  libdw_a_LIBADD = $(addprefix ../libdwfl/,$(libdwfl_objects))
>
> +libdwfl_stacktrace_objects = $(shell cat ../libdwfl_stacktrace/libdwfl_stacktrace.manifest)
> +libdw_a_LIBADD += $(addprefix ../libdwfl_stacktrace/,$(libdwfl_stacktrace_objects))
> +
>  libdwelf_objects = $(shell cat ../libdwelf/libdwelf.manifest)
>  libdw_a_LIBADD += $(addprefix ../libdwelf/,$(libdwelf_objects))
>
> diff --git a/libdw/libdw.map b/libdw/libdw.map
> index e7baf3c4..4f3fe6ba 100644
> --- a/libdw/libdw.map
> +++ b/libdw/libdw.map
> @@ -391,3 +391,9 @@ ELFUTILS_0.193 {
>      dwarf_language;
>      dwarf_language_lower_bound;
>  } ELFUTILS_0.192;
> +
> +/* XXX Experimental libdwfl_stacktrace API. */
> +ELFUTILS_0.193_EXPERIMENTAL {
> +  global:
> +    dwflst_perf_sample_preferred_regs_mask;
> +};
> diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am
> new file mode 100644
> index 00000000..6364c292
> --- /dev/null
> +++ b/libdwfl_stacktrace/Makefile.am
> @@ -0,0 +1,63 @@
> +## Makefile.am for libdwfl_stacktrace library subdirectory in elfutils.
> +##
> +## Process this file with automake to create Makefile.in
> +##
> +## 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/>.
> +##
> +include $(top_srcdir)/config/eu.am
> +AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
> +          -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(srcdir)/../libdwfl
> +VERSION = 1
> +
> +noinst_LIBRARIES = libdwfl_stacktrace.a
> +noinst_LIBRARIES += libdwfl_stacktrace_pic.a
> +
> +pkginclude_HEADERS = libdwfl_stacktrace.h
> +
> +
> +libdwfl_stacktrace_a_SOURCES = dwflst_perf_frame.c
> +
> +libdwfl_stacktrace = $(libdw)
> +libdw = ../libdw/libdw.so
> +libelf = ../libelf/libelf.so
> +libebl = ../libebl/libebl.a
> +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
> +
> +EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest
> +
> +libdwfl_stacktrace.manifest: $(libdwfl_stacktrace_a_OBJECTS)
> +       $(AM_V_GEN)echo $^ > $@
> +
> +MOSTLYCLEANFILES = $(am_libdwfl_stacktrace_a_OBJECTS) \
> +       $(am_libdwfl_stacktrace_pic_a_OBJECTS)
> +CLEANFILES = $(EXTRA_libdwfl_stacktrace_a_DEPENDENCIES)
> diff --git a/libdwfl_stacktrace/dwflst_perf_frame.c b/libdwfl_stacktrace/dwflst_perf_frame.c
> new file mode 100644
> index 00000000..79e8e482
> --- /dev/null
> +++ b/libdwfl_stacktrace/dwflst_perf_frame.c
> @@ -0,0 +1,63 @@
> +/* Get Dwarf Frame state for perf stack sample data.
> +   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
> +
> +#if defined(__linux__)
> +# include <linux/perf_event.h>
> +#endif
> +
> +#include "libdwfl_stacktraceP.h"
> +
> +Ebl *default_ebl = NULL;
> +GElf_Half default_ebl_machine = EM_NONE;
> +
> +uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
> +{
> +  /* XXX The most likely case is that this will only be called once,
> +     for the current architecture.  So we keep one Ebl* around for
> +     answering this query and replace it in the unlikely case of
> +     getting called with different architectures.  */
> +  if (default_ebl != NULL && default_ebl_machine != machine)
> +    {
> +      ebl_closebackend(default_ebl);
> +      default_ebl = NULL;
> +    }
> +  if (default_ebl == NULL)
> +    {
> +      default_ebl = ebl_openbackend_machine(machine);
> +      default_ebl_machine = machine;
> +    }
> +  if (default_ebl != NULL)
> +    return ebl_perf_frame_regs_mask (default_ebl);
> +  return 0;
> +}
> +
> +/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */
> diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h
> new file mode 100644
> index 00000000..564f504a
> --- /dev/null
> +++ b/libdwfl_stacktrace/libdwfl_stacktrace.h
> @@ -0,0 +1,58 @@
> +/* Interfaces for libdwfl_stacktrace.
> +
> +   XXX: This is an experimental initial version of the API, and is
> +   liable to change in future releases of elfutils, especially as
> +   we figure out how to generalize the work to other sample data
> +   formats in addition to perf_events.
> +
> +   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 _LIBDWFL_STACKTRACE_H
> +#define _LIBDWFL_STACKTRACE_H  1
> +
> +#include "libdwfl.h"
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */
> +
> +/* Returns the linux perf_events register mask describing a set of
> +   registers sufficient for unwinding on MACHINE, or 0 if libdwfl does
> +   not handle perf_events samples for MACHINE.  Does not take a Dwfl*
> +   or Elf* since this is meant to allow a profiling tool to configure
> +   perf_events to produce meaningful data for a libdwfl session to be
> +   opened later.  */
> +uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine);
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif  /* libdwfl_stacktrace.h */
> diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h
> new file mode 100644
> index 00000000..fe6945fc
> --- /dev/null
> +++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h
> @@ -0,0 +1,36 @@
> +/* Internal definitions for libdwfl_stacktrace.
> +   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 _LIBDWFL_STACKTRACEP_H
> +#define _LIBDWFL_STACKTRACEP_H 1
> +
> +#include <libdwfl_stacktrace.h>
> +
> +#include "libdwflP.h"
> +
> +#endif  /* libdwfl_stacktraceP.h */
> --
> 2.47.0
>

Thanks for separating the new API into its own "experimental"
namespace in libdw.map. libdwfl_stacktraceP.h cannot be installed and
it isn't included by any public header.  libdw.map, NEWS and
libdwfl_stacktrace.h all clearly mention the experimental nature of
the new API.  LGTM.

Aaron
  

Patch

diff --git a/Makefile.am b/Makefile.am
index 3a181d75..76e98f60 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@ 
 ## Process this file with automake to create Makefile.in
 ## Configure input file for elfutils.
 ##
-## Copyright (C) 1996-2006, 2008, 2009, 2015 Red Hat, Inc.
+## Copyright (C) 1996-2006, 2008, 2009, 2015, 2025 Red Hat, Inc.
 ##
 ## This file is part of elfutils.
 ##
@@ -28,8 +28,8 @@  AM_MAKEFLAGS = --no-print-directory
 
 pkginclude_HEADERS = version.h
 
-SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl libdw \
-	  libasm debuginfod src po doc tests
+SUBDIRS = config lib libelf libcpu backends libebl libdwelf libdwfl \
+	  libdwfl_stacktrace libdw libasm debuginfod src po doc tests
 
 EXTRA_DIST = elfutils.spec GPG-KEY NOTES CONTRIBUTING SECURITY \
 	     COPYING COPYING-GPLV2 COPYING-LGPLV3 CONDUCT
diff --git a/NEWS b/NEWS
index 664c125b..c3c611e3 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,12 @@  debuginfod: Add CORS (webapp access) support to webapi.
 
 libdw: Add dwarf_language and dwarf_language_lower_bound functions.
 
+libdwfl_stacktrace: Experimental new library interface for unwinding
+                    stack samples into call chains, and tracking and
+                    caching Elf data for multiple processes, building
+                    on libdwfl.  Initially supports perf_events stack
+                    sample data.
+
 Version 0.192 "New rules, faster tools"
 
 CONDUCT: A new code of conduct has been adopted.  See the
diff --git a/config/elfutils.spec.in b/config/elfutils.spec.in
index 96934514..37077365 100644
--- a/config/elfutils.spec.in
+++ b/config/elfutils.spec.in
@@ -300,6 +300,7 @@  fi
 #%%{_includedir}/elfutils/libasm.h
 %{_includedir}/elfutils/libdw.h
 %{_includedir}/elfutils/libdwfl.h
+%{_includedir}/elfutils/libdwfl_stacktrace.h
 %{_includedir}/elfutils/libdwelf.h
 %{_includedir}/elfutils/version.h
 #%%{_libdir}/libasm.so
diff --git a/configure.ac b/configure.ac
index 27488e3f..92108b7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -700,6 +700,9 @@  AC_CONFIG_FILES([libdw/Makefile])
 dnl Higher-level DWARF support library.
 AC_CONFIG_FILES([libdwfl/Makefile])
 
+dnl Higher-level DWARF stacktrace support library
+AC_CONFIG_FILES([libdwfl_stacktrace/Makefile])
+
 dnl CPU handling library.
 AC_CONFIG_FILES([libcpu/Makefile])
 
diff --git a/libdw/Makefile.am b/libdw/Makefile.am
index 9dadc19b..f024d652 100644
--- a/libdw/Makefile.am
+++ b/libdw/Makefile.am
@@ -1,6 +1,6 @@ 
 ## Process this file with automake to create Makefile.in
 ##
-## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018 Red Hat, Inc.
+## Copyright (C) 2002-2010, 2012, 2014, 2016, 2018, 2025 Red Hat, Inc.
 ## This file is part of elfutils.
 ##
 ## This file is free software; you can redistribute it and/or modify
@@ -107,7 +107,7 @@  am_libdw_pic_a_OBJECTS = $(libdw_a_SOURCES:.c=.os)
 
 libdw_so_LIBS = ../libebl/libebl_pic.a ../backends/libebl_backends_pic.a \
 		../libcpu/libcpu_pic.a libdw_pic.a ../libdwelf/libdwelf_pic.a \
-		../libdwfl/libdwfl_pic.a
+		../libdwfl/libdwfl_pic.a ../libdwfl_stacktrace/libdwfl_stacktrace_pic.a
 libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so
 libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(fts_LIBS) $(obstack_LIBS) $(zip_LIBS) -pthread
 libdw.so: $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS)
@@ -135,6 +135,9 @@  uninstall: uninstall-am
 libdwfl_objects = $(shell cat ../libdwfl/libdwfl.manifest)
 libdw_a_LIBADD = $(addprefix ../libdwfl/,$(libdwfl_objects))
 
+libdwfl_stacktrace_objects = $(shell cat ../libdwfl_stacktrace/libdwfl_stacktrace.manifest)
+libdw_a_LIBADD += $(addprefix ../libdwfl_stacktrace/,$(libdwfl_stacktrace_objects))
+
 libdwelf_objects = $(shell cat ../libdwelf/libdwelf.manifest)
 libdw_a_LIBADD += $(addprefix ../libdwelf/,$(libdwelf_objects))
 
diff --git a/libdw/libdw.map b/libdw/libdw.map
index e7baf3c4..4f3fe6ba 100644
--- a/libdw/libdw.map
+++ b/libdw/libdw.map
@@ -391,3 +391,9 @@  ELFUTILS_0.193 {
     dwarf_language;
     dwarf_language_lower_bound;
 } ELFUTILS_0.192;
+
+/* XXX Experimental libdwfl_stacktrace API. */
+ELFUTILS_0.193_EXPERIMENTAL {
+  global:
+    dwflst_perf_sample_preferred_regs_mask;
+};
diff --git a/libdwfl_stacktrace/Makefile.am b/libdwfl_stacktrace/Makefile.am
new file mode 100644
index 00000000..6364c292
--- /dev/null
+++ b/libdwfl_stacktrace/Makefile.am
@@ -0,0 +1,63 @@ 
+## Makefile.am for libdwfl_stacktrace library subdirectory in elfutils.
+##
+## Process this file with automake to create Makefile.in
+##
+## 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/>.
+##
+include $(top_srcdir)/config/eu.am
+AM_CPPFLAGS += -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
+	   -I$(srcdir)/../libdw -I$(srcdir)/../libdwelf -I$(srcdir)/../libdwfl
+VERSION = 1
+
+noinst_LIBRARIES = libdwfl_stacktrace.a
+noinst_LIBRARIES += libdwfl_stacktrace_pic.a
+
+pkginclude_HEADERS = libdwfl_stacktrace.h
+
+
+libdwfl_stacktrace_a_SOURCES = dwflst_perf_frame.c
+
+libdwfl_stacktrace = $(libdw)
+libdw = ../libdw/libdw.so
+libelf = ../libelf/libelf.so
+libebl = ../libebl/libebl.a
+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
+
+EXTRA_libdwfl_stacktrace_a_DEPENDENCIES = libdwfl_stacktrace.manifest
+
+libdwfl_stacktrace.manifest: $(libdwfl_stacktrace_a_OBJECTS)
+	$(AM_V_GEN)echo $^ > $@
+
+MOSTLYCLEANFILES = $(am_libdwfl_stacktrace_a_OBJECTS) \
+	$(am_libdwfl_stacktrace_pic_a_OBJECTS)
+CLEANFILES = $(EXTRA_libdwfl_stacktrace_a_DEPENDENCIES)
diff --git a/libdwfl_stacktrace/dwflst_perf_frame.c b/libdwfl_stacktrace/dwflst_perf_frame.c
new file mode 100644
index 00000000..79e8e482
--- /dev/null
+++ b/libdwfl_stacktrace/dwflst_perf_frame.c
@@ -0,0 +1,63 @@ 
+/* Get Dwarf Frame state for perf stack sample data.
+   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
+
+#if defined(__linux__)
+# include <linux/perf_event.h>
+#endif
+
+#include "libdwfl_stacktraceP.h"
+
+Ebl *default_ebl = NULL;
+GElf_Half default_ebl_machine = EM_NONE;
+
+uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
+{
+  /* XXX The most likely case is that this will only be called once,
+     for the current architecture.  So we keep one Ebl* around for
+     answering this query and replace it in the unlikely case of
+     getting called with different architectures.  */
+  if (default_ebl != NULL && default_ebl_machine != machine)
+    {
+      ebl_closebackend(default_ebl);
+      default_ebl = NULL;
+    }
+  if (default_ebl == NULL)
+    {
+      default_ebl = ebl_openbackend_machine(machine);
+      default_ebl_machine = machine;
+    }
+  if (default_ebl != NULL)
+    return ebl_perf_frame_regs_mask (default_ebl);
+  return 0;
+}
+
+/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */
diff --git a/libdwfl_stacktrace/libdwfl_stacktrace.h b/libdwfl_stacktrace/libdwfl_stacktrace.h
new file mode 100644
index 00000000..564f504a
--- /dev/null
+++ b/libdwfl_stacktrace/libdwfl_stacktrace.h
@@ -0,0 +1,58 @@ 
+/* Interfaces for libdwfl_stacktrace.
+
+   XXX: This is an experimental initial version of the API, and is
+   liable to change in future releases of elfutils, especially as
+   we figure out how to generalize the work to other sample data
+   formats in addition to perf_events.
+
+   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 _LIBDWFL_STACKTRACE_H
+#define _LIBDWFL_STACKTRACE_H  1
+
+#include "libdwfl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* XXX dwflst_perf_sample_getframes to be added in subsequent patch */
+
+/* Returns the linux perf_events register mask describing a set of
+   registers sufficient for unwinding on MACHINE, or 0 if libdwfl does
+   not handle perf_events samples for MACHINE.  Does not take a Dwfl*
+   or Elf* since this is meant to allow a profiling tool to configure
+   perf_events to produce meaningful data for a libdwfl session to be
+   opened later.  */
+uint64_t dwflst_perf_sample_preferred_regs_mask (GElf_Half machine);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* libdwfl_stacktrace.h */
diff --git a/libdwfl_stacktrace/libdwfl_stacktraceP.h b/libdwfl_stacktrace/libdwfl_stacktraceP.h
new file mode 100644
index 00000000..fe6945fc
--- /dev/null
+++ b/libdwfl_stacktrace/libdwfl_stacktraceP.h
@@ -0,0 +1,36 @@ 
+/* Internal definitions for libdwfl_stacktrace.
+   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 _LIBDWFL_STACKTRACEP_H
+#define _LIBDWFL_STACKTRACEP_H 1
+
+#include <libdwfl_stacktrace.h>
+
+#include "libdwflP.h"
+
+#endif  /* libdwfl_stacktraceP.h */