[v2,4/5] src: add unwind origin diagnostics to eu-stack

Message ID 96ad3956-0561-495b-a84a-ad875ff5788b@app.fastmail.com
State Committed
Headers
Series [v2,1/5] src: add eu-stacktrace tool |

Commit Message

Serhei Makarov Oct. 17, 2024, 6:54 p.m. UTC
  Since we obtain diagnostics about unwind method, another logical place
to show them is in eu-stack.

* src/stack.c (show_unwound_source): New global variable.
  (struct frame): Add unwound_source field.
  (frame_callback): Copy over unwound_source from Dwfl_Frame.
  (print_frame): Take unwound_source string and print it.
  (print_inline_frames): Take unwound_source argument and pass it on,
  except for subsequent frames where we pass the string "inline".
  (print_frames): Take unwound_source field from struct frame and pass
  it on.
  (parse_opt): Add --cfi-type,-c option to set show_unwound_source.
  (main): Ditto.
* tests/run-stack-i-test.sh: Add testcase for --cfi-type.

Signed-off-by: Serhei Makarov <serhei@serhei.io>
---
 src/stack.c               | 30 +++++++++++++++++++++++-------
 tests/run-stack-i-test.sh | 15 ++++++++++++++-
 2 files changed, 37 insertions(+), 8 deletions(-)
  

Comments

Mark Wielaard Oct. 17, 2024, 7:41 p.m. UTC | #1
Hi Serhei,

On Thu, Oct 17, 2024 at 02:54:34PM -0400, Serhei Makarov wrote:
> Since we obtain diagnostics about unwind method, another logical place
> to show them is in eu-stack.
> 
> * src/stack.c (show_unwound_source): New global variable.
>   (struct frame): Add unwound_source field.
>   (frame_callback): Copy over unwound_source from Dwfl_Frame.
>   (print_frame): Take unwound_source string and print it.
>   (print_inline_frames): Take unwound_source argument and pass it on,
>   except for subsequent frames where we pass the string "inline".
>   (print_frames): Take unwound_source field from struct frame and pass
>   it on.
>   (parse_opt): Add --cfi-type,-c option to set show_unwound_source.
>   (main): Ditto.
> * tests/run-stack-i-test.sh: Add testcase for --cfi-type.

I like this version.

Thanks,

Mark
  
Aaron Merey Oct. 17, 2024, 9:03 p.m. UTC | #2
On Thu, Oct 17, 2024 at 3:41 PM Mark Wielaard <mark@klomp.org> wrote:
>
> Hi Serhei,
>
> On Thu, Oct 17, 2024 at 02:54:34PM -0400, Serhei Makarov wrote:
> > Since we obtain diagnostics about unwind method, another logical place
> > to show them is in eu-stack.
> >
> > * src/stack.c (show_unwound_source): New global variable.
> >   (struct frame): Add unwound_source field.
> >   (frame_callback): Copy over unwound_source from Dwfl_Frame.
> >   (print_frame): Take unwound_source string and print it.
> >   (print_inline_frames): Take unwound_source argument and pass it on,
> >   except for subsequent frames where we pass the string "inline".
> >   (print_frames): Take unwound_source field from struct frame and pass
> >   it on.
> >   (parse_opt): Add --cfi-type,-c option to set show_unwound_source.
> >   (main): Ditto.
> > * tests/run-stack-i-test.sh: Add testcase for --cfi-type.
>
> I like this version.
>

Same here, this is now ready to merge.

Thanks,
Aaron
  

Patch

diff --git a/src/stack.c b/src/stack.c
index eb87f5f1..a06cb077 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -1,5 +1,5 @@ 
 /* Unwinding of frames like gstack/pstack.
-   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
@@ -44,6 +44,7 @@  ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
 static bool show_activation = false;
 static bool show_module = false;
 static bool show_build_id = false;
+static bool show_unwound_source = false;
 static bool show_source = false;
 static bool show_one_tid = false;
 static bool show_quiet = false;
@@ -58,6 +59,7 @@  struct frame
 {
   Dwarf_Addr pc;
   bool isactivation;
+  Dwfl_Unwound_Source unwound_source;
 };
 
 struct frames
@@ -180,6 +182,7 @@  frame_callback (Dwfl_Frame *state, void *arg)
 {
   struct frames *frames = (struct frames *) arg;
   int nr = frames->frames;
+  frames->frame[nr].unwound_source = dwfl_frame_unwound_source (state);
   if (! dwfl_frame_pc (state, &frames->frame[nr].pc,
 		       &frames->frame[nr].isactivation))
     return -1;
@@ -221,7 +224,7 @@  static void
 print_frame (int nr, Dwarf_Addr pc, bool isactivation,
 	     Dwarf_Addr pc_adjusted, Dwfl_Module *mod,
 	     const char *symname, Dwarf_Die *cudie,
-	     Dwarf_Die *die)
+	     Dwarf_Die *die, const char *unwound_source)
 {
   int width = get_addr_width (mod);
   printf ("#%-2u 0x%0*" PRIx64, nr, width, (uint64_t) pc);
@@ -271,6 +274,11 @@  print_frame (int nr, Dwarf_Addr pc, bool isactivation,
 	}
     }
 
+  if (show_unwound_source)
+    {
+      printf (" [%s]", unwound_source);
+    }
+
   if (show_source)
     {
       int line, col;
@@ -323,7 +331,8 @@  print_frame (int nr, Dwarf_Addr pc, bool isactivation,
 static void
 print_inline_frames (int *nr, Dwarf_Addr pc, bool isactivation,
 		     Dwarf_Addr pc_adjusted, Dwfl_Module *mod,
-		     const char *symname, Dwarf_Die *cudie, Dwarf_Die *die)
+		     const char *symname, Dwarf_Die *cudie, Dwarf_Die *die,
+		     Dwfl_Unwound_Source unwound_source)
 {
   Dwarf_Die *scopes = NULL;
   int nscopes = dwarf_getscopes_die (die, &scopes);
@@ -333,7 +342,7 @@  print_inline_frames (int *nr, Dwarf_Addr pc, bool isactivation,
 	 the name.  This is the actual source location where it
 	 happened.  */
       print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname,
