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