This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA_v2 2/8] Implement frame apply [all | COUNT | -COUNT] [FLAGS...] COMMAND.


Implement frame apply [all | COUNT | -COUNT] [FLAGS...] COMMAND.
Also implement the command 'faas COMMAND', a shortcut for
'frame apply all -s COMMAND'.

Note: the syntax of 'frame apply' to specify some innermost or outermost
frames is similar to 'backtrace' command.
An alternative could be to make 'frame apply' similar to
'thread apply'. This was not chosen as:
  * the typical use cases for frame apply are all/some innermost/some outermost
    frames.
  * a range based syntax would have obliged the user to use absolute numbers
    to specify the outermost N frames, which is cumbersone.
    Or an syntax different from the thread range would have been needed
    (e.g. -0-4 to specify the 5 outermost frames).
So, making frame apply similar to backtrace is found better.

Th new command 'frame apply' allows to apply a COMMAND to a number of frames,
or to all frames.
The optional FLAGS... arguments allow to control what output to produce
and how to handle errors raised when applying COMMAND to a frame.

Some examples usages for this new command:
   frame apply all info frame
      Produce info frame for all frames
   frame apply all p $sp
      For each frame, print the location, followed by the frame sp
   frame apply all -qq p $sp
      Same as before, but -qq flags (q = quiet) indicate to only print
      the frames sp.
   frame apply all -vv p $sp
      Same as before, but -vv flags (v = verbose) indicate to print
      location and source line for each frame.
   frame apply all p some_local_var_somewhere
      Print some_local_var_somewhere in all frames. 'frame apply'
      will abort as soon as the print command fails.
   frame apply all -c p some_local_var_somewhere
      Same as before, but -c flag (c = continue) means to
      print the error and continue applying command in case the
      print command fails.
   frame apply all -s p some_local_var_somewhere
      Same as before, but -s flag (s = silent) means to
      be silent for frames where the print command fails.
      In other words, this allows to 'search' the frame in which
      some_local_var_somewhere can be printed.

gdb/ChangeLog
2018-06-04  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* stack.c: (trailing_outermost_frame): New function, mostly
	extracted from backtrace_command_1.
	(backtrace_command_1): Update to call trailing_outermost_frame.
	(frame_apply_command_count): New function.
	(frame_apply_all_command): New function.
	(frame_apply_command): New function.
	(faas_command): New function.
	(frame_cmd_list): New variable.
	(_initialize_stack): Update to setup the new commands 'frame apply'
	and 'faas'.
---
 gdb/stack.c | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 223 insertions(+), 23 deletions(-)

diff --git a/gdb/stack.c b/gdb/stack.c
index 97ebc8bc23..090483cd12 100644
--- a/gdb/stack.c
+++ b/gdb/stack.c
@@ -1687,6 +1687,38 @@ info_frame_command (const char *addr_exp, int from_tty)
   }
 }
 
+/* trailing_outermost_frame returns the starting frame
+   needed to handle COUNT outermost frames.  */
+
+static struct frame_info *
+trailing_outermost_frame (int count)
+{
+  struct frame_info *current;
+  struct frame_info *trailing;
+
+  trailing = get_current_frame ();
+
+  gdb_assert (count > 0);
+
+  current = trailing;
+  while (current != nullptr && count--)
+    {
+      QUIT;
+      current = get_prev_frame (current);
+    }
+
+  /* Will stop when CURRENT reaches the top of the stack.
+     TRAILING will be COUNT below it.  */
+  while (current != nullptr)
+    {
+      QUIT;
+      trailing = get_prev_frame (trailing);
+      current = get_prev_frame (current);
+    }
+
+  return trailing;
+}
+
 /* Print briefly all stack frames or just the innermost COUNT_EXP
    frames.  */
 
