[3/4] eu-stacktrace: add unwind origin diagnostics
Commit Message
Track the method used to unwind each Dwfl_Frame using an enum field
unwound_source; provide access to the field. Then use that in
eu-stacktrace to display the unwind methods used for each process.
This is an important diagnostic to verify how many processes are
adequately covered by the eh_frame unwinder.
* libdw/libdw.map (ELFUTILS_0.192): Add dwfl_frame_unwound_source,
dwfl_unwound_source_str.
* libdwfl/libdwfl.h (Dwfl_Unwound_Source): New enum.
(dwfl_frame_unwound_source)
(dwfl_unwound_source_str): New functions.
* libdwfl/dwfl_frame.c (dwfl_frame_unwound_source)
(dwfl_unwound_source_str): New functions.
* libdwfl/dwflP.h: Add INTDECL for dwfl_frame_unwound_source,
dwfl_unwound_source_str.
(struct Dwfl_Frame): Add unwound_source field.
* libdwfl/frame_unwind.c (__libdwfl_frame_unwind): Set
state->unwound_source depending on the unwind method used.
* src/stacktrace.c (struct sysprof_unwind_info): Add last_pid
field to provide access to the current sample's dwfltab entry.
(sysprof_unwind_frame_cb): Add unwound_source to the data displayed
with the show_frames option.
(sysprof_unwind_cb): Set last_pid when processing a sample.
(main): Add unwind_source to the data displayed in the final summary
table.
Signed-off-by: Serhei Makarov <serhei@serhei.io>
---
libdw/libdw.map | 2 ++
libdwfl/dwfl_frame.c | 31 ++++++++++++++++++++++++++++++-
libdwfl/frame_unwind.c | 14 +++++++++++---
libdwfl/libdwfl.h | 20 +++++++++++++++++++-
libdwfl/libdwflP.h | 5 ++++-
src/stacktrace.c | 31 ++++++++++++++++++++++++++-----
6 files changed, 92 insertions(+), 11 deletions(-)
Comments
Hi Serhei,
On Tue, Oct 15, 2024 at 11:28 AM Serhei Makarov <serhei@serhei.io> wrote:
>
> Track the method used to unwind each Dwfl_Frame using an enum field
> unwound_source; provide access to the field. Then use that in
> eu-stacktrace to display the unwind methods used for each process.
>
> This is an important diagnostic to verify how many processes are
> adequately covered by the eh_frame unwinder.
>
> * libdw/libdw.map (ELFUTILS_0.192): Add dwfl_frame_unwound_source,
> dwfl_unwound_source_str.
> * libdwfl/libdwfl.h (Dwfl_Unwound_Source): New enum.
> (dwfl_frame_unwound_source)
> (dwfl_unwound_source_str): New functions.
> * libdwfl/dwfl_frame.c (dwfl_frame_unwound_source)
> (dwfl_unwound_source_str): New functions.
> * libdwfl/dwflP.h: Add INTDECL for dwfl_frame_unwound_source,
Should be libdwflP.h.
> dwfl_unwound_source_str.
> (struct Dwfl_Frame): Add unwound_source field.
> * libdwfl/frame_unwind.c (__libdwfl_frame_unwind): Set
> state->unwound_source depending on the unwind method used.
> * src/stacktrace.c (struct sysprof_unwind_info): Add last_pid
> field to provide access to the current sample's dwfltab entry.
> (sysprof_unwind_frame_cb): Add unwound_source to the data displayed
> with the show_frames option.
> (sysprof_unwind_cb): Set last_pid when processing a sample.
> (main): Add unwind_source to the data displayed in the final summary
> table.
>
> Signed-off-by: Serhei Makarov <serhei@serhei.io>
> ---
> libdw/libdw.map | 2 ++
> libdwfl/dwfl_frame.c | 31 ++++++++++++++++++++++++++++++-
> libdwfl/frame_unwind.c | 14 +++++++++++---
> libdwfl/libdwfl.h | 20 +++++++++++++++++++-
> libdwfl/libdwflP.h | 5 ++++-
> src/stacktrace.c | 31 ++++++++++++++++++++++++++-----
> 6 files changed, 92 insertions(+), 11 deletions(-)
>
> diff --git a/libdw/libdw.map b/libdw/libdw.map
> index 552588a9..bc53385f 100644
> --- a/libdw/libdw.map
> +++ b/libdw/libdw.map
> @@ -382,4 +382,6 @@ ELFUTILS_0.191 {
> ELFUTILS_0.192 {
> global:
> dwfl_set_sysroot;
> + dwfl_frame_unwound_source;
> + dwfl_unwound_source_str;
> } ELFUTILS_0.191;
> diff --git a/libdwfl/dwfl_frame.c b/libdwfl/dwfl_frame.c
> index 8af8843f..2e6c6de8 100644
> --- a/libdwfl/dwfl_frame.c
> +++ b/libdwfl/dwfl_frame.c
> @@ -1,5 +1,5 @@
> /* Get Dwarf Frame state for target PID or core file.
> - Copyright (C) 2013, 2014 Red Hat, Inc.
> + Copyright (C) 2013, 2014, 2024 Red Hat, Inc.
> This file is part of elfutils.
>
> This file is free software; you can redistribute it and/or modify
> @@ -98,6 +98,7 @@ state_alloc (Dwfl_Thread *thread)
> state->signal_frame = false;
> state->initial_frame = true;
> state->pc_state = DWFL_FRAME_STATE_ERROR;
> + state->unwound_source = DWFL_UNWOUND_INITIAL_FRAME;
> memset (state->regs_set, 0, sizeof (state->regs_set));
> thread->unwound = state;
> state->unwound = NULL;
> @@ -248,6 +249,34 @@ dwfl_frame_thread (Dwfl_Frame *state)
> }
> INTDEF(dwfl_frame_thread)
>
> +Dwfl_Unwound_Source
> +dwfl_frame_unwound_source (Dwfl_Frame *state)
> +{
> + return state->unwound_source;
> +}
> +INTDEF(dwfl_frame_unwound_source)
> +
> +const char *
> +dwfl_unwound_source_str (Dwfl_Unwound_Source unwound_source)
> +{
> + switch (unwound_source)
> + {
> + case DWFL_UNWOUND_NONE:
> + return "none";
> + case DWFL_UNWOUND_INITIAL_FRAME:
> + return "initial";
> + case DWFL_UNWOUND_EH_CFI:
> + return "eh_frame";
> + case DWFL_UNWOUND_DWARF_CFI:
> + return "dwarf";
> + case DWFL_UNWOUND_EBL:
> + return "ebl";
> + default:
> + return "unknown";
> + }
> +}
> +INTDEF(dwfl_unwound_source_str)
> +
> int
> dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
> void *arg)
> diff --git a/libdwfl/frame_unwind.c b/libdwfl/frame_unwind.c
> index ab444d25..de65e09c 100644
> --- a/libdwfl/frame_unwind.c
> +++ b/libdwfl/frame_unwind.c
> @@ -1,5 +1,5 @@
> /* Get previous frame state for an existing frame state.
> - Copyright (C) 2013, 2014, 2016 Red Hat, Inc.
> + Copyright (C) 2013, 2014, 2016, 2024 Red Hat, Inc.
> This file is part of elfutils.
>
> This file is free software; you can redistribute it and/or modify
> @@ -515,6 +515,7 @@ new_unwound (Dwfl_Frame *state)
> unwound->signal_frame = false;
> unwound->initial_frame = false;
> unwound->pc_state = DWFL_FRAME_STATE_ERROR;
> + unwound->unwound_source = DWFL_UNWOUND_NONE;
> memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
> return unwound;
> }
> @@ -742,14 +743,20 @@ __libdwfl_frame_unwind (Dwfl_Frame *state)
> {
> handle_cfi (state, pc - bias, cfi_eh, bias);
> if (state->unwound)
> - return;
> + {
> + state->unwound->unwound_source = DWFL_UNWOUND_EH_CFI;
> + return;
> + }
> }
> Dwarf_CFI *cfi_dwarf = INTUSE(dwfl_module_dwarf_cfi) (mod, &bias);
> if (cfi_dwarf)
> {
> handle_cfi (state, pc - bias, cfi_dwarf, bias);
> if (state->unwound)
> - return;
> + {
> + state->unwound->unwound_source = DWFL_UNWOUND_DWARF_CFI;
> + return;
> + }
> }
> }
> assert (state->unwound == NULL);
> @@ -774,6 +781,7 @@ __libdwfl_frame_unwind (Dwfl_Frame *state)
> // __libdwfl_seterrno has been called above.
> return;
> }
> + state->unwound->unwound_source = DWFL_UNWOUND_EBL;
> assert (state->unwound->pc_state == DWFL_FRAME_STATE_PC_SET);
> state->unwound->signal_frame = signal_frame;
> }
> diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
> index 4cbeab55..ffd951db 100644
> --- a/libdwfl/libdwfl.h
> +++ b/libdwfl/libdwfl.h
> @@ -1,5 +1,5 @@
> /* Interfaces for libdwfl.
> - Copyright (C) 2005-2010, 2013 Red Hat, Inc.
> + Copyright (C) 2005-2010, 2013, 2024 Red Hat, Inc.
> This file is part of elfutils.
>
> This file is free software; you can redistribute it and/or modify
> @@ -49,6 +49,17 @@ typedef struct Dwfl_Thread Dwfl_Thread;
> PC location described by an FDE belonging to Dwfl_Thread. */
> typedef struct Dwfl_Frame Dwfl_Frame;
>
> +/* This identifies the method used to unwind a particular Dwfl_Frame: */
> +typedef enum {
> + DWFL_UNWOUND_NONE = 0,
> + DWFL_UNWOUND_INITIAL_FRAME,
> + DWFL_UNWOUND_EH_CFI,
> + DWFL_UNWOUND_DWARF_CFI,
> + DWFL_UNWOUND_EBL,
> + DWFL_UNWOUND_UNKNOWN,
> + DWFL_UNWOUND_NUM,
> +} Dwfl_Unwound_Source;
It might be worth adding a "Keep this the last entry." comment right
above DWFL_UNWOUND_NUM. This is done for libelf.h enums.
Also is DWFL_UNWOUND_UNKNOWN needed? It isn't used anywhere.
> +
> /* Handle for debuginfod-client connection. */
> #ifndef _ELFUTILS_DEBUGINFOD_CLIENT_TYPEDEF
> typedef struct debuginfod_client debuginfod_client;
> @@ -748,6 +759,13 @@ pid_t dwfl_thread_tid (Dwfl_Thread *thread)
> Dwfl_Thread *dwfl_frame_thread (Dwfl_Frame *state)
> __nonnull_attribute__ (1);
>
> +/* Return unwind method for frame STATE. This function never fails. */
> +Dwfl_Unwound_Source dwfl_frame_unwound_source (Dwfl_Frame *state)
> + __nonnull_attribute__ (1);
> +
> +/* Return a string suitable for printing based on UNWOUND_SOURCE. */
> +const char *dwfl_unwound_source_str (Dwfl_Unwound_Source unwound_source);
> +
> /* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation.
> For every known continuous block of registers <FIRSTREG..FIRSTREG+NREGS)
> (inclusive..exclusive) set their content to REGS (array of NREGS items).
> diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
> index d0a5f056..6ec5c966 100644
> --- a/libdwfl/libdwflP.h
> +++ b/libdwfl/libdwflP.h
> @@ -1,5 +1,5 @@
> /* Internal definitions for libdwfl.
> - Copyright (C) 2005-2015, 2018 Red Hat, Inc.
> + Copyright (C) 2005-2015, 2018, 2024 Red Hat, Inc.
> This file is part of elfutils.
>
> This file is free software; you can redistribute it and/or modify
> @@ -272,6 +272,7 @@ struct Dwfl_Frame
> outermost frame. */
> DWFL_FRAME_STATE_PC_UNDEFINED
> } pc_state;
> + Dwfl_Unwound_Source unwound_source;
> /* Either initialized from appropriate REGS element or on some archs
> initialized separately as the return address has no DWARF register. */
> Dwarf_Addr pc;
> @@ -795,6 +796,8 @@ INTDECL (dwfl_pid)
> INTDECL (dwfl_thread_dwfl)
> INTDECL (dwfl_thread_tid)
> INTDECL (dwfl_frame_thread)
> +INTDECL (dwfl_frame_unwound_source)
> +INTDECL (dwfl_unwound_source_str)
> INTDECL (dwfl_thread_state_registers)
> INTDECL (dwfl_thread_state_register_pc)
> INTDECL (dwfl_getthread_frames)
> diff --git a/src/stacktrace.c b/src/stacktrace.c
> index ebd914e5..369c8c2d 100644
> --- a/src/stacktrace.c
> +++ b/src/stacktrace.c
> @@ -640,6 +640,8 @@ typedef struct
> 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
> @@ -849,6 +851,7 @@ struct sysprof_unwind_info
> #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;
> };
> @@ -1137,9 +1140,24 @@ sysprof_unwind_frame_cb (Dwfl_Frame *state, void *arg)
> }
> #endif
>
> - if (show_frames)
> - fprintf(stderr, "* frame %d: pc_adjusted=%lx sp=%lx+(%lx)\n",
> - sui->n_addrs, pc_adjusted, sui->last_base, sp - sui->last_base);
> + 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;
What does worst_unwound mean? It looks like the enum values are ordered.
A comment that describes this ordering would be helpful.
> + 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)
> {
> @@ -1231,6 +1249,7 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
> #ifdef DEBUG_MODULES
> sui->last_dwfl = dwfl;
> #endif
> + sui->last_pid = frame->pid;
> int rc = dwfl_getthread_frames (dwfl, ev->tid, sysprof_unwind_frame_cb, sui);
> if (rc < 0)
> {
> @@ -1535,10 +1554,12 @@ Utility is a work-in-progress, see README.eu-stacktrace in the source branch.")
> dwfltab_ent *t = default_table.table;
> if (!t[idx].used)
> continue;
> - fprintf(stderr, N_("%d %s -- max %d frames, received %d samples, lost %d samples (%.1f%%)\n"),
> + 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));
> + 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;
> }
> --
> 2.46.2
>
On Wed, Oct 16, 2024, at 11:39 PM, Aaron Merey wrote:
> Hi Serhei,
>
> On Tue, Oct 15, 2024 at 11:28 AM Serhei Makarov <serhei@serhei.io> wrote:
>>
>> Track the method used to unwind each Dwfl_Frame using an enum field
>> unwound_source; provide access to the field. Then use that in
>> eu-stacktrace to display the unwind methods used for each process.
>>
>> This is an important diagnostic to verify how many processes are
>> adequately covered by the eh_frame unwinder.
>>
>> * libdw/libdw.map (ELFUTILS_0.192): Add dwfl_frame_unwound_source,
>> dwfl_unwound_source_str.
>> * libdwfl/libdwfl.h (Dwfl_Unwound_Source): New enum.
>> (dwfl_frame_unwound_source)
>> (dwfl_unwound_source_str): New functions.
>> * libdwfl/dwfl_frame.c (dwfl_frame_unwound_source)
>> (dwfl_unwound_source_str): New functions.
>> * libdwfl/dwflP.h: Add INTDECL for dwfl_frame_unwound_source,
>
> Should be libdwflP.h.
Fixed.
> It might be worth adding a "Keep this the last entry." comment right
> above DWFL_UNWOUND_NUM. This is done for libelf.h enums.
Fixed.
>
> Also is DWFL_UNWOUND_UNKNOWN needed? It isn't used anywhere.
Removed. An earlier version of the code required it, but this one no longer does.
> What does worst_unwound mean? It looks like the enum values are ordered.
> A comment that describes this ordering would be helpful.
Added comments at the fprintf that displays (last %s, worst %s)
and at the enum definition (also important):
"This identifies the method used to unwind a particular Dwfl_Frame.
The enum order matches the order of preference for unwinding
(i.e. eh_frame is preferred to dwarf is preferred to ebl)."
@@ -382,4 +382,6 @@ ELFUTILS_0.191 {
ELFUTILS_0.192 {
global:
dwfl_set_sysroot;
+ dwfl_frame_unwound_source;
+ dwfl_unwound_source_str;
} ELFUTILS_0.191;
@@ -1,5 +1,5 @@
/* Get Dwarf Frame state for target PID or core file.
- Copyright (C) 2013, 2014 Red Hat, Inc.
+ Copyright (C) 2013, 2014, 2024 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -98,6 +98,7 @@ state_alloc (Dwfl_Thread *thread)
state->signal_frame = false;
state->initial_frame = true;
state->pc_state = DWFL_FRAME_STATE_ERROR;
+ state->unwound_source = DWFL_UNWOUND_INITIAL_FRAME;
memset (state->regs_set, 0, sizeof (state->regs_set));
thread->unwound = state;
state->unwound = NULL;
@@ -248,6 +249,34 @@ dwfl_frame_thread (Dwfl_Frame *state)
}
INTDEF(dwfl_frame_thread)
+Dwfl_Unwound_Source
+dwfl_frame_unwound_source (Dwfl_Frame *state)
+{
+ return state->unwound_source;
+}
+INTDEF(dwfl_frame_unwound_source)
+
+const char *
+dwfl_unwound_source_str (Dwfl_Unwound_Source unwound_source)
+{
+ switch (unwound_source)
+ {
+ case DWFL_UNWOUND_NONE:
+ return "none";
+ case DWFL_UNWOUND_INITIAL_FRAME:
+ return "initial";
+ case DWFL_UNWOUND_EH_CFI:
+ return "eh_frame";
+ case DWFL_UNWOUND_DWARF_CFI:
+ return "dwarf";
+ case DWFL_UNWOUND_EBL:
+ return "ebl";
+ default:
+ return "unknown";
+ }
+}
+INTDEF(dwfl_unwound_source_str)
+
int
dwfl_getthreads (Dwfl *dwfl, int (*callback) (Dwfl_Thread *thread, void *arg),
void *arg)
@@ -1,5 +1,5 @@
/* Get previous frame state for an existing frame state.
- Copyright (C) 2013, 2014, 2016 Red Hat, Inc.
+ Copyright (C) 2013, 2014, 2016, 2024 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -515,6 +515,7 @@ new_unwound (Dwfl_Frame *state)
unwound->signal_frame = false;
unwound->initial_frame = false;
unwound->pc_state = DWFL_FRAME_STATE_ERROR;
+ unwound->unwound_source = DWFL_UNWOUND_NONE;
memset (unwound->regs_set, 0, sizeof (unwound->regs_set));
return unwound;
}
@@ -742,14 +743,20 @@ __libdwfl_frame_unwind (Dwfl_Frame *state)
{
handle_cfi (state, pc - bias, cfi_eh, bias);
if (state->unwound)
- return;
+ {
+ state->unwound->unwound_source = DWFL_UNWOUND_EH_CFI;
+ return;
+ }
}
Dwarf_CFI *cfi_dwarf = INTUSE(dwfl_module_dwarf_cfi) (mod, &bias);
if (cfi_dwarf)
{
handle_cfi (state, pc - bias, cfi_dwarf, bias);
if (state->unwound)
- return;
+ {
+ state->unwound->unwound_source = DWFL_UNWOUND_DWARF_CFI;
+ return;
+ }
}
}
assert (state->unwound == NULL);
@@ -774,6 +781,7 @@ __libdwfl_frame_unwind (Dwfl_Frame *state)
// __libdwfl_seterrno has been called above.
return;
}
+ state->unwound->unwound_source = DWFL_UNWOUND_EBL;
assert (state->unwound->pc_state == DWFL_FRAME_STATE_PC_SET);
state->unwound->signal_frame = signal_frame;
}
@@ -1,5 +1,5 @@
/* Interfaces for libdwfl.
- Copyright (C) 2005-2010, 2013 Red Hat, Inc.
+ Copyright (C) 2005-2010, 2013, 2024 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -49,6 +49,17 @@ typedef struct Dwfl_Thread Dwfl_Thread;
PC location described by an FDE belonging to Dwfl_Thread. */
typedef struct Dwfl_Frame Dwfl_Frame;
+/* This identifies the method used to unwind a particular Dwfl_Frame: */
+typedef enum {
+ DWFL_UNWOUND_NONE = 0,
+ DWFL_UNWOUND_INITIAL_FRAME,
+ DWFL_UNWOUND_EH_CFI,
+ DWFL_UNWOUND_DWARF_CFI,
+ DWFL_UNWOUND_EBL,
+ DWFL_UNWOUND_UNKNOWN,
+ DWFL_UNWOUND_NUM,
+} Dwfl_Unwound_Source;
+
/* Handle for debuginfod-client connection. */
#ifndef _ELFUTILS_DEBUGINFOD_CLIENT_TYPEDEF
typedef struct debuginfod_client debuginfod_client;
@@ -748,6 +759,13 @@ pid_t dwfl_thread_tid (Dwfl_Thread *thread)
Dwfl_Thread *dwfl_frame_thread (Dwfl_Frame *state)
__nonnull_attribute__ (1);
+/* Return unwind method for frame STATE. This function never fails. */
+Dwfl_Unwound_Source dwfl_frame_unwound_source (Dwfl_Frame *state)
+ __nonnull_attribute__ (1);
+
+/* Return a string suitable for printing based on UNWOUND_SOURCE. */
+const char *dwfl_unwound_source_str (Dwfl_Unwound_Source unwound_source);
+
/* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation.
For every known continuous block of registers <FIRSTREG..FIRSTREG+NREGS)
(inclusive..exclusive) set their content to REGS (array of NREGS items).
@@ -1,5 +1,5 @@
/* Internal definitions for libdwfl.
- Copyright (C) 2005-2015, 2018 Red Hat, Inc.
+ Copyright (C) 2005-2015, 2018, 2024 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -272,6 +272,7 @@ struct Dwfl_Frame
outermost frame. */
DWFL_FRAME_STATE_PC_UNDEFINED
} pc_state;
+ Dwfl_Unwound_Source unwound_source;
/* Either initialized from appropriate REGS element or on some archs
initialized separately as the return address has no DWARF register. */
Dwarf_Addr pc;
@@ -795,6 +796,8 @@ INTDECL (dwfl_pid)
INTDECL (dwfl_thread_dwfl)
INTDECL (dwfl_thread_tid)
INTDECL (dwfl_frame_thread)
+INTDECL (dwfl_frame_unwound_source)
+INTDECL (dwfl_unwound_source_str)
INTDECL (dwfl_thread_state_registers)
INTDECL (dwfl_thread_state_register_pc)
INTDECL (dwfl_getthread_frames)
@@ -640,6 +640,8 @@ typedef struct
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
@@ -849,6 +851,7 @@ struct sysprof_unwind_info
#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;
};
@@ -1137,9 +1140,24 @@ sysprof_unwind_frame_cb (Dwfl_Frame *state, void *arg)
}
#endif
- if (show_frames)
- fprintf(stderr, "* frame %d: pc_adjusted=%lx sp=%lx+(%lx)\n",
- sui->n_addrs, pc_adjusted, sui->last_base, sp - sui->last_base);
+ 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)
{
@@ -1231,6 +1249,7 @@ sysprof_unwind_cb (SysprofCaptureFrame *frame, void *arg)
#ifdef DEBUG_MODULES
sui->last_dwfl = dwfl;
#endif
+ sui->last_pid = frame->pid;
int rc = dwfl_getthread_frames (dwfl, ev->tid, sysprof_unwind_frame_cb, sui);
if (rc < 0)
{
@@ -1535,10 +1554,12 @@ Utility is a work-in-progress, see README.eu-stacktrace in the source branch.")
dwfltab_ent *t = default_table.table;
if (!t[idx].used)
continue;
- fprintf(stderr, N_("%d %s -- max %d frames, received %d samples, lost %d samples (%.1f%%)\n"),
+ 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));
+ 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;
}