-		   NULL, NULL);
+		   NULL, NULL, dwfl_unwound_source_str(unwound_source));
 
       /* last_scope is the source location where the next frame/function
 	 call was done. */
@@ -349,7 +358,7 @@  print_inline_frames (int *nr, Dwarf_Addr pc, bool isactivation,
 
 	  symname = die_name (scope);
 	  print_frame ((*nr)++, pc, isactivation, pc_adjusted, mod, symname,
-		       cudie, last_scope);
+		       cudie, last_scope, "inline");
 
 	  /* Found the "top-level" in which everything was inlined?  */
 	  if (tag == DW_TAG_subprogram)
@@ -375,6 +384,7 @@  print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
       Dwarf_Addr pc = frames->frame[nr].pc;
       bool isactivation = frames->frame[nr].isactivation;
       Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+      Dwfl_Unwound_Source unwound_source = frames->frame[nr].unwound_source;
 
       /* Get PC->SYMNAME.  */
       Dwfl_Module *mod = dwfl_addrmodule (dwfl, pc_adjusted);
@@ -417,10 +427,10 @@  print_frames (struct frames *frames, pid_t tid, int dwflerr, const char *what)
 
       if (show_inlines && die != NULL)
 	print_inline_frames (&frame_nr, pc, isactivation, pc_adjusted, mod,
-			     symname, cudie, die);
+			     symname, cudie, die, unwound_source);
       else
 	print_frame (frame_nr++, pc, isactivation, pc_adjusted, mod, symname,
-		     NULL, NULL);
+		     NULL, NULL, dwfl_unwound_source_str(unwound_source));
     }
 
   if (frames->frames > 0 && frame_nr == maxframes)
@@ -535,6 +545,10 @@  parse_opt (int key, char *arg __attribute__ ((unused)),
       show_build_id = true;
       break;
 
+    case 'c':
+      show_unwound_source = true;
+      break;
+
     case 'q':
       show_quiet = true;
       break;
@@ -676,6 +690,8 @@  main (int argc, char **argv)
 	N_("Show raw function symbol names, do not try to demangle names"), 0 },
       { "build-id",  'b', NULL, 0,
 	N_("Show module build-id, load address and pc offset"), 0 },
+      { "cfi-type", 'c', NULL, 0,
+	N_("Show the backtrace method for each frame (eh_frame, dwarf, inline, or ebl)"), 0 },
       { NULL, '1', NULL, 0,
 	N_("Show the backtrace of only one thread"), 0 },
       { NULL, 'n', "MAXFRAMES", 0,
diff --git a/tests/run-stack-i-test.sh b/tests/run-stack-i-test.sh
index 3722ab09..bc46d9d5 100755
--- a/tests/run-stack-i-test.sh
+++ b/tests/run-stack-i-test.sh
@@ -1,5 +1,5 @@ 
 #! /bin/sh
-# Copyright (C) 2014, 2015 Red Hat, Inc.
+# Copyright (C) 2014, 2015, 2024 Red Hat, Inc.
 # This file is part of elfutils.
 #
 # This file is free software; you can redistribute it and/or modify
@@ -73,6 +73,19 @@  TID 13654:
 $STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
 EOF
 
+# With --cfi-type we also see what unwind method was used for each frame:
+testrun_compare ${abs_top_builddir}/src/stack -r -n 6 -c -i -e testfiledwarfinlines --core testfiledwarfinlines.core<<EOF
+PID 13654 - core
+TID 13654:
+#0  0x00000000004006c8 fubar [initial]
+#1  0x00000000004006c8 foobar [inline]
+#2  0x00000000004006c8 bar [inline]
+#3  0x00000000004006c8 foo [inline]
+#4  0x00000000004006c8 _Z2fui [inline]
+#5  0x00000000004004c5 main [eh_frame]
+$STACKCMD: tid 13654: shown max number of frames (6, use -n 0 for unlimited)
+EOF
+
 if [ "x$SAVED_VALGRIND_CMD" != "x" ]; then
   VALGRIND_CMD="$SAVED_VALGRIND_CMD"
   export VALGRIND_CMD