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.

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