Hi Serhei,

On Mon, Apr 13, 2026 at 5:31 PM Serhei Makarov <[email protected]> wrote:
>
> Now that stackprof is added, the existing src/stacktrace.c tool is
> officially obsolete as a demo -- it relies on a custom patchset for
> Sysprof (communicating via pipe, not contributed to mainline, in
> favour of the sysprof-live-unwinder tool which links elfutils as a
> library), whereas stackprof pulls data from perf_events directly.
>
> Likewise, stackprof includes all of the eu-stacktrace functionality
> for logging/statistics.
>
> * configure.ac: Remove eu-stacktrace related bits.
>   The configure checks for stackprof are simpler (just perf_events.h
>   and libpfm).
> * src/Makefile.am: Remove eu-stacktrace related bits.
> * src/stacktrace.c: Remove.

There are various references to eu-stacktrace in config/elfutils.spec.in
that should be removed or else rpmbuild will break on x86_64.

.forgejo/workflows/check-fedora.yaml includes sysprof-capture-devel just
for eu-stacktrace so that should be removed.  src/.gitignore should have
its stacktrace entry removed. eu-stacktrace removal should be mentioned
in NEWS too.

Aaron

>
> Co-authored-by: <[email protected]>
> Signed-off-by: <[email protected]>
> ---
>  configure.ac     |   55 +-
>  src/Makefile.am  |    6 -
>  src/stacktrace.c | 1418 ----------------------------------------------
>  3 files changed, 1 insertion(+), 1478 deletions(-)
>  delete mode 100644 src/stacktrace.c
>
> diff --git a/configure.ac b/configure.ac
> index e5be95b8..34027691 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1012,59 +1012,6 @@ AC_ARG_ENABLE(debuginfod-ima-cert-path,
>  AC_SUBST(DEBUGINFOD_IMA_CERT_PATH, $default_debuginfod_ima_cert_path)
>  AC_CONFIG_FILES([config/profile.sh config/profile.csh config/profile.fish])
>
> -# XXX Currently, eu-stacktrace can only work with sysprof/x86, hence:
> -AC_ARG_ENABLE([stacktrace],AS_HELP_STRING([--enable-stacktrace], [Enable 
> eu-stacktrace]))
> -# check for x86, or more precisely _ASM_X86_PERF_REGS_H
> -AS_IF([test "x$enable_stacktrace" = "xyes"], [
> -   enable_stacktrace=no
> -   AC_LANG([C])
> -   AC_CACHE_CHECK([for _ASM_X86_PERF_REGS_H], ac_cv_has_asm_x86_perf_regs_h,
> -      [AC_COMPILE_IFELSE([AC_LANG_SOURCE([
> -#include <asm/perf_regs.h>
> -#ifndef _ASM_X86_PERF_REGS_H
> -#error "_ASM_X86_PERF_REGS_H not found"
> -#endif
> -])], ac_cv_has_asm_x86_perf_regs_h=yes, ac_cv_has_asm_x86_perf_regs_h=no)])
> -   AS_IF([test "x$ac_cv_has_asm_x86_perf_regs_h" = xyes], [
> -      enable_stacktrace=yes
> -   ])
> -   if test "x$enable_stacktrace" = "xno"; then
> -      AC_MSG_ERROR([${program_prefix}stacktrace currently only supports x86, 
> use --disable-stacktrace to disable.])
> -   fi
> -])
> -# check for sysprof headers:
> -AS_IF([test "x$enable_stacktrace" = "xyes"], [
> -   enable_stacktrace=no
> -   AC_CACHE_CHECK([for sysprof-6/sysprof-capture-types.h], 
> ac_cv_has_sysprof_6_headers,
> -      [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include 
> <sysprof-6/sysprof-capture-types.h>]])],
> -      ac_cv_has_sysprof_6_headers=yes, ac_cv_has_sysprof_6_headers=no)])
> -   AS_IF([test "x$ac_cv_has_sysprof_6_headers" = xyes], [
> -      AC_DEFINE(HAVE_SYSPROF_6_HEADERS)
> -      enable_stacktrace=yes
> -   ])
> -   AH_TEMPLATE([HAVE_SYSPROF_6_HEADERS], [Define to 1 if 
> `sysprof-6/sysprof-capture-types.h` is provided by the system, 0 otherwise.])
> -   AC_CACHE_CHECK([for sysprof-4/sysprof-capture-types.h], 
> ac_cv_has_sysprof_4_headers,
> -      [AC_COMPILE_IFELSE([AC_LANG_SOURCE([[#include 
> <sysprof-4/sysprof-capture-types.h>]])],
> -      ac_cv_has_sysprof_4_headers=yes, ac_cv_has_sysprof_4_headers=no)])
> -   AS_IF([test "x$ac_cv_has_sysprof_4_headers" = xyes], [
> -      AC_DEFINE(HAVE_SYSPROF_4_HEADERS)
> -      enable_stacktrace=yes
> -   ])
> -   AH_TEMPLATE([HAVE_SYSPROF_4_HEADERS], [Define to 1 if 
> `sysprof-4/sysprof-capture-types.h` is provided by the system, 0 otherwise.])
> -   if test "x$enable_stacktrace" = "xno"; then
> -      AC_MSG_ERROR([sysprof headers for ${program_prefix}stacktrace not 
> found, use --disable-stacktrace to disable.])
> -   fi
> -],[
> -   # If eu-stacktrace is disabled, also disable the automake conditionals:
> -   ac_cv_has_sysprof_6_headers=no
> -   ac_cv_has_sysprof_4_headers=no
> -   # And make sure the feature listing shows 'no':
> -   enable_stacktrace=no
> -])
> -AM_CONDITIONAL([HAVE_SYSPROF_6_HEADERS],[test 
> "x$ac_cv_has_sysprof_6_headers" = xyes])
> -AM_CONDITIONAL([HAVE_SYSPROF_4_HEADERS],[test 
> "x$ac_cv_has_sysprof_4_headers" = xyes])
> -AM_CONDITIONAL([ENABLE_STACKTRACE],[test "x$enable_stacktrace" = "xyes"])
> -
>  AC_OUTPUT
>
>  AC_MSG_NOTICE([
> @@ -1104,7 +1051,7 @@ AC_MSG_NOTICE([
>      Default DEBUGINFOD_URLS            : ${default_debuginfod_urls}
>      Debuginfod RPM sig checking        : 
> ${enable_debuginfod_ima_verification}
>      Default DEBUGINFOD_IMA_CERT_PATH   : ${default_debuginfod_ima_cert_path}
> -    ${program_prefix}stacktrace support              : ${enable_stacktrace}
> +    ${program_prefix}stackprof support              : ${enable_stackprof}
>
>    EXTRA TEST FEATURES (used with make check)
>      have bunzip2 installed (required)  : ${HAVE_BUNZIP2}
> diff --git a/src/Makefile.am b/src/Makefile.am
> index f753c70c..a6c34d15 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -32,9 +32,6 @@ bin_PROGRAMS = readelf nm size strip elflint findtextrel 
> addr2line \
>                elfcmp objdump ranlib strings ar unstrip stack elfcompress \
>                elfclassify srcfiles
>
> -if ENABLE_STACKTRACE
> -bin_PROGRAMS += stacktrace
> -endif
>  if ENABLE_STACKPROF
>  bin_PROGRAMS += stackprof
>  endif
> @@ -124,9 +121,6 @@ strings_LDADD = $(libelf) $(libeu) $(argp_LDADD)
>  ar_LDADD = libar.a $(libelf) $(libeu) $(argp_LDADD) $(obstack_LIBS)
>  unstrip_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
>  stack_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD) 
> $(demanglelib)
> -if ENABLE_STACKTRACE
> -stacktrace_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
> -endif
>  elfcompress_LDADD = $(libebl) $(libelf) $(libdw) $(libeu) $(argp_LDADD)
>  elfclassify_LDADD = $(libelf) $(libdw) $(libeu) $(argp_LDADD)
>  srcfiles_SOURCES = srcfiles.cxx
> diff --git a/src/stacktrace.c b/src/stacktrace.c
> deleted file mode 100644
> index 1c9faeeb..00000000
> --- a/src/stacktrace.c
> +++ /dev/null
> @@ -1,1418 +0,0 @@
> -/* Process a stream of stack samples into stack traces.
> -   Copyright (C) 2023-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 the GNU General Public License as published by
> -   the Free Software Foundation; either version 3 of the License, or
> -   (at your option) any later version.
> -
> -   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 a copy of the GNU General Public License
> -   along with this program.  If not, see <http://www.gnu.org/licenses/>.
> -
> -   This file incorporates work covered by the following copyright and
> -   permission notice:
> -
> -   Copyright 2016-2019 Christian Hergert <[email protected]>
> -
> -   Redistribution and use in source and binary forms, with or without
> -   modification, are permitted provided that the following conditions are
> -   met:
> -
> -   1. Redistributions of source code must retain the above copyright notice,
> -      this list of conditions and the following disclaimer.
> -
> -   2. Redistributions in binary form must reproduce the above copyright
> -      notice, this list of conditions and the following disclaimer in the
> -      documentation and/or other materials provided with the distribution.
> -
> -   Subject to the terms and conditions of this license, each copyright holder
> -   and contributor hereby grants to those receiving rights under this license
> -   a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
> -   irrevocable (except for failure to satisfy the conditions of this license)
> -   patent license to make, have made, use, offer to sell, sell, import, and
> -   otherwise transfer this software, where such license applies only to those
> -   patent claims, already acquired or hereafter acquired, licensable by such
> -   copyright holder or contributor that are necessarily infringed by:
> -
> -   (a) their Contribution(s) (the licensed copyrights of copyright holders
> -       and non-copyrightable additions of contributors, in source or binary
> -       form) alone; or
> -
> -   (b) combination of their Contribution(s) with the work of authorship to
> -       which such Contribution(s) was added by such copyright holder or
> -       contributor, if, at the time the Contribution is added, such addition
> -       causes such combination to be necessarily infringed. The patent 
> license
> -       shall not apply to any other combinations which include the
> -       Contribution.
> -
> -   Except as expressly stated above, no rights or licenses from any copyright
> -   holder or contributor is granted under this license, whether expressly, by
> -   implication, estoppel or otherwise.
> -
> -   DISCLAIMER
> -
> -   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
> -   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
> -   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> -   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
> -   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
> -   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
> -   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
> -   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
> -   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
> -   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
> -   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
> -
> -#include <config.h>
> -#include <assert.h>
> -#include <argp.h>
> -#include <stdbool.h>
> -#include <stdio.h>
> -#include <string.h>
> -#include <fcntl.h>
> -#include <signal.h>
> -#include <locale.h>
> -
> -#include <system.h>
> -
> -#include <linux/perf_event.h>
> -
> -/* TODO: Need to generalize the code beyond x86 architectures. */
> -#include <asm/perf_regs.h>
> -#ifndef _ASM_X86_PERF_REGS_H
> -#error "eu-stacktrace is currently limited to x86 architectures"
> -#endif
> -
> -/*************************************
> - * Includes: libdwfl data structures *
> - *************************************/
> -
> -#include ELFUTILS_HEADER(ebl)
> -/* #include ELFUTILS_HEADER(dwfl) */
> -#include "../libdwfl/libdwflP.h"
> -/* XXX: Private header needed for find_procfile. */
> -#include ELFUTILS_HEADER(dwfl_stacktrace)
> -
> -/*************************************
> - * Includes: sysprof data structures *
> - *************************************/
> -
> -#if HAVE_SYSPROF_6_HEADERS
> -#include <sysprof-6/sysprof-capture-types.h>
> -#define HAVE_SYSPROF_HEADERS 1
> -#elif HAVE_SYSPROF_4_HEADERS
> -#include <sysprof-4/sysprof-capture-types.h>
> -#define HAVE_SYSPROF_HEADERS 1
> -#else
> -#define HAVE_SYSPROF_HEADERS 0
> -#endif
> -
> -#if HAVE_SYSPROF_HEADERS
> -
> -/* XXX: To be added to new versions of sysprof.  If a
> -   sysprof-capture-types.h with new capture frame is being used, this
> -   #if should guard against duplicate declarations. */
> -#if SYSPROF_CAPTURE_FRAME_LAST < 19
> -
> -#undef SYSPROF_CAPTURE_FRAME_LAST
> -#define SYSPROF_CAPTURE_FRAME_STACK_USER 18
> -#define SYSPROF_CAPTURE_FRAME_LAST 19
> -
> -SYSPROF_ALIGNED_BEGIN(1)
> -typedef struct
> -{
> -  SysprofCaptureFrame   frame;
> -  uint64_t              size;
> -  int32_t               tid;
> -  uint32_t              padding;
> -  uint8_t               data[0];
> -} SysprofCaptureStackUser
> -SYSPROF_ALIGNED_END(1);
> -
> -/* Does not appear standalone; instead, appended to the end of a 
> SysprofCaptureStackUser frame. */
> -SYSPROF_ALIGNED_BEGIN(1)
> -typedef struct
> -{
> -  uint32_t              n_regs;
> -  uint32_t              abi;
> -  uint64_t              regs[0];
> -} SysprofCaptureUserRegs
> -SYSPROF_ALIGNED_END(1);
> -
> -#endif /* SYSPROF_CAPTURE_FRAME_STACK_USER */
> -
> -#endif /* HAVE_SYSPROF_HEADERS */
> -
> -/**************************
> - * Global data structures *
> - **************************/
> -
> -/* TODO: Reduce repeated error messages in show_failures. */
> -
> -static int maxframes = 256;
> -
> -static char *input_path = NULL;
> -static int input_fd = -1;
> -static char *output_path = NULL;
> -static int output_fd = -1;
> -
> -static int signal_count = 0;
> -
> -#define MODE_OPTS "none/passthru/naive"
> -#define MODE_NONE 0x0
> -#define MODE_PASSTHRU 0x1
> -#define MODE_NAIVE 0x2
> -static int processing_mode = MODE_NAIVE;
> -
> -#define FORMAT_OPTS "sysprof"
> -#define FORMAT_PERF 0x1
> -#define FORMAT_SYSPROF 0x2
> -static int input_format;
> -static int output_format = FORMAT_SYSPROF;
> -
> -/* XXX Used to decide regs_mask for dwflst_perf_sample_getframes. */
> -Ebl *default_ebl = NULL;
> -
> -/* non-printable argp options.  */
> -#define OPT_DEBUG      0x100
> -
> -/* Diagnostic options. */
> -static bool show_frames = false;
> -static bool show_samples = false;
> -static bool show_failures = false;
> -static bool show_summary = true;
> -
> -/* Environment vars to drive diagnostic options: */
> -#define ELFUTILS_STACKTRACE_VERBOSE_ENV_VAR "ELFUTILS_STACKTRACE_VERBOSE"
> -/* Valid values that turn on diagnostics: 'true', 'verbose', 'debug', '1', 
> '2'. */
> -
> -/* Enables even more diagnostics on modules: */
> -/* #define DEBUG_MODULES */
> -
> -/* Enables standard access to DWARF debuginfo, matching stack.c.
> -   This is of dubious benefit -- for profiling, we really should
> -   aim to resolve everything with minimal overhead using eh CFI. */
> -/* #define FIND_DEBUGINFO */
> -
> -/* Program exit codes.  All samples processed without any errors is
> -   GOOD.  Some non-fatal errors during processing is an ERROR.  A
> -   fatal error or no samples processed at all is BAD.  A command line
> -   USAGE exit is generated by argp_error. */
> -#define EXIT_OK     0
> -#define EXIT_ERROR  1
> -#define EXIT_BAD    2
> -#define EXIT_USAGE 64
> -
> -/**************************
> - * Sysprof format support *
> - **************************/
> -
> -/* TODO: elfutils (internal) libraries use libNN_set_errno and _E_WHATEVER;
> -   this code sets errno variable directly and uses standard EWHATEVER. */
> -
> -/* XXX based on sysprof src/libsysprof-capture/sysprof-capture-reader.c
> -
> -   Note: BSD license attribution at the top of the file applies to this
> -   segment. Could split into a separate file or even a library,
> -   in which case the attribution notice will move along with it. */
> -
> -#if HAVE_SYSPROF_HEADERS
> -
> -/* A complete passthrough can be implemented based on the following 7 
> functions:
> - - sysprof_reader_begin/sysprof_reader_end :: 
> sysprof_capture_reader_new_from_fd
> - - sysprof_reader_getheader :: sysprof_capture_reader_read_file_header
> - - sysprof_reader_getframes :: sysprof_capture_reader_discover_end_time, an 
> example main loop that doesn't handle every type of frame
> - - sysprof_reader_next_frame :: sysprof_capture_reader_peek_frame + 
> sysprof_capture_reader_skip + sysprof_capture_reader_read_basic
> - - sysprof_reader_ensure_space_for :: sysprof_capture_reader_ensure_space_for
> - - sysprof_reader_bswap_frame :: sysprof_capture_reader_bswap_frame
> - */
> -
> -/* Callback results */
> -enum
> -{
> -  SYSPROF_CB_OK = 0,
> -  SYSPROF_CB_ABORT
> -};
> -
> -typedef struct
> -{
> -  uint8_t *buf;
> -  size_t bufsz;
> -  size_t len;
> -  size_t pos;
> -  size_t fd_off; /* XXX track offset for debugging only */
> -  int fd;
> -  int endian;
> -  SysprofCaptureFileHeader header;
> -} SysprofReader;
> -
> -/* forward decls */
> -SysprofReader *sysprof_reader_begin (int fd);
> -void sysprof_reader_end (SysprofReader *reader);
> -bool sysprof_reader_getheader (SysprofReader *reader,
> -                              SysprofCaptureFileHeader *header);
> -void sysprof_reader_bswap_frame (SysprofReader *reader,
> -                                SysprofCaptureFrame *frame);
> -bool sysprof_reader_ensure_space_for (SysprofReader *reader, size_t len);
> -SysprofCaptureFrame *sysprof_reader_next_frame (SysprofReader *reader);
> -ptrdiff_t sysprof_reader_getframes (SysprofReader *reader,
> -                                   int (*callback) (SysprofCaptureFrame 
> *frame,
> -                                                    void *arg),
> -                                   void *arg);
> -
> -SysprofReader *
> -sysprof_reader_begin (int fd)
> -{
> -  SysprofReader *reader;
> -
> -  assert (fd > -1);
> -
> -  reader = malloc (sizeof (SysprofReader));
> -  if (reader == NULL)
> -    {
> -      errno = ENOMEM;
> -      return NULL;
> -    }
> -
> -  reader->bufsz = USHRT_MAX * 2;
> -  reader->buf = malloc (reader->bufsz);
> -  if (reader->buf == NULL)
> -    {
> -      free (reader);
> -      errno = ENOMEM;
> -      return NULL;
> -    }
> -
> -  reader->len = 0;
> -  reader->pos = 0;
> -  reader->fd = fd;
> -  reader->fd_off = 0;
> -
> -  if (!sysprof_reader_getheader (reader, &reader->header))
> -    {
> -      int errsv = errno;
> -      sysprof_reader_end (reader);
> -      errno = errsv;
> -      return NULL;
> -    }
> -
> -  if (reader->header.little_endian)
> -    reader->endian = __LITTLE_ENDIAN;
> -  else
> -    reader->endian = __BIG_ENDIAN;
> -
> -  return reader;
> -}
> -
> -void
> -sysprof_reader_end (SysprofReader *reader)
> -{
> -  if (reader != NULL)
> -    {
> -      free (reader->buf);
> -      free (reader);
> -    }
> -}
> -
> -bool
> -sysprof_reader_getheader (SysprofReader *reader,
> -                         SysprofCaptureFileHeader *header)
> -{
> -  assert (reader != NULL);
> -  assert (header != NULL);
> -
> -  if (sizeof *header != read (reader->fd, header, sizeof *header))
> -    {
> -      /* errno is propagated */
> -      return false;
> -    }
> -  reader->fd_off += sizeof *header;
> -
> -  if (header->magic != SYSPROF_CAPTURE_MAGIC)
> -    {
> -      errno = EBADMSG;
> -      return false;
> -    }
> -
> -  header->capture_time[sizeof header->capture_time - 1] = '\0';
> -
> -  return true;
> -}
> -
> -void
> -sysprof_reader_bswap_frame (SysprofReader *reader, SysprofCaptureFrame 
> *frame)
> -{
> -  assert (reader != NULL);
> -  assert (frame  != NULL);
> -
> -  if (unlikely (reader->endian != __BYTE_ORDER))
> -    {
> -      frame->len = bswap_16 (frame->len);
> -      frame->cpu = bswap_16 (frame->cpu);
> -      frame->pid = bswap_32 (frame->pid);
> -      frame->time = bswap_64 (frame->time);
> -    }
> -}
> -
> -bool
> -sysprof_reader_ensure_space_for (SysprofReader *reader, size_t len)
> -{
> -  assert (reader != NULL);
> -  assert (reader->pos <= reader->len);
> -  assert (len > 0);
> -
> -  /* Ensure alignment of length to read */
> -  len = (len + SYSPROF_CAPTURE_ALIGN - 1) & ~(SYSPROF_CAPTURE_ALIGN - 1);
> -
> -  if ((reader->len - reader->pos) < len)
> -    {
> -      ssize_t r;
> -
> -      if (reader->len > reader->pos)
> -       memmove (reader->buf,
> -                &reader->buf[reader->pos],
> -                reader->len - reader->pos);
> -      reader->len -= reader->pos;
> -      reader->pos = 0;
> -
> -      while (reader->len < len)
> -       {
> -         assert ((reader->pos + reader->len) < reader->bufsz);
> -         assert (reader->len < reader->bufsz);
> -
> -         /* Read into our buffer */
> -         r = read (reader->fd,
> -                   &reader->buf[reader->len],
> -                   reader->bufsz - reader->len);
> -
> -         if (r <= 0)
> -           break;
> -
> -         reader->fd_off += r;
> -         reader->len += r;
> -       }
> -    }
> -
> -  return (reader->len - reader->pos) >= len;
> -}
> -
> -/* XXX May want to signal errors in more detail with an rc. */
> -SysprofCaptureFrame *
> -sysprof_reader_next_frame (SysprofReader *reader)
> -{
> -  SysprofCaptureFrame frame_hdr;
> -  SysprofCaptureFrame *frame = NULL;
> -
> -  assert (reader != NULL);
> -  assert ((reader->pos % SYSPROF_CAPTURE_ALIGN) == 0);
> -  assert (reader->pos <= reader->len);
> -  assert (reader->pos <= reader->bufsz);
> -
> -  if (!sysprof_reader_ensure_space_for (reader, sizeof *frame))
> -    return NULL;
> -
> -  assert ((reader->pos % SYSPROF_CAPTURE_ALIGN) == 0);
> -
> -  frame = (SysprofCaptureFrame *)(void *)&reader->buf[reader->pos];
> -  frame_hdr = *frame;
> -  sysprof_reader_bswap_frame (reader, &frame_hdr);
> -
> -  if (frame_hdr.len < sizeof (SysprofCaptureFrame))
> -    return NULL;
> -
> -  if (!sysprof_reader_ensure_space_for (reader, frame_hdr.len))
> -    return NULL;
> -
> -  frame = (SysprofCaptureFrame *)(void *)&reader->buf[reader->pos];
> -  sysprof_reader_bswap_frame (reader, frame);
> -
> -  if (frame->len > (reader->len - reader->pos))
> -    return NULL;
> -
> -  reader->pos += frame->len;
> -
> -  if ((reader->pos % SYSPROF_CAPTURE_ALIGN) != 0)
> -    return NULL;
> -
> -  /* if (frame->type < 0 || frame->type >= SYSPROF_CAPTURE_FRAME_LAST) */
> -  if (frame->type >= SYSPROF_CAPTURE_FRAME_LAST)
> -    return NULL;
> -
> -  return frame;
> -}
> -
> -ptrdiff_t
> -sysprof_reader_getframes (SysprofReader *reader,
> -                         int (*callback) (SysprofCaptureFrame *,
> -                                          void *),
> -                         void *arg)
> -{
> -  SysprofCaptureFrame *frame;
> -
> -  assert (reader != NULL);
> -
> -  while ((frame = sysprof_reader_next_frame (reader)))
> -    {
> -      int ok = callback (frame, arg);
> -      if (ok != SYSPROF_CB_OK)
> -       return -1;
> -    }
> -  return 0;
> -}
> -
> -#endif /* HAVE_SYSPROF_HEADERS */
> -
> -/****************************************************
> - * Dwfl and statistics table for multiple processes *
> - ****************************************************/
> -
> -Dwflst_Process_Tracker *tracker = NULL;
> -
> -/* This echoes lib/dynamicsizehash.* with some necessary modifications. */
> -typedef struct
> -{
> -  bool used;
> -  pid_t pid;
> -  Dwfl *dwfl;
> -  char *comm;
> -  int max_frames; /* for diagnostic purposes */
> -  int total_samples; /* for diagnostic purposes */
> -  int lost_samples; /* for diagnostic purposes */
> -  Dwfl_Unwound_Source last_unwound; /* track CFI source, for diagnostic 
> purposes */
> -  Dwfl_Unwound_Source worst_unwound; /* track CFI source, for diagnostic 
> purposes */
> -} dwfltab_ent;
> -
> -typedef struct
> -{
> -  ssize_t size;
> -  ssize_t filled;
> -  dwfltab_ent *table;
> -} dwfltab;
> -
> -/* XXX table size must be a prime */
> -#define DWFLTAB_DEFAULT_SIZE 1021
> -extern size_t next_prime (size_t); /* XXX from libeu.a lib/next_prime.c */
> -dwfltab_ent *dwfltab_find(pid_t pid); /* forward decl */
> -
> -/* TODO: Could turn this into a field of sui instead of a global. */
> -dwfltab default_table;
> -
> -/* XXX based on lib/dynamicsizehash.* *_init */
> -bool dwfltab_init(void)
> -{
> -  dwfltab *htab = &default_table;
> -  htab->size = DWFLTAB_DEFAULT_SIZE;
> -  htab->filled = 0;
> -  htab->table = calloc ((htab->size + 1), sizeof(htab->table[0]));
> -  return (htab->table != NULL);
> -}
> -
> -/* XXX based on lib/dynamicsizehash.* insert_entry_2 */
> -bool dwfltab_resize(void)
> -{
> -  /* TODO: Also implement LRU eviction, especially given the number of
> -     extremely-short-lived processes seen on GNOME desktop. */
> -  dwfltab *htab = &default_table;
> -  ssize_t old_size = htab->size;
> -  dwfltab_ent *old_table = htab->table;
> -  htab->size = next_prime (htab->size * 2);
> -  htab->table = calloc ((htab->size + 1), sizeof(htab->table[0]));
> -  if (htab->table == NULL)
> -    {
> -      htab->size = old_size;
> -      htab->table = old_table;
> -      return false;
> -    }
> -  htab->filled = 0;
> -  /* Transfer the old entries to the new table. */
> -  for (ssize_t idx = 1; idx <= old_size; ++idx)
> -    if (old_table[idx].used)
> -      {
> -       dwfltab_ent *ent0 = &old_table[idx];
> -       dwfltab_ent *ent1 = dwfltab_find(ent0->pid);
> -       assert (ent1 != NULL);
> -       memcpy (ent1, ent0, sizeof(dwfltab_ent));
> -      }
> -  free (old_table);
> -  /* TODO: Need to decide log level for this message. For now, it's
> -     not a failure, and printing it by default seems harmless: */
> -  fprintf(stderr, N_("Resized Dwfl table from %ld to %ld, copied %ld 
> entries\n"),
> -         old_size, htab->size, htab->filled);
> -  return true;
> -}
> -
> -/* XXX based on lib/dynamicsizehash.* *_find */
> -dwfltab_ent *dwfltab_find(pid_t pid)
> -{
> -  dwfltab *htab = &default_table;
> -  ssize_t idx = 1 + (htab->size > (ssize_t)pid ? (ssize_t)pid : (ssize_t)pid 
> % htab->size);
> -
> -  if (!htab->table[idx].used)
> -    goto found;
> -  if (htab->table[idx].pid == pid)
> -    goto found;
> -
> -  int64_t hash = 1 + pid % (htab->size - 2);
> -  do
> -    {
> -      if (idx <= hash)
> -       idx = htab->size + idx - hash;
> -      else
> -       idx -= hash;
> -
> -      if (!htab->table[idx].used)
> -       goto found;
> -      if (htab->table[idx].pid == pid)
> -       goto found;
> -    }
> -  while (true);
> -
> - found:
> -  if (htab->table[idx].pid == 0)
> -    {
> -      if (100 * htab->filled > 90 * htab->size)
> -       if (!dwfltab_resize())
> -         return NULL;
> -      htab->table[idx].used = true;
> -      htab->table[idx].pid = pid;
> -      htab->filled += 1;
> -    }
> -  return &htab->table[idx];
> -}
> -
> -Dwfl *
> -pid_find_dwfl (pid_t pid)
> -{
> -  dwfltab_ent *entry = dwfltab_find(pid);
> -  if (entry == NULL)
> -    return NULL;
> -  return entry->dwfl;
> -}
> -
> -char *
> -pid_find_comm (pid_t pid)
> -{
> -  dwfltab_ent *entry = dwfltab_find(pid);
> -  if (entry == NULL)
> -    return "<unknown>";
> -  if (entry->comm != NULL)
> -    return entry->comm;
> -  char name[64];
> -  int i = snprintf (name, sizeof(name), "/proc/%ld/comm", (long) pid);
> -  FILE *procfile = fopen(name, "r");
> -  if (procfile == NULL)
> -    goto fail;
> -  size_t linelen = 0;
> -  i = getline(&entry->comm, &linelen, procfile);
> -  if (i < 0)
> -    {
> -      free(entry->comm);
> -      goto fail;
> -    }
> -  for (i = linelen - 1; i > 0; i--)
> -    if (entry->comm[i] == '\n')
> -       entry->comm[i] = '\0';
> -  fclose(procfile);
> -  goto done;
> - fail:
> -  entry->comm = (char *)malloc(16);
> -  snprintf (entry->comm, 16, "<unknown>");
> - done:
> -  return entry->comm;
> -}
> -
> -/* Cache dwfl structs in a basic hash table. */
> -void
> -pid_store_dwfl (pid_t pid, Dwfl *dwfl)
> -{
> -  dwfltab_ent *entry = dwfltab_find(pid);
> -  if (entry == NULL)
> -    return;
> -  entry->pid = pid;
> -  entry->dwfl = dwfl;
> -  pid_find_comm(pid);
> -  return;
> -}
> -
> -/*****************************************************
> - * Sysprof backend: basic none/passthrough callbacks *
> - *****************************************************/
> -
> -#if HAVE_SYSPROF_HEADERS
> -
> -int
> -sysprof_none_cb (SysprofCaptureFrame *frame __attribute__ ((unused)),
> -                void *arg __attribute__ ((unused)))
> -{
> -  return SYSPROF_CB_OK;
> -}
> -
> -struct sysprof_passthru_info
> -{
> -  int output_fd;
> -  SysprofReader *reader;
> -  int pos; /* for diagnostic purposes */
> -};
> -
> -int
> -sysprof_passthru_cb (SysprofCaptureFrame *frame, void *arg)
> -{
> -  struct sysprof_passthru_info *spi = (struct sysprof_passthru_info *)arg;
> -  sysprof_reader_bswap_frame (spi->reader, frame); /* reverse the earlier 
> bswap */
> -  ssize_t n_write = write (spi->output_fd, frame, frame->len);
> -  spi->pos += frame->len;
> -  assert ((spi->pos % SYSPROF_CAPTURE_ALIGN) == 0);
> -  if (n_write < 0)
> -    error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), 
> output_path);
> -  return SYSPROF_CB_OK;
> -}
> -
> -#endif /* HAVE_SYSPROF_HEADERS */
> -
> -/****************************************
> - * Sysprof backend: unwinding callbacks *
> - ****************************************/
> -
> -#if HAVE_SYSPROF_HEADERS
> -
> -#define UNWIND_ADDR_INCREMENT 512
> -struct sysprof_unwind_info
> -{
> -  int output_fd;
> -  SysprofReader *reader;
> -  int pos; /* for diagnostic purposes */
> -  int n_addrs;
> -  int max_addrs; /* for diagnostic purposes */
> -  uint64_t last_abi;
> -  Dwarf_Addr last_base; /* for diagnostic purposes */
> -  Dwarf_Addr last_sp; /* for diagnostic purposes */
> -#ifdef DEBUG_MODULES
> -  Dwfl *last_dwfl; /* for diagnostic purposes */
> -#endif
> -  pid_t last_pid; /* for diagnostic purposes, to provide access to dwfltab */
> -  Dwarf_Addr *addrs; /* allocate blocks of UNWIND_ADDR_INCREMENT */
> -  void *outbuf;
> -};
> -
> -#ifdef FIND_DEBUGINFO
> -
> -static char *debuginfo_path = NULL;
> -
> -static const Dwfl_Callbacks sample_callbacks =
> -  {
> -    .find_elf = dwflst_tracker_linux_proc_find_elf,
> -    .find_debuginfo = dwfl_standard_find_debuginfo,
> -    .debuginfo_path = &debuginfo_path,
> -  };
> -
> -#else
> -
> -int
> -nop_find_debuginfo (Dwfl_Module *mod __attribute__((unused)),
> -                   void **userdata __attribute__((unused)),
> -                   const char *modname __attribute__((unused)),
> -                   GElf_Addr base __attribute__((unused)),
> -                   const char *file_name __attribute__((unused)),
> -                   const char *debuglink_file __attribute__((unused)),
> -                   GElf_Word debuglink_crc __attribute__((unused)),
> -                   char **debuginfo_file_name __attribute__((unused)))
> -{
> -#ifdef DEBUG_MODULES
> -  fprintf(stderr, "nop_find_debuginfo: modname=%s file_name=%s 
> debuglink_file=%s\n",
> -         modname, file_name, debuglink_file);
> -#endif
> -  return -1;
> -}
> -
> -static const Dwfl_Callbacks sample_callbacks =
> -{
> -  .find_elf = dwflst_tracker_linux_proc_find_elf,
> -  .find_debuginfo = nop_find_debuginfo, /* work with CFI only */
> -};
> -
> -#endif /* FIND_DEBUGINFO */
> -
> -/* TODO: Probably needs to be relocated to libdwfl/linux-pid-attach.c
> -   to remove a dependency on the private dwfl interface. */
> -int
> -find_procfile (Dwfl *dwfl, pid_t *pid, Elf **elf, int *elf_fd)
> -{
> -  char buffer[36];
> -  FILE *procfile;
> -  int err = 0; /* The errno to return and set for dwfl->attacherr.  */
> -
> -  /* Make sure to report the actual PID (thread group leader) to
> -     dwfl_attach_state.  */
> -  snprintf (buffer, sizeof (buffer), "/proc/%ld/status", (long) *pid);
> -  procfile = fopen (buffer, "r");
> -  if (procfile == NULL)
> -    {
> -      err = errno;
> -    fail:
> -      if (dwfl->process == NULL && dwfl->attacherr == DWFL_E_NOERROR) /* XXX 
> requires libdwflP.h */
> -       {
> -         errno = err;
> -         /* TODO: __libdwfl_canon_error not exported from libdwfl */
> -         /* dwfl->attacherr = __libdwfl_canon_error (DWFL_E_ERRNO); */
> -       }
> -      return err;
> -    }
> -
> -  char *line = NULL;
> -  size_t linelen = 0;
> -  while (getline (&line, &linelen, procfile) >= 0)
> -    if (startswith (line, "Tgid:"))
> -      {
> -       errno = 0;
> -       char *endptr;
> -       long val = strtol (&line[5], &endptr, 10);
> -       if ((errno == ERANGE && val == LONG_MAX)
> -           || *endptr != '\n' || val < 0 || val != (pid_t) val)
> -         *pid = 0;
> -       else
> -         *pid = (pid_t) val;
> -       break;
> -      }
> -  free (line);
> -  fclose (procfile);
> -
> -  if (*pid == 0)
> -    {
> -      err = ESRCH;
> -      goto fail;
> -    }
> -
> -  char name[64];
> -  int i = snprintf (name, sizeof (name), "/proc/%ld/task", (long) *pid);
> -  if (i <= 0 || i >= (ssize_t) sizeof (name) - 1)
> -    {
> -      errno = -ENOMEM;
> -      goto fail;
> -    }
> -  DIR *dir = opendir (name);
> -  if (dir == NULL)
> -    {
> -      err = errno;
> -      goto fail;
> -    }
> -  else
> -    closedir(dir);
> -
> -  i = snprintf (name, sizeof (name), "/proc/%ld/exe", (long) *pid);
> -  assert (i > 0 && i < (ssize_t) sizeof (name) - 1);
> -  *elf_fd = open (name, O_RDONLY);
> -  if (*elf_fd >= 0)
> -    {
> -      *elf = elf_begin (*elf_fd, ELF_C_READ_MMAP, NULL);
> -      if (*elf == NULL)
> -       {
> -         /* Just ignore, dwfl_attach_state will fall back to trying
> -            to associate the Dwfl with one of the existing Dwfl_Module
> -            ELF images (to know the machine/class backend to use).  */
> -         if (show_failures)
> -           fprintf(stderr, N_("find_procfile pid %lld: elf not found"),
> -                   (long long)*pid);
> -         close (*elf_fd);
> -         *elf_fd = -1;
> -       }
> -    }
> -  else
> -    *elf = NULL;
> -  return 0;
> -}
> -
> -Dwfl *
> -sysprof_init_dwfl_cb (Dwflst_Process_Tracker *cb_tracker,
> -                     pid_t pid,
> -                     void *arg __attribute__ ((unused)))
> -{
> -  Dwfl *dwfl = dwflst_tracker_dwfl_begin (cb_tracker);
> -
> -  int err = dwfl_linux_proc_report (dwfl, pid);
> -  if (err < 0)
> -    {
> -      if (show_failures)
> -       fprintf(stderr, "dwfl_linux_proc_report pid %lld: %s",
> -               (long long) pid, dwfl_errmsg (-1));
> -      return NULL;
> -    }
> -  err = dwfl_report_end (dwfl, NULL, NULL);
> -  if (err != 0)
> -    {
> -      if (show_failures)
> -       fprintf(stderr, "dwfl_report_end pid %lld: %s",
> -               (long long) pid, dwfl_errmsg (-1));
> -      return NULL;
> -    }
> -
> -  return dwfl;
> -}
> -
> -Dwfl *
> -sysprof_find_dwfl (struct sysprof_unwind_info *sui,
> -                  SysprofCaptureStackUser *ev,
> -                  SysprofCaptureUserRegs *regs,
> -                  Elf **out_elf)
> -{
> -  pid_t pid = ev->frame.pid;
> -  /* XXX: Note that sysprof requesting the x86_64 register file from
> -     perf_events will result in an array of 17 regs even for 32-bit
> -     applications. */
> -  if (regs->n_regs < ebl_frame_nregs(default_ebl)) /* XXX expecting 
> everything except FLAGS */
> -    {
> -      if (show_failures)
> -       fprintf(stderr, N_("sysprof_find_dwfl: n_regs=%d, expected %ld\n"),
> -               regs->n_regs, ebl_frame_nregs(default_ebl));
> -      return NULL;
> -    }
> -
> -  Elf *elf = NULL;
> -  Dwfl *dwfl = dwflst_tracker_find_pid (tracker, pid, sysprof_init_dwfl_cb, 
> NULL);
> -  bool cached = false;
> -  if (dwfl != NULL && dwfl->process != NULL)
> -    {
> -      cached = true;
> -      goto reuse;
> -    }
> -
> -  int elf_fd = -1;
> -  int err = find_procfile (dwfl, &pid, &elf, &elf_fd);
> -  if (err < 0)
> -    {
> -      if (show_failures)
> -       fprintf(stderr, "find_procfile pid %lld: %s",
> -               (long long) pid, dwfl_errmsg (-1));
> -      return NULL;
> -    }
> -
> - reuse:
> -  /* TODO: Generalize to other architectures than x86_64. */
> -  sui->last_sp = regs->regs[7];
> -  sui->last_base = sui->last_sp;
> -
> -  if (show_frames) {
> -    bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
> -    fprintf(stderr, "sysprof_find_dwfl pid %lld%s: size=%ld%s pc=%lx 
> sp=%lx+(%lx)\n",
> -           (long long) pid, cached ? " (cached)" : "",
> -           ev->size, is_abi32 ? " (32-bit)" : "",
> -           regs->regs[8], sui->last_base, (long)0);
> -  }
> -
> -  if (!cached)
> -    pid_store_dwfl (pid, dwfl);
> -  *out_elf = elf;
> -  return dwfl;
> -}
> -
> -int
> -sysprof_unwind_frame_cb (Dwfl_Frame *state, void *arg)
> -{
> -  Dwarf_Addr pc;
> -  bool isactivation;
> -  if (! dwfl_frame_pc (state, &pc, &isactivation))
> -    {
> -      if (show_failures)
> -       fprintf(stderr, "dwfl_frame_pc: %s\n",
> -               dwfl_errmsg(-1));
> -      return DWARF_CB_ABORT;
> -    }
> -
> -  Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
> -  Dwarf_Addr sp;
> -  /* TODO: Need to generalize this code beyond x86 architectures. */
> -  struct sysprof_unwind_info *sui = (struct sysprof_unwind_info *)arg;
> -  int is_abi32 = (sui->last_abi == PERF_SAMPLE_REGS_ABI_32);
> -  /* DWARF register order cf. elfutils backends/{x86_64,i386}_initreg.c: */
> -  int user_regs_sp = is_abi32 ? 4 : 7;
> -  int rc = dwfl_frame_reg (state, user_regs_sp, &sp);
> -  if (rc < 0)
> -    {
> -      if (show_failures)
> -       fprintf(stderr, "dwfl_frame_reg: %s\n",
> -               dwfl_errmsg(-1));
> -      return DWARF_CB_ABORT;
> -    }
> -
> -#ifdef DEBUG_MODULES
> -  Dwfl_Module *mod = dwfl_addrmodule(sui->last_dwfl, pc);
> -  if (mod == NULL)
> -    {
> -      fprintf(stderr, "* pc=%lx -> NO MODULE\n", pc);
> -    }
> -  else
> -    {
> -      const char *mainfile;
> -      const char *debugfile;
> -      const char *modname = dwfl_module_info (mod, NULL, NULL, NULL, NULL,
> -                                             NULL, &mainfile, &debugfile);
> -      fprintf (stderr, "* module %s -> mainfile=%s debugfile=%s\n", modname, 
> mainfile, debugfile);
> -      Dwarf_Addr bias;
> -      Dwarf_CFI *cfi_eh = dwfl_module_eh_cfi (mod, &bias);
> -      if (cfi_eh == NULL)
> -       fprintf(stderr, "* pc=%lx -> NO EH_CFI\n", pc);
> -    }
> -#endif
> -
> -  dwfltab_ent *dwfl_ent = dwfltab_find(sui->last_pid);
> -  if (dwfl_ent != NULL)
> -    {
> -      Dwfl_Unwound_Source unwound_source = dwfl_frame_unwound_source(state);
> -      if (unwound_source > dwfl_ent->worst_unwound)
> -       dwfl_ent->worst_unwound = unwound_source;
> -      dwfl_ent->last_unwound = unwound_source;
> -      if (show_frames)
> -       fprintf(stderr, "* frame %d: pc_adjusted=%lx sp=%lx+(%lx) [%s]\n",
> -               sui->n_addrs, pc_adjusted, sui->last_base, sp - 
> sui->last_base,
> -               dwfl_unwound_source_str(unwound_source));
> -    }
> -  else
> -    {
> -      if (show_frames)
> -       fprintf(stderr, N_("* frame %d: pc_adjusted=%lx sp=%lx+(%lx) 
> [dwfl_ent not found]\n"),
> -               sui->n_addrs, pc_adjusted, sui->last_base, sp - 
> sui->last_base);
> -    }
> -
> -  if (sui->n_addrs > maxframes)
> -    {
> -      /* XXX very rarely, the unwinder can loop infinitely; worth 
> investigating? */
> -      if (show_failures)
> -       fprintf(stderr, N_("sysprof_unwind_frame_cb: sample exceeded 
> maxframes %d\n"),
> -               maxframes);
> -      return DWARF_CB_ABORT;
> -    }
> -
> -  sui->last_sp = sp;
> -  if (sui->n_addrs >= sui->max_addrs)
> -    {
> -      sui->addrs = reallocarray (sui->addrs, sui->max_addrs + 
> UNWIND_ADDR_INCREMENT, sizeof(Dwarf_Addr));
> -      sui->max_addrs = sui->max_addrs + UNWIND_ADDR_INCREMENT;
> -    }
> -  sui->addrs[sui->n_addrs] = pc;
> -  sui->n_addrs++;
> -  return DWARF_CB_OK;
> -}
> -
> -int
> -sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
> -{
> -  struct sysprof_unwind_info *sui = (struct sysprof_unwind_info *)arg;
> -  ssize_t n_write;
> -
> -  /* additional diagnostic to display process name */
> -  char *comm = NULL;
> -  if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE || frame->type == 
> SYSPROF_CAPTURE_FRAME_STACK_USER)
> -      comm = pid_find_comm(frame->pid);
> -
> -  if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE)
> -    {
> -      /* XXX additional diagnostics for comparing to eu-stacktrace unwind */
> -      SysprofCaptureSample *ev_sample = (SysprofCaptureSample *)frame;
> -      if (show_samples)
> -       fprintf(stderr, N_("sysprof_unwind_cb pid %lld (%s): callchain sample 
> with %d frames\n"),
> -               (long long)frame->pid, comm, ev_sample->n_addrs);
> -      if (show_summary)
> -       {
> -         /* For final diagnostics. */
> -         dwfltab_ent *dwfl_ent = dwfltab_find(frame->pid);
> -         if (dwfl_ent == NULL && show_failures)
> -           fprintf(stderr, N_("sysprof_unwind_cb pid %lld (%s): could not 
> create Dwfl table entry\n"),
> -                   (long long)frame->pid, comm);
> -         else if (dwfl_ent != NULL)
> -           {
> -             if (ev_sample->n_addrs > dwfl_ent->max_frames)
> -               dwfl_ent->max_frames = ev_sample->n_addrs;
> -             dwfl_ent->total_samples ++;
> -             if (ev_sample->n_addrs <= 2)
> -               dwfl_ent->lost_samples ++;
> -           }
> -       }
> -    }
> -  if (frame->type != SYSPROF_CAPTURE_FRAME_STACK_USER)
> -    {
> -      sysprof_reader_bswap_frame (sui->reader, frame); /* reverse the 
> earlier bswap */
> -      n_write = write (sui->output_fd, frame, frame->len);
> -      sui->pos += frame->len;
> -      assert ((sui->pos % SYSPROF_CAPTURE_ALIGN) == 0);
> -      if (n_write < 0)
> -       error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), 
> output_path);
> -      return SYSPROF_CB_OK;
> -    }
> -  SysprofCaptureStackUser *ev = (SysprofCaptureStackUser *)frame;
> -  uint8_t *tail_ptr = (uint8_t *)ev;
> -  tail_ptr += sizeof(SysprofCaptureStackUser) + ev->size;
> -  SysprofCaptureUserRegs *regs = (SysprofCaptureUserRegs *)tail_ptr;
> -  if (show_frames)
> -    fprintf(stderr, "\n"); /* extra newline for padding */
> -  Elf *elf = NULL;
> -  Dwfl *dwfl = sysprof_find_dwfl (sui, ev, regs, &elf);
> -  if (dwfl == NULL)
> -    {
> -      if (show_summary)
> -       {
> -         dwfltab_ent *dwfl_ent = dwfltab_find(frame->pid);
> -         dwfl_ent->total_samples++;
> -         dwfl_ent->lost_samples++;
> -       }
> -      if (show_failures)
> -       fprintf(stderr, "sysprof_find_dwfl pid %lld (%s) (failed)\n",
> -               (long long)frame->pid, comm);
> -      return SYSPROF_CB_OK;
> -    }
> -  sui->n_addrs = 0;
> -  sui->last_abi = regs->abi;
> -#ifdef DEBUG_MODULES
> -  sui->last_dwfl = dwfl;
> -#endif
> -  sui->last_pid = frame->pid;
> -  uint64_t regs_mask = ebl_perf_frame_regs_mask (default_ebl);
> -  int rc = dwflst_perf_sample_getframes (dwfl, elf, frame->pid, ev->tid,
> -                                        (uint8_t *)&ev->data, ev->size,
> -                                        regs->regs, regs->n_regs,
> -                                        regs_mask, regs->abi,
> -                                        sysprof_unwind_frame_cb, sui);
> -  if (rc < 0)
> -    {
> -      if (show_failures)
> -       fprintf(stderr, "dwflst_perf_sample_getframes pid %lld: %s\n",
> -               (long long)frame->pid, dwfl_errmsg(-1));
> -    }
> -  if (show_samples)
> -    {
> -      bool is_abi32 = (regs->abi == PERF_SAMPLE_REGS_ABI_32);
> -      fprintf(stderr, N_("sysprof_unwind_cb pid %lld (%s)%s: unwound %d 
> frames\n"),
> -             (long long)frame->pid, comm, is_abi32 ? " (32-bit)" : "", 
> sui->n_addrs);
> -    }
> -  if (show_summary)
> -    {
> -      /* For final diagnostics. */
> -      dwfltab_ent *dwfl_ent = dwfltab_find(frame->pid);
> -      if (dwfl_ent != NULL && sui->n_addrs > dwfl_ent->max_frames)
> -       dwfl_ent->max_frames = sui->n_addrs;
> -      dwfl_ent->total_samples++;
> -      if (sui->n_addrs <= 2)
> -       dwfl_ent->lost_samples ++;
> -    }
> -
> -  /* Assemble and output callchain frame. */
> -  /* XXX assert(sizeof(Dwarf_Addr) == sizeof(SysprofCaptureAddress)); */
> -  SysprofCaptureSample *ev_callchain;
> -  size_t len = sizeof *ev_callchain + (sui->n_addrs * sizeof(Dwarf_Addr));
> -  ev_callchain = (SysprofCaptureSample *)sui->outbuf;
> -  if (len > USHRT_MAX)
> -    {
> -      if (show_failures)
> -       fprintf(stderr, N_("sysprof_unwind_cb frame size %ld is too large 
> (max %d)\n"),
> -               len, USHRT_MAX);
> -      return SYSPROF_CB_OK;
> -    }
> -  SysprofCaptureFrame *out_frame = (SysprofCaptureFrame *)ev_callchain;
> -  out_frame->len = len;
> -  out_frame->cpu = ev->frame.cpu;
> -  out_frame->pid = ev->frame.pid;
> -  out_frame->time = ev->frame.time;
> -  out_frame->type = SYSPROF_CAPTURE_FRAME_SAMPLE;
> -  out_frame->padding1 = 0;
> -  out_frame->padding2 = 0;
> -  ev_callchain->n_addrs = sui->n_addrs;
> -  ev_callchain->tid = ev->tid;
> -  memcpy (ev_callchain->addrs, sui->addrs, (sui->n_addrs * 
> sizeof(SysprofCaptureAddress)));
> -  n_write = write (sui->output_fd, ev_callchain, len);
> -  if (n_write < 0)
> -    error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), 
> output_path);
> -  return SYSPROF_CB_OK;
> -}
> -
> -#endif /* HAVE_SYSPROF_HEADERS */
> -
> -/****************
> - * Main program *
> - ****************/
> -
> -/* Required to match our signal handling with that of a sysprof parent 
> process. */
> -static void sigint_handler (int signo __attribute__ ((unused)))
> -{
> -  if (signal_count >= 2)
> -    {
> -      exit(1);
> -    }
> -
> -  if (signal_count == 0)
> -    {
> -      fprintf (stderr, "%s\n", N_("Waiting for input to finish. Press twice 
> more ^C to force exit."));
> -    }
> -
> -  signal_count ++;
> -}
> -
> -static error_t
> -parse_opt (int key, char *arg __attribute__ ((unused)),
> -          struct argp_state *state)
> -{
> -  switch (key)
> -    {
> -    case 'i':
> -      input_path = arg;
> -      break;
> -
> -    case 'o':
> -      output_path = arg;
> -      break;
> -
> -    case 'm':
> -      if (strcmp (arg, "none") == 0)
> -       {
> -         processing_mode = MODE_NONE;
> -       }
> -      else if (strcmp (arg, "passthru") == 0)
> -       {
> -         processing_mode = MODE_PASSTHRU;
> -       }
> -      else if (strcmp (arg, "naive") == 0)
> -       {
> -         processing_mode = MODE_NAIVE;
> -       }
> -      else
> -       {
> -         argp_error (state, N_("Unsupported -m '%s', should be " MODE_OPTS 
> "."), arg);
> -       }
> -      break;
> -
> -    case 'f':
> -      if (strcmp (arg, "sysprof") == 0)
> -       {
> -         input_format = FORMAT_SYSPROF;
> -       }
> -      else
> -       {
> -         argp_error (state, N_("Unsupported -f '%s', should be " FORMAT_OPTS 
> "."), arg);
> -       }
> -      break;
> -
> -    case OPT_DEBUG:
> -      show_frames = true;
> -      FALLTHROUGH;
> -    case 'v':
> -      show_samples = true;
> -      show_failures = true;
> -      show_summary = true;
> -      break;
> -
> -    case ARGP_KEY_END:
> -      if (input_path == NULL)
> -       input_path = "-"; /* default to stdin */
> -
> -      if (output_path == NULL)
> -       output_path = "-"; /* default to stdout */
> -
> -      if (processing_mode == 0)
> -       processing_mode = MODE_NAIVE;
> -
> -      if (input_format == 0)
> -       input_format = FORMAT_SYSPROF;
> -      break;
> -
> -    default:
> -      return ARGP_ERR_UNKNOWN;
> -    }
> -  return 0;
> -}
> -
> -int
> -main (int argc, char **argv)
> -{
> -  /* Set locale. */
> -  (void) setlocale (LC_ALL, "");
> -
> -  const struct argp_option options[] =
> -    {
> -      { NULL, 0, NULL, 0, N_("Input and output selection options:"), 0 },
> -      { "input", 'i', "PATH", 0,
> -       N_("File or FIFO to read stack samples from"), 0 },
> -      /* TODO: Should also support taking an FD for fork/exec pipes. */
> -      { "output", 'o', "PATH", 0,
> -       N_("File or FIFO to send stack traces to"), 0 },
> -
> -      { NULL, 0, NULL, 0, N_("Processing options:"), 0 },
> -      { "mode", 'm', MODE_OPTS, 0,
> -       N_("Processing mode, default 'naive'"), 0 },
> -      /* TODO: Should also support 'naive', 'caching'. */
> -      /* TODO: Add an option to control stack-stitching. */
> -      { "verbose", 'v', NULL, 0,
> -       N_("Show additional information for each unwound sample"), 0 },
> -      { "debug", OPT_DEBUG, NULL, 0,
> -       N_("Show additional information for each unwound frame"), 0 },
> -      /* TODO: Add a 'quiet' option suppressing summaries + errors.
> -         Perhaps also allow -v, -vv, -vvv in SystemTap style? */
> -      { "format", 'f', FORMAT_OPTS, 0,
> -       N_("Input data format, default 'sysprof'"), 0 },
> -      /* TODO: Add an option to control output data format separately,
> -        shift to I/O selection section. */
> -      { NULL, 0, NULL, 0, NULL, 0 }
> -    };
> -
> -  const struct argp argp =
> -    {
> -      .options = options,
> -      .parser = parse_opt,
> -      .doc = N_("Process a stream of stack samples into stack traces.\n\
> -\n\
> -Experimental tool, see README.eu-stacktrace in the development branch:\n\
> -https://sourceware.org/cgit/elfutils/tree/README.eu-stacktrace?h=users/serhei/eu-stacktrace\n";)
> -    };
> -
> -  argp_parse(&argp, argc, argv, 0, NULL, NULL);
> -
> -  /* Also handle ELFUTILS_STACKTRACE_VERBOSE_ENV_VAR: */
> -  char *env_verbose = getenv(ELFUTILS_STACKTRACE_VERBOSE_ENV_VAR);
> -  if (env_verbose == NULL || strlen(env_verbose) == 0)
> -    ; /* nop, use command line options */
> -  else if (strcmp(env_verbose, "false") == 0
> -          || strcmp(env_verbose, "0") == 0)
> -    ; /* nop, use command line options */
> -  else if (strcmp(env_verbose, "true") == 0
> -          || strcmp(env_verbose, "verbose") == 0
> -          || strcmp(env_verbose, "1") == 0)
> -    {
> -      show_samples = true;
> -      show_failures = true;
> -      show_summary = true;
> -    }
> -  else if (strcmp(env_verbose, "debug") == 0
> -          || strcmp(env_verbose, "2") == 0)
> -    {
> -      show_frames = true;
> -      show_samples = true;
> -      show_failures = true;
> -      show_summary = true;
> -    }
> -  else
> -    fprintf (stderr, N_("WARNING: Unknown value '%s' in environment variable 
> %s, ignoring\n"),
> -            env_verbose, ELFUTILS_STACKTRACE_VERBOSE_ENV_VAR);
> -
> -  /* TODO: Also handle common expansions e.g. ~/foo instead of 
> /home/user/foo. */
> -  if (strcmp (input_path, "-") == 0)
> -    input_fd = STDIN_FILENO;
> -  else
> -    input_fd = open (input_path, O_RDONLY);
> -  if (input_fd < 0)
> -    error (EXIT_BAD, errno, N_("Cannot open input file or FIFO '%s'"), 
> input_path);
> -  if (strcmp (output_path, "-") == 0)
> -    output_fd = STDOUT_FILENO;
> -  else
> -    output_fd = open (output_path, O_CREAT | O_WRONLY, 0640);
> -  if (output_fd < 0)
> -    error (EXIT_BAD, errno, N_("Cannot open output file or FIFO '%s'"), 
> output_path);
> -
> -  /* TODO: Only really needed if launched from sysprof and inheriting its 
> signals. */
> -  if (signal (SIGINT, sigint_handler) == SIG_ERR)
> -    error (EXIT_BAD, errno, N_("Cannot set signal handler for SIGINT"));
> -
> -#if !(HAVE_SYSPROF_HEADERS)
> -  /* TODO: Should hide corresponding command line options when this is the 
> case? */
> -  error (EXIT_BAD, 0, N_("Sysprof support is not available in this 
> version."));
> -
> -  /* XXX: The following are not specific to the Sysprof backend;
> -     avoid unused-variable warnings while it is the only backend. */
> -  (void)sample_thread_callbacks;
> -  (void)output_format;
> -  (void)maxframes;
> -#else
> -  fprintf(stderr, "\n=== starting eu-stacktrace ===\n");
> -  tracker = dwflst_tracker_begin (&sample_callbacks);
> -
> -  /* TODO: For now, code the processing loop for sysprof only; generalize 
> later. */
> -  assert (input_format == FORMAT_SYSPROF);
> -  assert (output_format == FORMAT_SYSPROF);
> -  SysprofReader *reader = sysprof_reader_begin (input_fd);
> -  ssize_t n_write = write (output_fd, &reader->header, sizeof 
> reader->header);
> -  if (n_write < 0)
> -    error (EXIT_BAD, errno, N_("Write error to file or FIFO '%s'"), 
> output_path);
> -  ptrdiff_t offset;
> -  unsigned long int output_pos = 0;
> -  if (processing_mode == MODE_NONE)
> -    {
> -      struct sysprof_passthru_info sni = { output_fd, reader, sizeof 
> reader->header };
> -      offset = sysprof_reader_getframes (reader, &sysprof_none_cb, &sni);
> -      output_pos = sni.pos;
> -    }
> -  else if (processing_mode == MODE_PASSTHRU)
> -    {
> -      struct sysprof_passthru_info spi = { output_fd, reader, sizeof 
> reader->header };
> -      offset = sysprof_reader_getframes (reader, &sysprof_passthru_cb, &spi);
> -      output_pos = spi.pos;
> -    }
> -  else /* processing_mode == MODE_NAIVE */
> -    {
> -      if (!dwfltab_init())
> -       error (EXIT_BAD, errno, N_("Could not initialize Dwfl table"));
> -
> -      /* TODO: Generalize to other architectures. */
> -      default_ebl = ebl_openbackend_machine(EM_X86_64);
> -
> -      struct sysprof_unwind_info sui;
> -      sui.output_fd = output_fd;
> -      sui.reader = reader;
> -      sui.pos = sizeof reader->header;
> -      sui.n_addrs = 0;
> -      sui.last_base = 0;
> -      sui.last_sp = 0;
> -      sui.last_abi = PERF_SAMPLE_REGS_ABI_NONE;
> -      sui.max_addrs = UNWIND_ADDR_INCREMENT;
> -      sui.addrs = (Dwarf_Addr *)malloc (sui.max_addrs * sizeof(Dwarf_Addr));
> -      sui.outbuf = (void *)malloc (USHRT_MAX * sizeof(uint8_t));
> -      offset = sysprof_reader_getframes (reader, &sysprof_unwind_cb, &sui);
> -      if (show_summary)
> -       {
> -         /* Final diagnostics. */
> -#define PERCENT(x,tot) ((x+tot == 0)?0.0:((double)x)/((double)tot)*100.0)
> -         int total_samples = 0;
> -         int total_lost_samples = 0;
> -         fprintf(stderr, "\n=== final summary ===\n");
> -         for (unsigned idx = 1; idx < default_table.size; idx++)
> -           {
> -             dwfltab_ent *t = default_table.table;
> -             if (!t[idx].used)
> -               continue;
> -             /* XXX worst_unwound gives least preferred unwind method used 
> for this process
> -                (i.e. eh_frame is preferred to dwarf is preferred to ebl) */
> -             fprintf(stderr, N_("%d %s -- max %d frames, received %d 
> samples, lost %d samples (%.1f%%) (last %s, worst %s)\n"),
> -                     t[idx].pid, t[idx].comm, t[idx].max_frames,
> -                     t[idx].total_samples, t[idx].lost_samples,
> -                     PERCENT(t[idx].lost_samples, t[idx].total_samples),
> -                     dwfl_unwound_source_str(t[idx].last_unwound),
> -                     dwfl_unwound_source_str(t[idx].worst_unwound));
> -             total_samples += t[idx].total_samples;
> -             total_lost_samples += t[idx].lost_samples;
> -           }
> -         fprintf(stderr, "===\n");
> -         fprintf(stderr, N_("TOTAL -- received %d samples, lost %d samples, 
> loaded %ld processes\n"),
> -                 total_samples, total_lost_samples,
> -                 default_table.filled /* TODO: after implementing LRU 
> eviction, need to maintain a separate count, e.g. htab->filled + 
> htab->evicted */);
> -       }
> -      output_pos = sui.pos;
> -      free(sui.addrs);
> -      free(sui.outbuf);
> -    }
> -  if (offset < 0 && output_pos <= sizeof reader->header)
> -    error (EXIT_BAD, errno, N_("No frames in file or FIFO '%s'"), 
> input_path);
> -  else if (offset < 0)
> -    error (EXIT_BAD, errno, N_("Error processing file or FIFO '%s' at input 
> offset %ld, output offset %ld"),
> -          input_path, reader->pos, output_pos);
> -  sysprof_reader_end (reader);
> -#endif
> -
> -  if (input_fd != -1)
> -    close (input_fd);
> -  if (output_fd != -1)
> -    close (output_fd);
> -
> -  dwflst_tracker_end (tracker);
> -
> -  return EXIT_OK;
> -}
> --
> 2.53.0
>

Reply via email to