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