From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 55244 invoked by alias); 21 Dec 2015 13:35:22 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 55214 invoked by uid 89); 21 Dec 2015 13:35:21 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.7 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 spammy=sk:backtra, no-filters, nofilters, UD:be X-HELO: mailsec116.isp.belgacom.be Received: from mailsec116.isp.belgacom.be (HELO mailsec116.isp.belgacom.be) (195.238.20.112) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 21 Dec 2015 13:35:17 +0000 X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: A2BwBgDY/ndW/5DT81FdgzqOEbMRhg0CgSU8EQEBAQEBAQGBCoQ0AQEBAwEjBC8oCwgDGAICJgICOR4GLYgNDKwYhlYBBwEBAQkCAQSLahlohVWEfod3gUkFlwCNS4FchEWDKJNjOCyCER2BVz2FYAEBAQ Received: from 144.211-243-81.adsl-dyn.isp.belgacom.be (HELO soleil) ([81.243.211.144]) by relay.skynet.be with ESMTP/TLS/AES128-GCM-SHA256; 21 Dec 2015 14:35:13 +0100 Message-ID: <1450705028.2412.10.camel@skynet.be> Subject: Re: RFC: block of commands From: Philippe Waroquiers To: gdb-patches Date: Mon, 21 Dec 2015 13:35:00 -0000 In-Reply-To: <1449181812.8195.1.camel@skynet.be> References: <1448131372.2153.17.camel@skynet.be> <1449181812.8195.1.camel@skynet.be> Content-Type: text/plain; charset="UTF-8" Mime-Version: 1.0 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2015-12/txt/msg00430.txt.bz2 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."), _("\ > > > > > > > > > > >