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]

Re: RFC: block of commands


Ping ^ 3 ?

On Sat, 2015-11-21 at 19:42 +0100, Philippe Waroquiers wrote:
> The included patch implements the concept of a 'block of commands'.
> A block of commands is one or more commands, separated by ;.
> A block of command starts with {.
> Block of commands can be included (nested) in block of commands.
> If nested blocks are used, each block must be terminated by }.
> {, } and ;  can be escaped with \ if needed.
> 
> A block of command can optionally start with a control flag that
> controls the behaviour when a command of the block fails.
> The /c flag indicates to output the error and continue the block.
> The /s flag indicates to continue the block, without outputting the error.
> 
> The block of commands can be used in the
>    'thread apply' command,
> to execute more than one command for each thread.
> 
> The backtrace command has been modified to (optionally) accept a
> trailing block of commands.
> 
> Command qualifiers /s have been added to backtrace and thread apply.
> /s qualifier indicates to only output a frame (or thread) info if
> the given block of commands (or command for thread apply) has produced
> some output.
> 
> A block of command can be used in a user defined command, to avoid
> having a command failing to stop the execution of the user defined
> command.
> 
> And finally, such a block can be used interactively, to group some
> commands and e.g. retrieve and execute a group from the history.
> 
> Some example of usage:
>    bt {/c p i
>      => show all frames, show var i (if it exists), otherwise show an error)
> 
>    bt {/s p i
>      => same, but do not show an error
> 
>    bt /s {/s p i
>      => only show the frames where i can be succesfully printed
> 
> 
>    thread apply all bt {/s p i
>      => show a backtrace for all threads, print i in each frame (if it exists)
> 
>    thread apply all /s bt /s {/s p i
>      => show only the threads and thread frames with a var i.
> 
>    thread apply all { set $prevsp = $sp; bt \{ p $ebp - $prevsp\; set $prevsp = $sp \} }
>      => print the size of each frame
> 
>    define robustshow
>    {/c print i }
>    {/c print j }
>    end
>       => define a command that will print i and/or j.
> 
> 
> The patch has been (somewhat) manually tested.
> Not yet done: tests, user manual, NEWS, ChangeLog.
> 
> Comments welcome on the idea and/or the patch.
> 
> Thanks
> 
> Philippe
> 
> diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c
> index cab2336..615e543 100644
> --- a/gdb/cli/cli-decode.c
> +++ b/gdb/cli/cli-decode.c
> @@ -1255,6 +1255,11 @@ find_command_name_length (const char *text)
>    if (*p == '!')
>      return 1;
>  
> +  /* Similarly, recognize '{' as a single character command so that
> +     a command block works as expected.  */
> +  if (*p == '{')
> +    return 1;
> +
>    while (isalnum (*p) || *p == '-' || *p == '_'
>  	 /* Characters used by TUI specific commands.  */
>  	 || *p == '+' || *p == '<' || *p == '>' || *p == '$')
> diff --git a/gdb/stack.c b/gdb/stack.c
> index b825bdf..d8d9791 100644
> --- a/gdb/stack.c
> +++ b/gdb/stack.c
> @@ -1712,10 +1712,14 @@ frame_info (char *addr_exp, int from_tty)
>  }
>  
>  /* Print briefly all stack frames or just the innermost COUNT_EXP
> -   frames.  */
> +   frames.  If cmd != NULL, executes cmd in the context of each
> +   printed stack frame.  If silent, only print stack frames
> +   for which cmd has produced some output.  */
>  
>  static void
> -backtrace_command_1 (char *count_exp, int show_locals, int no_filters,
> +backtrace_command_1 (char *count_exp,
> +		     char *cmd, int silent,
> +		     int show_locals, int no_filters,
>  		     int from_tty)
>  {
>    struct frame_info *fi;
> @@ -1820,8 +1824,42 @@ backtrace_command_1 (char *count_exp, int show_locals, int no_filters,
>      {
>        for (i = 0, fi = trailing; fi && count--; i++, fi = get_prev_frame (fi))
>  	{
> +	  char *cmd_result = NULL;
> +
>  	  QUIT;
>  
> +	  if (cmd != NULL)
> +	    {
> +	      char *saved_cmd;
> +	      struct frame_id frame_id = get_frame_id (fi);
> +
> +	      /* Save a copy of the command in case it is clobbered by
> +		 execute_command.  */
> +	      saved_cmd = xstrdup (cmd);
> +	      make_cleanup (xfree, saved_cmd);
> +
> +	      select_frame (fi);
> +	      cmd_result = execute_command_to_string (cmd, from_tty);
> +
> +	      /* Restore exact command used previously.  */
> +	      strcpy (cmd, saved_cmd);
> +
> +	      /* execute_command (might) invalidates FI.  */
> +	      fi = frame_find_by_id (frame_id);
> +	      if (fi == NULL)
> +		{
> +		  trailing = NULL;
> +		  warning (_("Unable to restore previously selected frame."));
> +		  break;
> +		}
> +	    }
> +
> +	  if (silent && (cmd_result == NULL || *cmd_result == 0))
> +	    {
> +	      xfree (cmd_result);
> +	      continue;
> +	    }
> +
>  	  /* Don't use print_stack_frame; if an error() occurs it probably
>  	     means further attempts to backtrace would fail (on the other
>  	     hand, perhaps the code does or could be fixed to make sure
> @@ -1843,6 +1881,13 @@ backtrace_command_1 (char *count_exp, int show_locals, int no_filters,
>  		  break;
>  		}
>  	    }
> +	  if (cmd_result != NULL)
> +	    {
> +	      if (*cmd_result != 0)
> +		printf_filtered ("%s", cmd_result);
> +	      xfree (cmd_result);
> +	    }
> +
>  
>  	  /* Save the last frame to check for error conditions.  */
>  	  trailing = fi;
> @@ -1871,6 +1916,9 @@ backtrace_command (char *arg, int from_tty)
>  {
>    struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
>    int fulltrace_arg = -1, arglen = 0, argc = 0, no_filters  = -1;
> +  int cmd_arg = -1;
> +  char *cmd = NULL;
> +  int silent_arg = -1;
>    int user_arg = 0;
>  
>    if (arg)
> @@ -1890,29 +1938,39 @@ backtrace_command (char *arg, int from_tty)
>  
>  	  if (no_filters < 0 && subset_compare (argv[i], "no-filters"))
>  	    no_filters = argc;
> +	  else if (fulltrace_arg < 0 && subset_compare (argv[i], "full"))
> +	    fulltrace_arg = argc;
> +	  else if (silent_arg < 0 && subset_compare (argv[i], "/s"))
> +	    silent_arg = argc;
> +	  else if (cmd_arg < 0 && subset_compare ("{", argv[i]))
> +	     {
> +		char *block = strchr(arg, '{');
> +
> +		cmd_arg = argc;
> +		cmd = xmalloc (strlen (block) + 1);
> +		strcpy (cmd, block);
> +		make_cleanup (xfree, cmd);
> +		break;
> +	     }
>  	  else
>  	    {
> -	      if (fulltrace_arg < 0 && subset_compare (argv[i], "full"))
> -		fulltrace_arg = argc;
> -	      else
> -		{
> -		  user_arg++;
> -		  arglen += strlen (argv[i]);
> -		}
> +	      user_arg++;
> +	      arglen += strlen (argv[i]);
>  	    }
>  	  argc++;
>  	}
>        arglen += user_arg;
> -      if (fulltrace_arg >= 0 || no_filters >= 0)
> +      if (fulltrace_arg >= 0 || no_filters >= 0
> +	  || cmd_arg >= 0 || silent_arg > 0)
>  	{
>  	  if (arglen > 0)
>  	    {
>  	      arg = (char *) xmalloc (arglen + 1);
>  	      make_cleanup (xfree, arg);
>  	      arg[0] = 0;
> -	      for (i = 0; i < argc; i++)
> +	      for (i = 0; i < argc && i != cmd_arg; i++)
>  		{
> -		  if (i != fulltrace_arg && i != no_filters)
> +		  if (i != fulltrace_arg && i != no_filters && i != silent_arg)
>  		    {
>  		      strcat (arg, argv[i]);
>  		      strcat (arg, " ");
> @@ -1924,7 +1982,8 @@ backtrace_command (char *arg, int from_tty)
>  	}
>      }
>  
> -  backtrace_command_1 (arg, fulltrace_arg >= 0 /* show_locals */,
> +  backtrace_command_1 (arg, cmd, silent_arg >= 0, /* silent */
> +		       fulltrace_arg >= 0 /* show_locals */,
>  		       no_filters >= 0 /* no frame-filters */, from_tty);
>  
>    do_cleanups (old_chain);
> @@ -2600,7 +2659,12 @@ Print backtrace of all stack frames, or innermost COUNT frames.\n\
>  With a negative argument, print outermost -COUNT frames.\nUse of the \
>  'full' qualifier also prints the values of the local variables.\n\
>  Use of the 'no-filters' qualifier prohibits frame filters from executing\n\
> -on this backtrace.\n"));
> +on this backtrace.\n\
> +After the backtrace arguments and qualifiers, you can specify a block of commands.\n\
> +The block of commands will be executed for each each printed stack frame.\n\
> +Example:    backtrace -5 {/s print i; print j}\n\
> +will print the variables i and j (if they exists) in all printed stack frames.\n\
> +   use    help {    for the syntax of a block of command."));
>    add_com_alias ("bt", "backtrace", class_stack, 0);
>  
>    add_com_alias ("where", "backtrace", class_alias, 0);
> diff --git a/gdb/thread.c b/gdb/thread.c
> index 6d410fb..8fdcfb8 100644
> --- a/gdb/thread.c
> +++ b/gdb/thread.c
> @@ -1596,6 +1596,7 @@ thread_apply_all_command (char *cmd, int from_tty)
>  {
>    struct cleanup *old_chain;
>    char *saved_cmd;
> +  int silent = 0;
>    int tc;
>    struct thread_array_cleanup ta_cleanup;
>  
> @@ -1607,6 +1608,13 @@ thread_apply_all_command (char *cmd, int from_tty)
>        tp_array_compar_ascending = 1;
>      }
>  
> +  if (cmd != NULL
> +      && check_for_argument (&cmd, "/s", strlen ("/s")))
> +    {
> +      cmd = skip_spaces (cmd);
> +      silent = 1;
> +    }
> +
>    if (cmd == NULL || *cmd == '\000')
>      error (_("Please specify a command following the thread ID list"));
>  
> @@ -1652,11 +1660,23 @@ thread_apply_all_command (char *cmd, int from_tty)
>        for (k = 0; k != i; k++)
>          if (thread_alive (tp_array[k]))
>            {
> +	    char *cmd_result = NULL;
> +
>              switch_to_thread (tp_array[k]->ptid);
> -            printf_filtered (_("\nThread %d (%s):\n"), 
> -			     tp_array[k]->num,
> -			     target_pid_to_str (inferior_ptid));
> -            execute_command (cmd, from_tty);
> +	    if (silent)
> +	      cmd_result = execute_command_to_string (cmd, from_tty);
> +	    if (!silent || (cmd_result != NULL && *cmd_result != 0))
> +	      printf_filtered (_("\nThread %d (%s):\n"),
> +			       tp_array[k]->num,
> +			       target_pid_to_str (inferior_ptid));
> +	    if (cmd_result)
> +	      {
> +		if (*cmd_result != 0)
> +		  printf_filtered ("%s", cmd_result);
> +		xfree (cmd_result);
> +	      }
> +	    else
> +	      execute_command (cmd, from_tty);
>  
>              /* Restore exact command used previously.  */
>              strcpy (cmd, saved_cmd);
> @@ -1677,7 +1697,9 @@ thread_apply_command (char *tidlist, int from_tty)
>    if (tidlist == NULL || *tidlist == '\000')
>      error (_("Please specify a thread ID list"));
>  
> -  for (cmd = tidlist; *cmd != '\000' && !isalpha (*cmd); cmd++);
> +  for (cmd = tidlist;
> +       *cmd != '\000' && !isalpha (*cmd) && *cmd != '{';
> +       cmd++);
>  
>    if (*cmd == '\000')
>      error (_("Please specify a command following the thread ID list"));
> diff --git a/gdb/top.c b/gdb/top.c
> index d1e2271..b553028 100644
> --- a/gdb/top.c
> +++ b/gdb/top.c
> @@ -534,6 +534,151 @@ execute_command_to_string (char *p, int from_tty)
>    return retval;
>  }
>  
> +static void
> +execute_one_block_command (char *cmd, int from_tty,
> +			   int continue_on_failure, int silent_failure)
> +{
> +  if (continue_on_failure)
> +    {
> +      TRY
> +	{
> +	  execute_command (cmd, from_tty);
> +	}
> +      CATCH (e, RETURN_MASK_ERROR)
> +	{
> +	  if (!silent_failure)
> +	    exception_print (gdb_stderr, e);
> +	}
> +      END_CATCH
> +	}
> +  else
> +    execute_command (cmd, from_tty);
> +}
> +
> +/* Parses in cmds optional /CONTROL, then executes each command part
> +   of the block. Returns the position in cmds after the block end.  */
> +static void
> +commands_block_command (char *cmds, int from_tty)
> +{
> +  int continue_on_failure = 0;
> +  int silent_failure = 0;
> +  int level = 0;
> +  char *cmd_end = NULL;
> +
> +#if 0
> +#define XX(msg) printf("%s cmds%s <%s> %p cmd_end <%s> %p\n", \
> +		       msg,					\
> +		       silent_failure ? "/s" :			\
> +		       continue_on_failure ? "/c" : "",		\
> +		       cmds, cmds, cmd_end, cmd_end)
> +#else
> +#define XX(msg)
> +#endif
> +
> +  cmds = skip_spaces (cmds);
> +
> +  if (cmds == NULL || *cmds == 0)
> +    error (_("Please specify one or more commands separated by ;,\n"
> +	     "optionally followed by a block end }"));
> +
> +  /* Parses the optional /CONTROL.  */
> +  if (*cmds == '/')
> +    {
> +      cmds++;
> +      while (*cmds) {
> +	if (*cmds == ' ')
> +	  break;
> +	else if (*cmds == 's')
> +	  continue_on_failure = silent_failure = 1;
> +	else if (*cmds == 'c')
> +	  continue_on_failure = 1;
> +	else
> +	  error (_("Invalid commands block control flag %c"), *cmds);
> +	cmds++;
> +      }
> +    }
> +
> +  cmds = skip_spaces (cmds);
> +  cmd_end = cmds;
> +  level = 0;
> +  XX("enter");
> +  while (*cmd_end)
> +    {
> +      if (*cmd_end == '\\'
> +	  && (*(cmd_end+1) == '{'
> +	      || *(cmd_end+1) == '}'
> +	      || *(cmd_end+1) == ';'))
> +	{
> +	  char *p, *q;
> +
> +	  p = cmd_end;
> +	  q = cmd_end+1;
> +	  while (*q)
> +	    *p++ = *q++;
> +	  cmd_end++;
> +	}
> +      else if (*cmd_end == '{')
> +	{
> +	  /* Found the begin of a nested block.  */
> +
> +	  /* Execute last command of including block, if any.  */
> +	  if (cmds <= cmd_end)
> +	    {
> +	      *cmd_end = 0;
> +	      XX("prev block lastcmd");
> +	      execute_one_block_command (cmds, from_tty,
> +					 continue_on_failure, silent_failure);
> +	      *cmd_end = '{';
> +	    }
> +
> +	  /* Search for the nested block end: either a } or end of cmds.  */
> +	  cmds = cmd_end;
> +	  while (*cmd_end) {
> +	    if (*cmd_end == '{')
> +	      level++;
> +	    else if (*cmd_end == '}')
> +	      level--;
> +	    if (level == 0)
> +	      break;
> +	    cmd_end++;
> +	  }
> +
> +	  /* recursively executes the block.  */
> +	  *cmd_end = 0;
> +	  XX("block");
> +	  execute_one_block_command (cmds, from_tty,
> +				     continue_on_failure, silent_failure);
> +	  if (level == 0)
> +	    cmd_end++;
> +	  cmds = cmd_end;
> +	}
> +      else if ((*cmd_end == ';' || *cmd_end == '}') && level == 0)
> +	{
> +	  /*  When encountering a command terminator or a block end,
> +	      executes this command.  Note : the block end comparison
> +	      is needed to execute the last command of a block, if
> +	      this command is not terminated by ;.  */
> +	  *cmd_end = 0;
> +	  XX("cmd");
> +	  execute_one_block_command (cmds, from_tty,
> +				     continue_on_failure, silent_failure);
> +	  cmd_end++;
> +	  cmds = cmd_end;
> +	}
> +      else
> +	cmd_end++;
> +    }
> +
> +  /* execute last command of this block, if any */
> +  if (cmds <= cmd_end && *cmds)
> +    {
> +      XX("lastcmd");
> +      execute_one_block_command (cmds, from_tty,
> +				 continue_on_failure, silent_failure);
> +    }
> +#undef XX
> +}
> +
>  /* Read commands from `instream' and execute them
>     until end of file or error reading instream.  */
>  
> @@ -1942,6 +2087,17 @@ Don't repeat this command.\nPrimarily \
>  used inside of user-defined commands that should not be repeated when\n\
>  hitting return."));
>  
> +  add_com ("{", class_support,
> +	   commands_block_command, _("\
> +Executes a block of commands : {/CONTROL CMD1; CMD2; ... }.\n\
> +Commands are separated by the character ';'.\n\
> +Nested blocks can be terminated by the character '}'.\n\
> +By default, the execution of a block stops if a CMD fails.\n\
> +/CONTROL allows to control the execution in case of a CMD failure:\n\
> +  /c indicates to report the error and continue the block execution.\n\
> +  /s indicates to silently continue the block execution.\n\
> +You can use '\\' to escape '{', '}' and ';'."));
> +
>    add_setshow_boolean_cmd ("editing", class_support,
>  			   &async_command_editing_p, _("\
>  Set editing of command lines as they are typed."), _("\
> 
> 
> 
> 



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