From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6174 invoked by alias); 29 Feb 2012 07:04:26 -0000 Received: (qmail 6153 invoked by uid 22791); 29 Feb 2012 07:04:17 -0000 X-SWARE-Spam-Status: No, hits=0.8 required=5.0 tests=BAYES_50,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,RCVD_IN_DNSWL_NONE,TW_EG,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from elasmtp-spurfowl.atl.sa.earthlink.net (HELO elasmtp-spurfowl.atl.sa.earthlink.net) (209.86.89.66) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Wed, 29 Feb 2012 07:03:56 +0000 Received: from [68.96.200.16] (helo=macbook2.local) by elasmtp-spurfowl.atl.sa.earthlink.net with esmtpa (Exim 4.67) (envelope-from ) id 1S2dZr-0006zS-2f for gdb-patches@sourceware.org; Wed, 29 Feb 2012 02:03:55 -0500 Message-ID: <4F4DCDD5.2040807@earthlink.net> Date: Wed, 29 Feb 2012 09:14:00 -0000 From: Stan Shebs User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 MIME-Version: 1.0 To: gdb-patches@sourceware.org Subject: [PATCH] dynamic printf Content-Type: multipart/mixed; boundary="------------020701060001040103040700" X-ELNK-Trace: ae6f8838ff913eba0cc1426638a40ef67e972de0d01da940f189a100ae074d756352c43e05f91530350badd9bab72f9c350badd9bab72f9c350badd9bab72f9c X-IsSubscribed: yes 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 X-SW-Source: 2012-02/txt/msg00690.txt.bz2 This is a multi-part message in MIME format. --------------020701060001040103040700 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 5023 This patch implements a "dynamic printf", which is basically a breakpoint with a printf;continue as its command list - but with additional features that make it more interesting. First, there is an option to send the result to the inferior's output stream rather than to the GDB console; second, there is an option to do the printing in a remote agent/stub; and third, the agent version can continue printf'ing even after GDB detaches(!). (For those who pay attention to the competitive landscape, this is an Ericsson-requested GDBification of a Wind River feature.) The syntax of the command is dprintf ,,... where the location is as for breakpoints, while the format and args are as for the printf command. So you could have something like dprintf myfun "myfun args are %d,%d\n",arg1,arg2 A setshow variable dprintf-style controls whether the printing is done by GDB, the inferior, or by a remote agent, and another variable disconnected-dprintf controls persistence upon disconnection, similarly to disconnected tracing. The patch itself is somewhat of a hack-n-slash through the middle of GDB, and there is much to critique. :-) Partly this is because I wanted to push through to a genuinely useful example of target-side breakpoint commands, rather than doing limited-functionality "commands" in the abstract. It also points up our continuing need for being able to define types of breakpoints by properties rather than adding enums... I borrowed some of Luis' target-side conditional code, and got some inspiration from Hui Zhu's previous try at a printf bytecode. Anyway, without further ado, here's the patch itself. I've skipped the testsuite for now, as I expect everybody will want nontrivial modifications. :-) Stan 2012-02-28 Stan Shebs Add dynamic printf. * breakpoint.h (enum bptype): New type bp_dprintf. (struct bp_target_info): New field commands and persist. (struct bp_location): New field cmd_bytecode. (struct breakpoint): New field extra_string. * breakpoint.c (dprintf_breakpoint_ops): New. (disconnected_dprintf): New global. (is_breakpoint): Add bp_dprintf. (parse_format_string): New function. (parse_cmd_to_aexpr): New function. (build_target_command_list): New function. (insert_bp_location): Call it. (remove_breakpoints_pid): Skip dprintf breakpoints. (bpstat_what): Add dprintf case. (bptype_string): Ditto. (print_one_breakpoint_location): Ditto. (init_bp_location): Ditto. (bkpt_print_mention): Ditto. (dprintf_style_enums): New array. (dprintf_style): New global. (glob_extra_string): New global. (init_breakpoint_sal): Handle extra string. (addr_string_to_sals): Ditto. (find_condition_and_thread): Add extra argument. (create_breakpoint): Save away additional text at end of command. (dprintf_command): New function. (_initialize_breakpoint): Add new commands. * common/ax.def (printf): New bytecode. * ax-gdb.c (gen_printf): New function. (agent_print_command): New function. (_initialize_ax_gdb): Add maint agent-printf command. * ax-gdb.h (gen_printf): Declare. * ax-general.c (ax_print): Add printf disassembly. * remote.c (remote_state) : New field. (PACKET_BreakpointCommandss): New enum. (remote_breakpoint_commands_feature): New function. (remote_protocol_features): Add new BreakpointCommands entry. (remote_can_run_breakpoint_commands): New function. (remote_add_target_side_commands): New function. (remote_insert_breakpoint): Call it. (remote_insert_hw_breakpoint): Ditto. (_initialize_remote): Add new packet configuration for target-side breakpoint commands. * target.h (struct target_ops): New field to_can_run_breakpoint_commands. (target_can_run_breakpoint_commands): New macro. * target.c (update_current_target): Handle to_can_run_breakpoint_commands. [gdbserver] * server.c (handle_query): Claim support for breakpoint commands. (process_point_options): Add command case. (process_serial_event): Leave running if there are printfs in effect. * mem-break.c (struct point_command_list): New struct. (struct breakpoint): New field command_list. (any_persistent_commands): New function. (add_commands_to_breakpoint): New function. (add_breakpoint_commands): New function. (gdb_no_commands_at_breakpoint): New function. (run_breakpoint_commands): New function. * mem-break.h (any_persistent_commands): Declare. (add_breakpoint_commands): Declare. (gdb_no_commands_at_breakpoint): Declare. (run_breakpoint_commands): Declare. * linux-low.c (linux_wait_1): Test for and run breakpoint commands locally. * ax.c (gdb_eval_agent_expr): Add printf opcode. [doc] * gdb.texinfo (Dynamic Printf): New subsection. --------------020701060001040103040700 Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0"; name="dprintf-patch-1" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="dprintf-patch-1" Content-length: 46628 Index: ax-gdb.c =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.c,v retrieving revision 1.92 diff -u -p -r1.92 ax-gdb.c --- ax-gdb.c 7 Feb 2012 04:48:18 -0000 1.92 +++ ax-gdb.c 29 Feb 2012 05:52:13 -0000 @@ -2511,6 +2511,55 @@ gen_trace_for_return_address (CORE_ADDR return ax; } +/* Given a collection of printf-style arguments, generate code to + evaluate the arguments and pass everything to a special + bytecode. */ + +struct agent_expr * +gen_printf (CORE_ADDR scope, int channel, char *format, + int nargs, struct expression **exprs) +{ + struct expression *expr; + struct cleanup *old_chain = 0; + struct agent_expr *ax = new_agent_expr (exprs[0]->gdbarch, scope); + union exp_element *pc; + struct axs_value value; + int tem, bot; + + old_chain = make_cleanup_free_agent_expr (ax); + + /* Evaluate and push the args on the stack in reverse order, + for simplicity of collecting them on the target side. */ + for (tem = nargs - 1; tem >= 0; --tem) + { + pc = exprs[tem]->elts; + /* We're computing values, not doing side effects. */ + trace_kludge = 0; + value.optimized_out = 0; + gen_expr (exprs[tem], &pc, ax, &value); + require_rvalue (ax, &value); + } + + ax_simple (ax, aop_printf); + ax_simple (ax, channel); + ax_simple (ax, nargs); + /* FIXME define a string-adder in ax-general.c */ + ax_simple (ax, strlen (format) + 1); + while (*format != '\0') + ax_simple (ax, *format++); + ax_simple (ax, '\0'); + + /* Oh, and terminate. */ + ax_simple (ax, aop_end); + + /* We have successfully built the agent expr, so cancel the cleanup + request. If we add more cleanups that we always want done, this + will have to get more complicated. */ + discard_cleanups (old_chain); + + return ax; +} + static void agent_command (char *exp, int from_tty) { @@ -2594,6 +2643,66 @@ agent_eval_command (char *exp, int from_ do_cleanups (old_chain); dont_repeat (); } +/* Parse the given expression, compile it into an agent expression + that does a printf, and display the resulting expression. */ + +extern char *parse_format_string (char **arg); + +static void +agent_printf_command (char *exp, int from_tty) +{ + struct cleanup *old_chain = 0; + struct expression *expr; + struct expression *argvec[100]; + struct agent_expr *agent; + struct frame_info *fi = get_current_frame (); /* need current scope */ + char *cmdrest, *string; + int nargs; + + /* We don't deal with overlay debugging at the moment. We need to + think more carefully about this. If you copy this code into + another command, change the error message; the user shouldn't + have to know anything about agent expressions. */ + if (overlay_debugging) + error (_("GDB can't do agent expression translation with overlays.")); + + if (exp == 0) + error_no_arg (_("expression to translate")); + + cmdrest = exp; + + string = parse_format_string (&cmdrest); + + nargs = 0; + while (*cmdrest != '\0') + { + char *cmd1; + + cmd1 = cmdrest; + expr = parse_exp_1 (&cmd1, (struct block *) 0, 1); + argvec[nargs] = expr; + ++nargs; + cmdrest = cmd1; + if (*cmdrest == ',') + ++cmdrest; + } + +#if 0 + old_chain = make_cleanup (free_current_contents, &expr); +#endif + agent = gen_printf (get_frame_pc (fi), 0, string, nargs, argvec); + make_cleanup_free_agent_expr (agent); + ax_reqs (agent); + ax_print (gdb_stdout, agent); + + /* It would be nice to call ax_reqs here to gather some general info + about the expression, and then print out the result. */ + +#if 0 + do_cleanups (old_chain); +#endif + dont_repeat (); +} /* Initialization code. */ @@ -2611,4 +2720,9 @@ _initialize_ax_gdb (void) _("Translate an expression into remote " "agent bytecode for evaluation."), &maintenancelist); + + add_cmd ("agent-printf", class_maintenance, agent_printf_command, + _("Translate an expression into remote " + "agent bytecode for evaluation."), + &maintenancelist); } Index: ax-gdb.h =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.h,v retrieving revision 1.23 diff -u -p -r1.23 ax-gdb.h --- ax-gdb.h 4 Jan 2012 08:16:59 -0000 1.23 +++ ax-gdb.h 29 Feb 2012 05:52:13 -0000 @@ -110,6 +110,9 @@ extern struct agent_expr *gen_trace_for_ extern struct agent_expr *gen_eval_for_expr (CORE_ADDR, struct expression *); +extern struct agent_expr *gen_printf (CORE_ADDR, int, char *, + int, struct expression **); + extern int trace_kludge; extern int trace_string_kludge; Index: ax-general.c =================================================================== RCS file: /cvs/src/src/gdb/ax-general.c,v retrieving revision 1.29 diff -u -p -r1.29 ax-general.c --- ax-general.c 4 Jan 2012 08:16:59 -0000 1.29 +++ ax-general.c 29 Feb 2012 05:52:13 -0000 @@ -391,6 +391,19 @@ ax_print (struct ui_file *f, struct agen print_longest (f, 'd', 0, read_const (x, i + 1, aop_map[op].op_size)); } + /* Handle the complicated printf arguments specially. */ + else if (op == aop_printf) + { + int chan, slen, nargs; + + ++i; + chan = x->buf[i++]; + nargs = x->buf[i++]; + slen = x->buf[i++]; + fprintf_filtered (f, " %d \"%s\", %d args", + chan, &(x->buf[i]), nargs); + i += slen - 1; + } fprintf_filtered (f, "\n"); i += 1 + aop_map[op].op_size; Index: breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.657 diff -u -p -r1.657 breakpoint.c --- breakpoint.c 24 Feb 2012 16:26:36 -0000 1.657 +++ breakpoint.c 29 Feb 2012 05:52:13 -0000 @@ -290,6 +290,12 @@ static struct breakpoint_ops momentary_b breakpoints. */ struct breakpoint_ops bkpt_breakpoint_ops; +static struct breakpoint_ops dprintf_breakpoint_ops; + +/* True if dprintf commands should continue to operate even if GDB + has disconnected. */ +static int disconnected_dprintf = 1; + /* A reference-counted struct command_line. This lets multiple breakpoints share a single command list. */ struct counted_command_line @@ -1480,7 +1486,8 @@ int is_breakpoint (const struct breakpoint *bpt) { return (bpt->type == bp_breakpoint - || bpt->type == bp_hardware_breakpoint); + || bpt->type == bp_hardware_breakpoint + || bpt->type == bp_dprintf); } /* Return true if BPT is of any hardware watchpoint kind. */ @@ -2062,6 +2069,247 @@ build_target_condition_list (struct bp_l return; } +char * +parse_format_string (char **arg) +{ + char *s, *f, *string; + + s = *arg; + + s = skip_spaces (s); + + /* A format string should follow, enveloped in double quotes. */ + if (*s++ != '"') + error (_("Bad format string, missing '\"'.")); + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + + while (*s != '"') + { + int c = *s++; + switch (c) + { + case '\0': + error (_("Bad format string, non-terminated '\"'.")); + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'a': + *f++ = '\a'; + break; + case 'b': + *f++ = '\b'; + break; + case 'f': + *f++ = '\f'; + break; + case 'n': + *f++ = '\n'; + break; + case 'r': + *f++ = '\r'; + break; + case 't': + *f++ = '\t'; + break; + case 'v': + *f++ = '\v'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences. */ + error (_("Unrecognized escape character \\%c in format string."), + c); + } + break; + + default: + *f++ = c; + } + } + + /* Skip over " and following space and comma. */ + s++; + *f++ = '\0'; + s = skip_spaces (s); + + if (*s != ',' && *s != 0) + error (_("Invalid argument syntax")); + + if (*s == ',') + s++; + s = skip_spaces (s); + + *arg = s; + + return xstrdup (string); +} + +/* Parses a command described by string CMD into an agent expression + bytecode suitable for evaluation by the bytecode interpreter. + Return NULL if there was any error during parsing. */ + +struct agent_expr * +parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd) +{ + struct expression *expr; + struct expression *argvec[100]; + struct agent_expr *aexpr = NULL; + struct cleanup *old_chain = NULL; + volatile struct gdb_exception ex; + char *cmdrest, *string; + int nargs; + + if (!cmd) + return NULL; + + /* For each argument, make an expression. */ + + cmdrest = cmd; + + string = parse_format_string (&cmdrest); + + /* (should get comma separator here) */ + + nargs = 0; + while (*cmdrest != '\0') + { + char *cmd1; + + cmd1 = cmdrest; + expr = parse_exp_1 (&cmd1, (struct block *) 0, 1); + argvec[nargs++] = expr; + cmdrest = cmd1; + if (*cmdrest == ',') + ++cmdrest; + } + + /* We don't want to stop processing, so catch any errors + that may show up. */ + TRY_CATCH (ex, RETURN_MASK_ERROR) + { + aexpr = gen_printf (scope, 0, string, nargs, argvec); + } + + if (ex.reason < 0) + { + /* If we got here, it means the condition could not be parsed to a valid + bytecode expression and thus can't be evaluated on the target's side. + It's no use iterating through the conditions. */ + return NULL; + } + + /* We have a valid agent expression. */ + return aexpr; +} + +/* Based on location BL, create a list of breakpoint commands to be + passed on to the target. If we have duplicated locations with + different commands, we will add any such to the list. */ + +static void +build_target_command_list (struct bp_location *bl) +{ + struct bp_location **locp = NULL, **loc2p; + int null_command_or_parse_error = 0; + int modified = bl->needs_update; + struct bp_location *loc; + + /* For now, limit to dprintf breakpoints. */ + if (bl->owner->type != bp_dprintf) + return; + + if (!target_can_run_breakpoint_commands ()) + return; + + /* Do a first pass to check for locations with no assigned + conditions or conditions that fail to parse to a valid agent expression + bytecode. If any of these happen, then it's no use to send conditions + to the target since this location will always trigger and generate a + response back to GDB. */ + ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address) + { + loc = (*loc2p); + if (is_breakpoint (loc->owner) && loc->pspace->num == bl->pspace->num) + { + if (modified) + { + struct agent_expr *aexpr; + + /* Re-parse the conditions since something changed. In that + case we already freed the condition bytecodes (see + force_breakpoint_reinsertion). We just + need to parse the condition to bytecodes again. */ + aexpr = parse_cmd_to_aexpr (bl->address, + loc->owner->extra_string); + loc->cmd_bytecode = aexpr; + + if (!aexpr) + continue; + } + + /* If we have a NULL bytecode expression, it means something + went wrong or we have a null condition expression. */ + if (!loc->cmd_bytecode) + { + null_command_or_parse_error = 1; + break; + } + } + } + + /* If anything failed, then we're not doing target-side commands, + and so clean up. */ + if (null_command_or_parse_error) + { + ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address) + { + loc = (*loc2p); + if (is_breakpoint (loc->owner) + && loc->pspace->num == bl->pspace->num) + { + /* Only go as far as the first NULL bytecode is + located. */ + if (!loc->cond_bytecode) + return; + + free_agent_expr (loc->cond_bytecode); + loc->cond_bytecode = NULL; + } + } + } + + /* No NULL commands or failed bytecode generation. Build a condition list + for this location's address. */ + ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address) + { + loc = (*loc2p); + if (loc->owner->extra_string + && is_breakpoint (loc->owner) + && loc->pspace->num == bl->pspace->num + && loc->owner->enable_state == bp_enabled + && loc->enabled) + /* Add the command to the vector. This will be used later + to send the conditions to the target. */ + VEC_safe_push (agent_expr_p, bl->target_info.commands, + loc->cmd_bytecode); + } + + bl->target_info.persist = 0; + /* Maybe flag this location as persistent. */ + if (bl->owner->type == bp_dprintf && disconnected_dprintf) + bl->target_info.persist = 1; +} + /* Insert a low-level "breakpoint" of some type. BL is the breakpoint location. Any error messages are printed to TMP_ERROR_STREAM; and DISABLED_BREAKS, and HW_BREAKPOINT_ERROR are used to report problems. @@ -2095,7 +2343,8 @@ insert_bp_location (struct bp_location * if (is_breakpoint (bl->owner)) { build_target_condition_list (bl); - /* Reset the condition modification marker. */ + build_target_command_list (bl); + /* Reset the modification marker. */ bl->needs_update = 0; } @@ -2601,6 +2850,9 @@ remove_breakpoints_pid (int pid) if (bl->pspace != inf->pspace) continue; + if (bl->owner->type == bp_dprintf) + continue; + if (bl->inserted) { val = remove_breakpoint (bl, mark_uninserted); @@ -5054,6 +5306,11 @@ bpstat_what (bpstat bs_head) PC of the former breakpoint. */ this_action = BPSTAT_WHAT_KEEP_CHECKING; break; + + case bp_dprintf: + this_action = BPSTAT_WHAT_STOP_SILENT; + break; + default: internal_error (__FILE__, __LINE__, _("bpstat_what: unhandled bptype %d"), (int) bptype); @@ -5318,6 +5575,7 @@ bptype_string (enum bptype type) {bp_tracepoint, "tracepoint"}, {bp_fast_tracepoint, "fast tracepoint"}, {bp_static_tracepoint, "static tracepoint"}, + {bp_dprintf, "dprintf"}, {bp_jit_event, "jit events"}, {bp_gnu_ifunc_resolver, "STT_GNU_IFUNC resolver"}, {bp_gnu_ifunc_resolver_return, "STT_GNU_IFUNC resolver return"}, @@ -5458,6 +5716,7 @@ print_one_breakpoint_location (struct br case bp_tracepoint: case bp_fast_tracepoint: case bp_static_tracepoint: + case bp_dprintf: case bp_jit_event: case bp_gnu_ifunc_resolver: case bp_gnu_ifunc_resolver_return: @@ -5635,6 +5894,15 @@ print_one_breakpoint_location (struct br } } + if (!part_of_multiple && b->extra_string + && b->type == bp_dprintf && !b->commands) + { + annotate_field (7); + ui_out_text (uiout, "\t(agent printf) "); + ui_out_field_string (uiout, "printf", b->extra_string); + ui_out_text (uiout, "\n"); + } + l = b->commands ? b->commands->commands : NULL; if (!part_of_multiple && l) { @@ -6320,6 +6588,7 @@ init_bp_location (struct bp_location *lo case bp_exception_master: case bp_gnu_ifunc_resolver: case bp_gnu_ifunc_resolver_return: + case bp_dprintf: loc->loc_type = bp_loc_software_breakpoint; mark_breakpoint_location_modified (loc); break; @@ -8192,7 +8461,19 @@ bp_loc_is_permanent (struct bp_location return retval; } +static const char dprintf_style_gdb[] = "gdb"; +static const char dprintf_style_call[] = "call"; +static const char dprintf_style_agent[] = "agent"; +static const char *const dprintf_style_enums[] = { + dprintf_style_gdb, + dprintf_style_call, + dprintf_style_agent, + NULL +}; +static const char *dprintf_style = dprintf_style_gdb; +/* Temporary hack to smuggle remainder of command line through. */ +char *glob_extra_string = NULL; /* Create a breakpoint with SAL as location. Use ADDR_STRING as textual description of the location, and COND_STRING @@ -8247,6 +8528,7 @@ init_breakpoint_sal (struct breakpoint * b->task = task; b->cond_string = cond_string; + b->extra_string = glob_extra_string; b->ignore_count = ignore_count; b->enable_state = enabled ? bp_enabled : bp_disabled; b->disposition = disposition; @@ -8306,6 +8588,40 @@ init_breakpoint_sal (struct breakpoint * if (*arg) error (_("Garbage %s follows condition"), arg); } + + if (b->extra_string) + { + char *printf_line = NULL; + + if (strcmp (dprintf_style, "gdb") == 0) + printf_line = xstrprintf ("printf %s", b->extra_string); + else if (strcmp (dprintf_style, "call") == 0) + printf_line = xstrprintf ("call printf(%s)", b->extra_string); + else if (strcmp (dprintf_style, "agent") == 0) + /* Nothing to do here. */ ; + else + internal_error (__FILE__, __LINE__, + _("Invalid dprintf style.")); + + if (printf_line) + { + struct command_line *printf_cmd_line, *cont_cmd_line; + + cont_cmd_line = xmalloc (sizeof (struct command_line)); + cont_cmd_line->body_count = 0; + cont_cmd_line->control_type = simple_control; + cont_cmd_line->next = NULL; + cont_cmd_line->line = "continue"; + + printf_cmd_line = xmalloc (sizeof (struct command_line)); + printf_cmd_line->body_count = 0; + printf_cmd_line->control_type = simple_control; + printf_cmd_line->next = cont_cmd_line; + printf_cmd_line->line = printf_line; + + breakpoint_set_commands (b, printf_cmd_line); + } + } } b->display_canonical = display_canonical; @@ -8536,7 +8852,8 @@ check_fast_tracepoint_sals (struct gdbar If no thread is found, *THREAD is set to -1. */ static void find_condition_and_thread (char *tok, CORE_ADDR pc, - char **cond_string, int *thread, int *task) + char **cond_string, int *thread, int *task, + char **rest) { *cond_string = NULL; *thread = -1; @@ -8549,6 +8866,12 @@ find_condition_and_thread (char *tok, CO tok = skip_spaces (tok); + if (*tok == '"' && rest) + { + *rest = savestring (tok, strlen (tok)); + return; + } + end_tok = skip_to_space (tok); toklen = end_tok - tok; @@ -8588,6 +8911,11 @@ find_condition_and_thread (char *tok, CO if (!valid_task_id (*task)) error (_("Unknown task %d."), *task); } + else if (rest) + { + *rest = savestring (tok, toklen); + tok += toklen; + } else error (_("Junk at end of arguments.")); } @@ -8673,6 +9001,7 @@ create_breakpoint (struct gdbarch *gdbar int pending = 0; int task = 0; int prev_bkpt_count = breakpoint_count; + char *extra_string = NULL; gdb_assert (ops != NULL); @@ -8776,16 +9105,22 @@ create_breakpoint (struct gdbarch *gdbar if (parse_condition_and_thread) { + char *rest; /* Here we only parse 'arg' to separate condition from thread number, so parsing in context of first sal is OK. When setting the breakpoint we'll re-parse it in context of each sal. */ cond_string = NULL; thread = -1; + rest = NULL; find_condition_and_thread (arg, lsal->sals.sals[0].pc, &cond_string, - &thread, &task); + &thread, &task, &rest); if (cond_string) make_cleanup (xfree, cond_string); + if (rest) + make_cleanup (xfree, rest); + if (rest) + extra_string = rest; } else { @@ -8797,6 +9132,7 @@ create_breakpoint (struct gdbarch *gdbar } } + glob_extra_string = extra_string; ops->create_breakpoints_sal (gdbarch, &canonical, lsal, cond_string, type_wanted, tempflag ? disp_del : disp_donttouch, @@ -8944,6 +9280,21 @@ break_command (char *arg, int from_tty) } void +dprintf_command (char *arg, int from_tty) +{ + create_breakpoint (get_current_arch (), + arg, + NULL, 0, 1 /* parse arg */, + 0, bp_dprintf, + 0 /* Ignore count */, + pending_break_support, + &dprintf_breakpoint_ops, + from_tty, + 1 /* enabled */, + 0 /* internal */); +} + +void tbreak_command (char *arg, int from_tty) { break_command_1 (arg, BP_TEMPFLAG, from_tty); @@ -12132,6 +12483,9 @@ bkpt_print_mention (struct breakpoint *b case bp_hardware_breakpoint: printf_filtered (_("Hardware assisted breakpoint %d"), b->number); break; + case bp_dprintf: + printf_filtered (_("Dprintf %d"), b->number); + break; } say_where (b); @@ -13151,13 +13505,17 @@ addr_string_to_sals (struct breakpoint * char *cond_string = 0; int thread = -1; int task = 0; + char *extra_string = NULL; find_condition_and_thread (s, sals.sals[0].pc, - &cond_string, &thread, &task); + &cond_string, &thread, &task, + &extra_string); if (cond_string) b->cond_string = cond_string; b->thread = thread; b->task = task; + if (extra_string) + b->extra_string = extra_string; b->condition_not_parsed = 0; } @@ -14946,6 +15304,14 @@ initialize_breakpoint_ops (void) ops->print_one = print_one_catch_solib; ops->print_mention = print_mention_catch_solib; ops->print_recreate = print_recreate_catch_solib; + + ops = &dprintf_breakpoint_ops; + *ops = bkpt_base_breakpoint_ops; + ops->re_set = bkpt_re_set; + ops->resources_needed = bkpt_resources_needed; + ops->print_it = bkpt_print_it; + ops->print_mention = bkpt_print_mention; + ops->print_recreate = bkpt_print_recreate; } void @@ -15524,6 +15890,31 @@ The breakpoint will stop execution of th an instruction at any address within the [START-LOCATION, END-LOCATION]\n\ range (including START-LOCATION and END-LOCATION).")); + c = add_com ("dprintf", class_breakpoint, dprintf_command, _("\ +Set a dynamic printf at specified line or function.\n" +BREAK_ARGS_HELP ("dprintf"))); + set_cmd_completer (c, location_completer); + + add_setshow_enum_cmd ("dprintf-style", class_support, + dprintf_style_enums, &dprintf_style, _("\ +Set the style of usage for dynamic printf."), _("\ +Show the style of usage for dynamic printf."), _("\ +Choose the style of usage for dynamic printf.\n\ +foo"), + NULL, + NULL, + &setlist, &showlist); + + add_setshow_boolean_cmd ("disconnected-dprintf", no_class, + &disconnected_dprintf, _("\ +Set whether dprintf continues after GDB disconnects."), _("\ +Show whether dprintf continues after GDB disconnects."), _("\ +Use this to let dprintf commands continue to hit and produce output\n\ +even if GDB disconnects or detaches from the target."), + NULL, + NULL, + &setlist, &showlist); + automatic_hardware_breakpoints = 1; observer_attach_about_to_proceed (breakpoint_about_to_proceed); Index: breakpoint.h =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.h,v retrieving revision 1.175 diff -u -p -r1.175 breakpoint.h --- breakpoint.h 24 Feb 2012 15:10:59 -0000 1.175 +++ breakpoint.h 29 Feb 2012 05:52:13 -0000 @@ -154,6 +154,8 @@ enum bptype bp_fast_tracepoint, bp_static_tracepoint, + bp_dprintf, + /* Event for JIT compiled code generation or deletion. */ bp_jit_event, @@ -264,6 +266,14 @@ struct bp_target_info /* Vector of conditions the target should evaluate if it supports target-side breakpoint conditions. */ VEC(agent_expr_p) *conditions; + + /* Vector of commands the target should evaluate if it supports + target-side breakpoint commands. */ + VEC(agent_expr_p) *commands; + + /* Flag that is true if the breakpoint should be left in place even + when GDB is not connected. */ + int persist; }; /* GDB maintains two types of information about each breakpoint (or @@ -350,8 +360,11 @@ struct bp_location enum condition_status condition_changed; - /* Signals that breakpoint conditions need to be re-synched with the - target. This has no use other than target-side breakpoints. */ + struct agent_expr *cmd_bytecode; + + /* Signals that breakpoint conditions and/or commands need to be + re-synched with the target. This has no use other than + target-side breakpoints. */ char needs_update; /* This location's address is in an unloaded solib, and so this @@ -673,8 +686,9 @@ struct breakpoint /* String form of the breakpoint condition (malloc'd), or NULL if there is no condition. */ char *cond_string; - /* String form of exp to use for displaying to the user - (malloc'd), or NULL if none. */ + + /* String form of extra parameters, or NULL if there are none. */ + char *extra_string; /* Holds the address of the related watchpoint_scope breakpoint when using watchpoints on local variables (might the concept of Index: remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.486 diff -u -p -r1.486 remote.c --- remote.c 24 Feb 2012 23:48:37 -0000 1.486 +++ remote.c 29 Feb 2012 05:52:14 -0000 @@ -244,6 +244,8 @@ static void remote_console_output (char static int remote_supports_cond_breakpoints (void); +static int remote_can_run_breakpoint_commands (void); + /* The non-stop remote protocol provisions for one pending stop reply. This is where we keep it until it is acknowledged. */ @@ -321,6 +323,10 @@ struct remote_state conditions. */ int cond_breakpoints; + /* True if the stub reports support for target-side breakpoint + commands. */ + int breakpoint_commands; + /* True if the stub reports support for fast tracepoints. */ int fast_tracepoints; @@ -1270,6 +1276,7 @@ enum { PACKET_qAttached, PACKET_ConditionalTracepoints, PACKET_ConditionalBreakpoints, + PACKET_BreakpointCommands, PACKET_FastTracepoints, PACKET_StaticTracepoints, PACKET_InstallInTrace, @@ -3734,6 +3741,16 @@ remote_cond_breakpoint_feature (const st } static void +remote_breakpoint_commands_feature (const struct protocol_feature *feature, + enum packet_support support, + const char *value) +{ + struct remote_state *rs = get_remote_state (); + + rs->breakpoint_commands = (support == PACKET_ENABLE); +} + +static void remote_fast_tracepoint_feature (const struct protocol_feature *feature, enum packet_support support, const char *value) @@ -3829,6 +3846,8 @@ static struct protocol_feature remote_pr PACKET_ConditionalTracepoints }, { "ConditionalBreakpoints", PACKET_DISABLE, remote_cond_breakpoint_feature, PACKET_ConditionalBreakpoints }, + { "BreakpointCommands", PACKET_DISABLE, remote_breakpoint_commands_feature, + PACKET_BreakpointCommands }, { "FastTracepoints", PACKET_DISABLE, remote_fast_tracepoint_feature, PACKET_FastTracepoints }, { "StaticTracepoints", PACKET_DISABLE, remote_static_tracepoint_feature, @@ -7768,6 +7787,37 @@ remote_add_target_side_condition (struct return 0; } +static void +remote_add_target_side_commands (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt, char *buf) +{ + struct agent_expr *aexpr = NULL; + int i, ix; + + if (VEC_empty (agent_expr_p, bp_tgt->commands)) + return; + + buf += strlen (buf); + + sprintf (buf, ";cmds:%x,", bp_tgt->persist); + buf += strlen (buf); + + /* Concatenate all the agent expressions that are commands into the + cmds parameter. */ + for (ix = 0; + VEC_iterate (agent_expr_p, bp_tgt->commands, ix, aexpr); + ix++) + { + sprintf (buf, "X%x,", aexpr->len); + buf += strlen (buf); + for (i = 0; i < aexpr->len; ++i) + buf = pack_hex_byte (buf, aexpr->buf[i]); + *buf = '\0'; + } + + VEC_free (agent_expr_p, bp_tgt->commands); +} + /* Insert a breakpoint. On targets that have software breakpoint support, we ask the remote target to do the work; on targets which don't, we insert a traditional memory breakpoint. */ @@ -7804,6 +7854,9 @@ remote_insert_breakpoint (struct gdbarch if (remote_supports_cond_breakpoints ()) remote_add_target_side_condition (gdbarch, bp_tgt, p); + if (remote_can_run_breakpoint_commands ()) + remote_add_target_side_commands (gdbarch, bp_tgt, p); + putpkt (rs->buf); getpkt (&rs->buf, &rs->buf_size, 0); @@ -8041,6 +8094,9 @@ remote_insert_hw_breakpoint (struct gdba if (remote_supports_cond_breakpoints ()) remote_add_target_side_condition (gdbarch, bp_tgt, p); + if (remote_can_run_breakpoint_commands ()) + remote_add_target_side_commands (gdbarch, bp_tgt, p); + putpkt (rs->buf); getpkt (&rs->buf, &rs->buf_size, 0); @@ -9973,6 +10029,14 @@ remote_supports_string_tracing (void) return rs->string_tracing; } +static int +remote_can_run_breakpoint_commands (void) +{ + struct remote_state *rs = get_remote_state (); + + return rs->breakpoint_commands; +} + static void remote_trace_init (void) { @@ -10839,6 +10903,7 @@ Specify the serial device it is connecte remote_ops.to_supports_enable_disable_tracepoint = remote_supports_enable_disable_tracepoint; remote_ops.to_supports_string_tracing = remote_supports_string_tracing; remote_ops.to_supports_evaluation_of_breakpoint_conditions = remote_supports_cond_breakpoints; + remote_ops.to_can_run_breakpoint_commands = remote_can_run_breakpoint_commands; remote_ops.to_trace_init = remote_trace_init; remote_ops.to_download_tracepoint = remote_download_tracepoint; remote_ops.to_can_download_tracepoint = remote_can_download_tracepoint; @@ -11361,6 +11426,10 @@ Show the maximum size of the address (in "ConditionalBreakpoints", "conditional-breakpoints", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_BreakpointCommands], + "BreakpointCommands", + "breakpoint-commands", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_FastTracepoints], "FastTracepoints", "fast-tracepoints", 0); Index: target.c =================================================================== RCS file: /cvs/src/src/gdb/target.c,v retrieving revision 1.300 diff -u -p -r1.300 target.c --- target.c 24 Feb 2012 15:10:59 -0000 1.300 +++ target.c 29 Feb 2012 05:52:14 -0000 @@ -698,6 +698,7 @@ update_current_target (void) INHERIT (to_static_tracepoint_marker_at, t); INHERIT (to_static_tracepoint_markers_by_strid, t); INHERIT (to_traceframe_info, t); + INHERIT (to_can_run_breakpoint_commands, t); INHERIT (to_magic, t); INHERIT (to_supports_evaluation_of_breakpoint_conditions, t); /* Do not inherit to_memory_map. */ @@ -929,6 +930,9 @@ update_current_target (void) de_fault (to_supports_evaluation_of_breakpoint_conditions, (int (*) (void)) return_zero); + de_fault (to_can_run_breakpoint_commands, + (int (*) (void)) + return_zero); de_fault (to_execution_direction, default_execution_direction); #undef de_fault Index: target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.228 diff -u -p -r1.228 target.h --- target.h 24 Feb 2012 23:46:48 -0000 1.228 +++ target.h 29 Feb 2012 05:52:14 -0000 @@ -666,6 +666,10 @@ struct target_ops end? */ int (*to_supports_evaluation_of_breakpoint_conditions) (void); + /* Does this target support evaluation of breakpoint commands on its + end? */ + int (*to_can_run_breakpoint_commands) (void); + /* Determine current architecture of thread PTID. The target is supposed to determine the architecture of the code where @@ -978,6 +982,12 @@ int target_supports_disable_randomizatio #define target_supports_evaluation_of_breakpoint_conditions() \ (*current_target.to_supports_evaluation_of_breakpoint_conditions) () +/* Returns true if this target can handle breakpoint commands + on its end. */ + +#define target_can_run_breakpoint_commands() \ + (*current_target.to_can_run_breakpoint_commands) () + /* Invalidate all target dcaches. */ extern void target_dcache_invalidate (void); Index: common/ax.def =================================================================== RCS file: /cvs/src/src/gdb/common/ax.def,v retrieving revision 1.6 diff -u -p -r1.6 ax.def --- common/ax.def 4 Jan 2012 08:17:17 -0000 1.6 +++ common/ax.def 29 Feb 2012 05:52:14 -0000 @@ -93,3 +93,5 @@ DEFOP (invalid2, 0, 0, 0, 0, 0x31) express the right thing. */ DEFOP (pick, 1, 0, 0, 1, 0x32) DEFOP (rot, 0, 0, 3, 3, 0x33) +/* Both the argument and consumed numbers are dynamic for this one. */ +DEFOP (printf, 0, 0, 0, 0, 0x34) Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.926 diff -u -p -r1.926 gdb.texinfo --- doc/gdb.texinfo 25 Feb 2012 13:54:25 -0000 1.926 +++ doc/gdb.texinfo 29 Feb 2012 05:52:15 -0000 @@ -3301,6 +3301,7 @@ all breakpoints in that range are operat * Disabling:: Disabling breakpoints * Conditions:: Break conditions * Break Commands:: Breakpoint command lists +* Dynamic Printf:: Dynamic printf * Save Breakpoints:: How to save breakpoints in a file * Error in Breakpoints:: ``Cannot insert breakpoints'' * Breakpoint-related Warnings:: ``Breakpoint address adjusted...'' @@ -4587,6 +4588,68 @@ cont end @end smallexample +@node Dynamic Printf +@subsection Dynamic Printf + +@cindex dynamic printf +The dynamic printf command @code{dprintf} is a convenient way to +combine breakpoints with formatted printing of your program's data. + +In its most basic form, the output comes back to the GDB console and +the @code{dprintf} only lasts as long as the debugging session. +However, you can set the variable @code{dprintf-style} for additional +approaches. For instance, you can ask to format the output by calling +your program's @code{printf} function. This has the advantage that +the characters go to the program's output device, so they can recorded +in redirects to files and so forth. + +If you are doing remote debugging with a stub or agent, you can also +ask to have the printf handled by the remote agent. In addition to +ensuring that the output goes to the remote program's device along +with any other output the program might produce, you can also ask that +the dprintf remain active even after disconnecting from the remote +target. Using the stub/agent is also more efficient, as it can do +everything without needing to communicate with @value{GDBN}. + +@table @code +@kindex dprintf +@item dprintf @var{location} @var{template},@var{expression}[,@var{expression}@dots{}] +Whenever execution reaches @var{location}, print the values of one or +more @var{expressions} under the control of the string @var{template}. +To print several values, separate them with commas. + +@item set dprintf-style @var{style} +Set the dprintf output to be handled in one of several different +styles enumerated below: + +@item gdb +@kindex dprintf-style gdb +Handle the output using the @value{GDBN} @code{printf} command. + +@item call +@kindex dprintf-style call +Handle the output by calling your program's @code{printf} function. +(This expects @code{printf} to have been linked into the program.) + +@item agent +@kindex dprintf-style agent +Have the remote debugging agent (such as @code{gdbserver}) handle +the output itself. This style is only available for agents that +support it. + +@item set disconnected-dprintf on +@itemx set disconnected-dprintf off +@kindex set disconnected-dprintf +Choose whether @code{dprintf} commands should continue to run if +@value{GDBN} has disconnected from the target. + +@item show disconnected-dprintf off +@kindex show disconnected-dprintf +Show the current choice for disconnected @code{dprintf}. + +@end table + +@cindex breakpoint commands @node Save Breakpoints @subsection How to save breakpoints to a file Index: gdbserver/ax.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/ax.c,v retrieving revision 1.1 diff -u -p -r1.1 ax.c --- gdbserver/ax.c 24 Feb 2012 15:14:47 -0000 1.1 +++ gdbserver/ax.c 29 Feb 2012 05:52:15 -0000 @@ -1152,6 +1152,30 @@ gdb_eval_agent_expr (struct regcache *re top = stack[sp]; break; + case gdb_agent_op_printf: + { + int chan, nargs, slen, i; + int args[100]; + char *format; + + chan = aexpr->bytes[pc++]; + nargs = aexpr->bytes[pc++]; + slen = aexpr->bytes[pc++]; + format = (char *) &(aexpr->bytes[pc]); + pc += slen; + for (i = 0; i < nargs; ++i) + { + args[i] = top; + if (--sp >= 0) + top = stack[sp]; + } + /* (should re-check format before calling?) */ + printf (format, + args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9]); + } + break; + /* GDB never (currently) generates any of these ops. */ case gdb_agent_op_float: case gdb_agent_op_ref_float: Index: gdbserver/linux-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v retrieving revision 1.195 diff -u -p -r1.195 linux-low.c --- gdbserver/linux-low.c 27 Feb 2012 16:22:16 -0000 1.195 +++ gdbserver/linux-low.c 29 Feb 2012 05:52:15 -0000 @@ -2426,7 +2426,10 @@ Check if we're already there.\n", || (!step_over_finished && !bp_explains_trap && !trace_event) || (gdb_breakpoint_here (event_child->stop_pc) - && gdb_condition_true_at_breakpoint (event_child->stop_pc))); + && gdb_condition_true_at_breakpoint (event_child->stop_pc) + && gdb_no_commands_at_breakpoint (event_child->stop_pc))); + + run_breakpoint_commands (event_child->stop_pc); /* We found no reason GDB would want us to stop. We either hit one of our own breakpoints, or finished an internal step GDB @@ -3292,7 +3295,8 @@ need_step_over_p (struct inferior_list_e though. If the condition is being evaluated on the target's side and it evaluate to false, step over this breakpoint as well. */ if (gdb_breakpoint_here (pc) - && gdb_condition_true_at_breakpoint (pc)) + && gdb_condition_true_at_breakpoint (pc) + && gdb_no_commands_at_breakpoint (pc)) { if (debug_threads) fprintf (stderr, Index: gdbserver/mem-break.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/mem-break.c,v retrieving revision 1.33 diff -u -p -r1.33 mem-break.c --- gdbserver/mem-break.c 25 Feb 2012 19:54:50 -0000 1.33 +++ gdbserver/mem-break.c 29 Feb 2012 05:52:15 -0000 @@ -97,6 +97,20 @@ struct point_cond_list struct point_cond_list *next; }; +struct point_command_list +{ + /* Pointer to the agent expression that is the breakpoint's + commands. */ + struct agent_expr *cmd; + + /* Flag that is true if this command should run even while GDB is + disconnected. */ + int persistence; + + /* Pointer to the next command. */ + struct point_command_list *next; +}; + /* A high level (in gdbserver's perspective) breakpoint. */ struct breakpoint { @@ -111,6 +125,9 @@ struct breakpoint target's side. */ struct point_cond_list *cond_list; + /* Point to the list of commands to run when this is hit. */ + struct point_command_list *command_list; + /* Link to this breakpoint's raw breakpoint. This is always non-NULL. */ struct raw_breakpoint *raw; @@ -121,6 +138,23 @@ struct breakpoint int (*handler) (CORE_ADDR); }; +int +any_persistent_commands () +{ + struct process_info *proc = current_process (); + struct breakpoint *bp; + struct point_command_list *cl; + + for (bp = proc->breakpoints; bp != NULL; bp = bp->next) + { + for (cl = bp->command_list; cl != NULL; cl = cl->next) + if (cl->persistence) + return 1; + } + + return 0; +} + static struct raw_breakpoint * find_raw_breakpoint_at (CORE_ADDR where) { @@ -835,6 +869,97 @@ gdb_condition_true_at_breakpoint (CORE_A return (value != 0); } +/* Add commands COMMANDS to GDBserver's breakpoint BP. */ + +void +add_commands_to_breakpoint (struct breakpoint *bp, + struct agent_expr *commands, int persist) +{ + struct point_command_list *new_cmd; + + /* Create new command. */ + new_cmd = xcalloc (1, sizeof (*new_cmd)); + new_cmd->cmd = commands; + new_cmd->persistence = persist; + + /* Add commands to the list. */ + new_cmd->next = bp->command_list; + bp->command_list = new_cmd; +} + +/* Add a target-side command COMMAND to the breakpoint at ADDR. */ + +int +add_breakpoint_commands (CORE_ADDR addr, char **command, int persist) +{ + struct breakpoint *bp = find_gdb_breakpoint_at (addr); + char *actparm = *command; + struct agent_expr *cmd; + + if (bp == NULL) + return 1; + + if (command == NULL) + return 1; + + cmd = gdb_parse_agent_expr (&actparm); + + if (cmd == NULL) + { + fprintf (stderr, "Command evaluation failed. " + "Disabling.\n"); + return 0; + } + + add_commands_to_breakpoint (bp, cmd, persist); + + *command = actparm; + + return 0; +} + +/* Return true if there are no commands to run at this location, + which likely means we want to report back to GDB. */ +int +gdb_no_commands_at_breakpoint (CORE_ADDR where) +{ + struct breakpoint *bp = find_gdb_breakpoint_at (where); + + if (bp == NULL) + return 0; + + if (debug_threads) + fprintf (stderr, "at 0x%s, bp command_list is 0x%x\n", + paddress (where), (int) bp->command_list); + return (bp->command_list == NULL); +} + +void +run_breakpoint_commands (CORE_ADDR where) +{ + /* Fetch registers for the current inferior. */ + struct breakpoint *bp = find_gdb_breakpoint_at (where); + ULONGEST value = 0; + struct point_command_list *cl; + int err = 0; + + struct regcache *regcache = get_thread_regcache (current_inferior, 1); + + if (bp == NULL) + return; + + for (cl = bp->command_list; + cl && !value && !err; cl = cl->next) + { + /* Run the command. */ + err = gdb_eval_agent_expr (regcache, NULL, cl->cmd, &value); + + /* If one command has a problem, stop digging the hole deeper. */ + if (err) + break; + } +} + /* Return 1 if there is a breakpoint inserted in address WHERE and if its condition, if it exists, is true. */ Index: gdbserver/mem-break.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/mem-break.h,v retrieving revision 1.25 diff -u -p -r1.25 mem-break.h --- gdbserver/mem-break.h 24 Feb 2012 15:15:56 -0000 1.25 +++ gdbserver/mem-break.h 29 Feb 2012 05:52:15 -0000 @@ -52,11 +52,19 @@ void clear_gdb_breakpoint_conditions (CO int add_breakpoint_condition (CORE_ADDR addr, char **condition); +int add_breakpoint_commands (CORE_ADDR addr, char **commands, int persist); + +int any_persistent_commands (void); + /* Evaluation condition (if any) at breakpoint BP. Return 1 if true and 0 otherwise. */ int gdb_condition_true_at_breakpoint (CORE_ADDR where); +int gdb_no_commands_at_breakpoint (CORE_ADDR where); + +void run_breakpoint_commands (CORE_ADDR where); + /* Returns TRUE if there's a GDB breakpoint set at ADDR. */ int gdb_breakpoint_here (CORE_ADDR where); Index: gdbserver/server.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.c,v retrieving revision 1.162 diff -u -p -r1.162 server.c --- gdbserver/server.c 24 Feb 2012 15:15:56 -0000 1.162 +++ gdbserver/server.c 29 Feb 2012 05:52:15 -0000 @@ -1621,8 +1621,9 @@ handle_query (char *own_buf, int packet_ strcat (own_buf, ";tracenz+"); } - /* Support target-side breakpoint conditions. */ + /* Support target-side breakpoint conditions and commands. */ strcat (own_buf, ";ConditionalBreakpoints+"); + strcat (own_buf, ";BreakpointCommands+"); return; } @@ -2836,6 +2837,7 @@ static void process_point_options (CORE_ADDR point_addr, char **packet) { char *dataptr = *packet; + int persist; /* Check if data has the correct format. */ if (*dataptr != ';') @@ -2845,21 +2847,33 @@ process_point_options (CORE_ADDR point_a while (*dataptr) { - switch (*dataptr) + if (*dataptr == ';') + ++dataptr; + + if (*dataptr == 'X') { - case 'X': - /* Conditional expression. */ - fprintf (stderr, "Found breakpoint condition.\n"); - add_breakpoint_condition (point_addr, &dataptr); - break; - default: - /* Unrecognized token, just skip it. */ - fprintf (stderr, "Unknown token %c, ignoring.\n", - *dataptr); + /* Conditional expression. */ + fprintf (stderr, "Found breakpoint condition.\n"); + add_breakpoint_condition (point_addr, &dataptr); + } + else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0) + { + dataptr += strlen ("cmds:"); + if (debug_threads) + fprintf (stderr, "Found breakpoint commands %s.\n", dataptr); + persist = (*dataptr == '1'); + dataptr += 2; + add_breakpoint_commands (point_addr, &dataptr, persist); + } + else + { + /* Unrecognized token, just skip it. */ + fprintf (stderr, "Unknown token %c, ignoring.\n", + *dataptr); } /* Skip tokens until we find one that we recognize. */ - while (*dataptr && *dataptr != 'X' && *dataptr != ';') + while (*dataptr && *dataptr != ';') dataptr++; } *packet = dataptr; @@ -2925,7 +2939,7 @@ process_serial_event (void) pid = ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id); - if (tracing && disconnected_tracing) + if ((tracing && disconnected_tracing) || any_persistent_commands ()) { struct thread_resume resume_info; struct process_info *process = find_process_pid (pid); @@ -2936,9 +2950,15 @@ process_serial_event (void) break; } - fprintf (stderr, - "Disconnected tracing in effect, " - "leaving gdbserver attached to the process\n"); + if (tracing && disconnected_tracing) + fprintf (stderr, + "Disconnected tracing in effect, " + "leaving gdbserver attached to the process\n"); + + if (any_persistent_commands ()) + fprintf (stderr, + "Persistent commands are present, " + "leaving gdbserver attached to the process\n"); /* Make sure we're in non-stop/async mode, so we we can both wait for an async socket accept, and handle async target --------------020701060001040103040700--