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^2

If not much time, maybe just give feedback about this question:
Should } (termination of the block) be optional or mandatory ?
In the current patch, it is optional (which means that a block of
commands must be given on one line, as the end of line automatically
finishes all unterminated blocks).

If } is mandatory, then I think the parser can be modified to accept
multi-line block of commands.

What is preferrable ?

Thanks

Philippe



On Thu, 2015-12-03 at 23:30 +0100, Philippe Waroquiers wrote:
> Any feedback about the idea below (or the patch) ?
> Thanks
> Philippe
> 
> 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]