[PATCH 4/4] src/stacktrace.c: remove in favour of stackprof
Aaron Merey
amerey@redhat.com
Fri May 29 19:09:46 GMT 2026
Hi Serhei,
On Mon, Apr 13, 2026 at 5:31 PM Serhei Makarov <serhei@serhei.io> 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: <fche@redhat.com>
> Signed-off-by: <serhei@serhei.io>
> ---
> 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 <chergert@redhat.com>
> -
> - 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
>
More information about the Elfutils-devel
mailing list