@@ -1751,32 +1783,14 @@ backtrace_command_1 (const char *count_exp, frame_filter_flags flags,
 	 variable TRAILING to the frame from which we should start
 	 printing.  Second, it must set the variable count to the number
 	 of frames which we should print, or -1 if all of them.  */
-      trailing = get_current_frame ();
 
       if (count_exp != NULL && count < 0)
 	{
-	  struct frame_info *current;
-
-	  count = -count;
-
-	  current = trailing;
-	  while (current && count--)
-	    {
-	      QUIT;
-	      current = get_prev_frame (current);
-	    }
-
-	  /* Will stop when CURRENT reaches the top of the stack.
-	     TRAILING will be COUNT below it.  */
-	  while (current)
-	    {
-	      QUIT;
-	      trailing = get_prev_frame (trailing);
-	      current = get_prev_frame (current);
-	    }
-
+	  trailing = trailing_outermost_frame (-count);
 	  count = -1;
 	}
+      else
+	trailing = get_current_frame ();
 
       for (fi = trailing; fi && count--; fi = get_prev_frame (fi))
 	{
@@ -2494,9 +2508,160 @@ func_command (const char *arg, int from_tty)
     }
 }
 
+/* Apply a GDB command to a all stack frames, or innermost COUNT frames.
+   With a negative COUNT, apply command on outermost -COUNT frames.
+
+   frame apply 3 info frame     Apply 'info frame' to frames 0, 1, 2
+   frame apply -3 info frame    Apply 'info frame' to outermost 3 frames.
+   frame apply all x/i $pc      Apply 'x/i $pc' cmd to all frames.
+   frame apply all -s p local_var_no_idea_in_which_frame
+                If a frame has a local variable called
+                local_var_no_idea_in_which_frame, print frame
+                and value of local_var_no_idea_in_which_frame.
+   frame apply all -sqq p local_var_no_idea_in_which_frame
+                Same as before, but only print the variable value.  */
+
+/* Apply a GDB command to COUNT stack frames, starting at TRAILING.
+   COUNT -1 means all frames starting at TRAILING.  WHICH_COMMAND is used
+   for error messages.  */
+static void
+frame_apply_command_count (const char* which_command,
+			   const char *cmd, int from_tty,
+			   struct frame_info *trailing, int count)
+{
+  int print_what_v = 2; /* Corresponding to LOCATION.  */
+  enum print_what print_what[5] =
+    {
+      LOC_AND_ADDRESS, /* Should never be used, this is verbosity 0.  */
+      SRC_LINE,
+      LOCATION,
+      LOC_AND_ADDRESS,
+      SRC_AND_LOC
+    };
+  bool cont;
+  bool silent;
+  struct frame_info *fi;
+
+  if (cmd != NULL)
+    check_for_flags_vqcs (which_command, &cmd,
+			  &print_what_v, 4,
+			  &cont, &silent);
+
+  if (cmd == NULL || *cmd == '\0')
+    error (_("Please specify a command to apply on the selected frames"));
+
+  /* The below will restore the current inferior/thread/frame.
+     Usually, only the frame is effectively to be restored.
+     But in case CMD switches of inferior/thread, better restore
+     these also.  */
+  scoped_restore_current_thread restore_thread;
+
+  for (fi = trailing; fi && count--; fi = get_prev_frame (fi))
+    {
+      struct frame_id frame_id = get_frame_id (fi);
+
+      QUIT;
+
+      select_frame (fi);
+      TRY
+	{
+	  std::string cmd_result;
+	  {
+	    /* In case CMD switches of inferior/thread/frame, the below
+	       restores the inferior/thread.  We can then re-initialise
+	       FI based on FRAME_ID.  */
+	    scoped_restore_current_thread restore_fi_current_frame;
+
+	    cmd_result = execute_command_to_string (cmd, from_tty);
+	  }
+	  fi = frame_find_by_id (frame_id);
+	  if (fi == NULL)
+	    {
+	      warning (_("Unable to restore previously selected frame."));
+	      break;
+	    }
+	  if (!silent || cmd_result.length () > 0)
+	    {
+	      if (print_what_v > 0)
+		print_stack_frame (fi, 1, print_what[print_what_v], 0);
+	      printf_filtered ("%s", cmd_result.c_str ());
+	    }
+	}
+      CATCH (ex, RETURN_MASK_ERROR)
+	{
+	  fi = frame_find_by_id (frame_id);
+	  if (fi == NULL)
+	    {
+	      warning (_("Unable to restore previously selected frame."));
+	      break;
+	    }
+	  if (!silent)
+	    {
+	      if (print_what_v > 0)
+		print_stack_frame (fi, 1, print_what [print_what_v], 0);
+	      if (cont)
+		printf_filtered ("%s\n", ex.message);
+	      else
+		throw_exception (ex);
+	    }
+	}
+      END_CATCH;
+    }
+}
+
+static void
+frame_apply_all_command (const char *cmd, int from_tty)
+{
+  if (!target_has_stack)
+    error (_("No stack."));
+
+  frame_apply_command_count ("frame apply all", cmd, from_tty,
+			     get_current_frame (), INT_MAX);
+}
+
+/* Implementation of the "frame apply" command.  */
+
+static void
+frame_apply_command (const char* cmd, int from_tty)
+{
+  int count;
+  struct frame_info *trailing;
+
+  if (!target_has_stack)
+    error (_("No stack."));
+
+  count = get_number_trailer (&cmd, 0);
+
+  if (count < 0)
+    {
+      trailing = trailing_outermost_frame (-count);
+      count = -1;
+    }
+  else
+    trailing = get_current_frame ();
+
+  frame_apply_command_count ("frame apply", cmd, from_tty,
+			     trailing, count);
+}
+
+/* Implementation of the "faas" command.  */
+
+static void
+faas_command (const char *cmd, int from_tty)
+{
+  std::string expanded = std::string ("frame apply all -s ") + std::string (cmd);
+  execute_command (expanded.c_str (), from_tty);
+}
+
+
+/* Commands with a prefix of `frame'.  */
+struct cmd_list_element *frame_cmd_list = NULL;
+
 void
 _initialize_stack (void)
 {
+  static struct cmd_list_element *frame_apply_list = NULL;
+
   add_com ("return", class_stack, return_command, _("\
 Make selected stack frame return to its caller.\n\
 Control remains in the debugger, but when you continue\n\
@@ -2519,14 +2684,49 @@ An argument says how many frames down to go."));
 Same as the `down' command, but does not print anything.\n\
 This is useful in command scripts."));
 
-  add_com ("frame", class_stack, frame_command, _("\
+  add_prefix_cmd ("frame", class_stack, frame_command, _("\
 Select and print a stack frame.\nWith no argument, \
 print the selected stack frame.  (See also \"info frame\").\n\
 An argument specifies the frame to select.\n\
-It can be a stack frame number or the address of the frame."));
+It can be a stack frame number or the address of the frame."),
+		  &frame_cmd_list, "frame ", 1, &cmdlist);
 
   add_com_alias ("f", "frame", class_stack, 1);
 
+#define FRAME_APPLY_FLAGS_HELP "\
+FLAGS are -v (increase verbosity), -q (decrease verbosity)\n\
+  -c (continue), -s (silent).\n\
+Verbosity (default 2) controls what to print for a frame:\n\
+  0 : no frame info is printed\n\
+  1 : source line\n\
+  2 : location\n\
+  3 : location and address\n\
+  4 : source line and location\n\
+By default, if a COMMAND raises an error, frame apply is aborted.\n\
+Flag -c indicates to print the error and continue.\n\
+Flag -s indicates to silently ignore a COMMAND that raises an error\n\
+or produces no output."
+
+  add_prefix_cmd ("apply", class_stack, frame_apply_command,
+		  _("Apply a command to a number of frames.\n\
+Usage: frame apply COUNT [FLAGS...] COMMAND\n\
+With a negative COUNT argument, applies the command on outermost -COUNT frames.\n"
+FRAME_APPLY_FLAGS_HELP),
+		  &frame_apply_list, "frame apply ", 1, &frame_cmd_list);
+
+  add_cmd ("all", class_stack, frame_apply_all_command,
+	   _("\
+Apply a command to all frames.\n\
+\n\
+Usage: frame apply all [FLAGS...] COMMAND\n"
+FRAME_APPLY_FLAGS_HELP),
+	   &frame_apply_list);
+
+  add_com ("faas", class_stack, faas_command, _("\
+Apply a command to all frames (ignoring errors and empty output).\n\
+Usage: faas COMMAND\n\
+shortcut for 'frame apply all -s COMMAND'"));
+
   add_com_suppress_notification ("select-frame", class_stack, select_frame_command, _("\
 Select a stack frame without printing anything.\n\
 An argument specifies the frame to select.\n\
-- 
2.11.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]