* [PATCH] dynamic printf
@ 2012-02-29 9:14 Stan Shebs
2012-02-29 10:28 ` Andreas Schwab
` (4 more replies)
0 siblings, 5 replies; 15+ messages in thread
From: Stan Shebs @ 2012-02-29 9:14 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 5023 bytes --]
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 <location> <format>,<arg>,<arg>...
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 <stan@codesourcery.com>
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) <cond_breakpoints>: 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.
[-- Attachment #2: dprintf-patch-1 --]
[-- Type: text/plain, Size: 46628 bytes --]
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 ();
+}
\f
/* 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
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH] dynamic printf
2012-02-29 9:14 [PATCH] dynamic printf Stan Shebs
@ 2012-02-29 10:28 ` Andreas Schwab
2012-03-13 23:09 ` Stan Shebs
2012-02-29 16:16 ` Joel Brobecker
` (3 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Andreas Schwab @ 2012-02-29 10:28 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
Stan Shebs <stanshebs@earthlink.net> writes:
> 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.
How about:
break [LOCATION] [thread THREADNUM] [if CONDITION] [printf FORMAT,ARGS...]
Andreas.
--
Andreas Schwab, schwab@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH] dynamic printf
2012-02-29 10:28 ` Andreas Schwab
@ 2012-03-13 23:09 ` Stan Shebs
2012-03-14 14:47 ` Marc Khouzam
2012-03-14 15:28 ` Tom Tromey
0 siblings, 2 replies; 15+ messages in thread
From: Stan Shebs @ 2012-03-13 23:09 UTC (permalink / raw)
To: Andreas Schwab; +Cc: gdb-patches
On 2/29/12 1:14 AM, Andreas Schwab wrote:
> Stan Shebs<stanshebs@earthlink.net> writes:
>
>> 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.
> How about:
>
> break [LOCATION] [thread THREADNUM] [if CONDITION] [printf FORMAT,ARGS...]
>
> Andreas.
>
I've been wondering whether that might be a better idea. An advantage
of "dprintf" for the command line is that it can abbreviate to just "dp"
and tab completion works, while the optional arguments to the break
command don't have any way to abbreviate (although they probably
could). That's not super-compelling, as by its nature a printf command
entails typing in format string, double quotes and newlines and all, and
in practice I expect a lot of these will tend to accumulate in script files.
Another consideration is how the collection looks in an info break
command. If dynamic prints are a different kind of breakpoint, then you
can (in theory) filter the breakpoint listing to a single category, but
if they are just breakpoints with special commands, they'll continue to
be in the big list.
My intuition isn't giving me a strong read on which approach users will
like better. As the goal is at least partly to entice
printf()+recompile users into using a GDB command instead, I think it
needs to be among GDB's simpler commands. Maybe include both syntaxes,
mapping down to same functionality?
Stan
^ permalink raw reply [flat|nested] 15+ messages in thread
* RE: [PATCH] dynamic printf
2012-03-13 23:09 ` Stan Shebs
@ 2012-03-14 14:47 ` Marc Khouzam
2012-03-14 15:28 ` Tom Tromey
1 sibling, 0 replies; 15+ messages in thread
From: Marc Khouzam @ 2012-03-14 14:47 UTC (permalink / raw)
To: 'Stan Shebs', 'Andreas Schwab'
Cc: 'gdb-patches@sourceware.org'
> -----Original Message-----
> From: gdb-patches-owner@sourceware.org
> [mailto:gdb-patches-owner@sourceware.org] On Behalf Of Stan Shebs
> Sent: Tuesday, March 13, 2012 7:09 PM
> To: Andreas Schwab
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH] dynamic printf
>
> On 2/29/12 1:14 AM, Andreas Schwab wrote:
> > Stan Shebs<stanshebs@earthlink.net> writes:
> >
> >> 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.
> > How about:
> >
> > break [LOCATION] [thread THREADNUM] [if CONDITION] [printf
> FORMAT,ARGS...]
When I tried it a while back, using a breakpoint + printf printed
to the GDB console. The new dprintf has the goal of printing to the
inferior's console.
With dprintf, the resulting printouts should look exactly the
same as if the user had added the printf in the code + recompiled.
Another way to achieve this may be to have a new
'inferior-printf' command similar to GDB's 'printf'
then
break [LOCATION] [thread THREADNUM] [if CONDITION] [inferior-printf FORMAT,ARGS...]
would work too, I think.
However, one would loose the disconnected printing aspect of
the feature, which also makes it much closer to actually adding
a real printf in the code.
> I've been wondering whether that might be a better idea. An
> advantage
> of "dprintf" for the command line is that it can abbreviate
> to just "dp"
> and tab completion works, while the optional arguments to the break
> command don't have any way to abbreviate (although they probably
> could). That's not super-compelling, as by its nature a
> printf command
> entails typing in format string, double quotes and newlines
> and all, and
> in practice I expect a lot of these will tend to accumulate
> in script files.
>
> Another consideration is how the collection looks in an info break
> command. If dynamic prints are a different kind of
> breakpoint, then you
> can (in theory) filter the breakpoint listing to a single
> category, but
> if they are just breakpoints with special commands, they'll
> continue to
> be in the big list.
>
> My intuition isn't giving me a strong read on which approach
> users will
> like better. As the goal is at least partly to entice
> printf()+recompile users into using a GDB command instead, I think it
> needs to be among GDB's simpler commands. Maybe include both
> syntaxes,
> mapping down to same functionality?
I think those points are good.
I personally don't have a strong opinion on those, since
I'm looking at it from the point of view of using this feature
from Eclipse.
BTW, I think this is one cool feature!
Marc
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-03-13 23:09 ` Stan Shebs
2012-03-14 14:47 ` Marc Khouzam
@ 2012-03-14 15:28 ` Tom Tromey
1 sibling, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2012-03-14 15:28 UTC (permalink / raw)
To: Stan Shebs; +Cc: Andreas Schwab, gdb-patches
>>>>> "Stan" == Stan Shebs <stanshebs@earthlink.net> writes:
Stan> Another consideration is how the collection looks in an info break
Stan> command. If dynamic prints are a different kind of breakpoint, then
Stan> you can (in theory) filter the breakpoint listing to a single
Stan> category, but if they are just breakpoints with special commands,
Stan> they'll continue to be in the big list.
I tend to prefer a new command for this reason and also KISS.
Stan> My intuition isn't giving me a strong read on which approach users
Stan> will like better. As the goal is at least partly to entice
Stan> printf()+recompile users into using a GDB command instead, I think it
Stan> needs to be among GDB's simpler commands. Maybe include both
Stan> syntaxes, mapping down to same functionality?
dprintf breakpoints have different semantics from regular breakpoints,
too, don't they? They are non-stopping.
It seems to me that if you go the suffix route, then you also need ways
to control the stopping-ness, and to change or delete the printf stuff.
I think it is simpler and hardly any less convenient for users to just
have dprintf and regular breakpoints be separate things.
If we get a flood of bug reports to the contrary, we can always go back
and add the printf qualifier later.
Tom
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-02-29 9:14 [PATCH] dynamic printf Stan Shebs
2012-02-29 10:28 ` Andreas Schwab
@ 2012-02-29 16:16 ` Joel Brobecker
2012-03-13 23:15 ` Stan Shebs
2012-02-29 18:21 ` Eli Zaretskii
` (2 subsequent siblings)
4 siblings, 1 reply; 15+ messages in thread
From: Joel Brobecker @ 2012-02-29 16:16 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
> 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.
Or like a tracepoint that does printf? Would it influence an operation
such as "next", for instance?
> The syntax of the command is
>
> dprintf <location> <format>,<arg>,<arg>...
The idea looks uber-cool to me! The number of times I've had to
rebuild GDB to add some temporary traces...
--
Joel
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH] dynamic printf
2012-02-29 16:16 ` Joel Brobecker
@ 2012-03-13 23:15 ` Stan Shebs
2012-03-13 23:40 ` Joel Brobecker
0 siblings, 1 reply; 15+ messages in thread
From: Stan Shebs @ 2012-03-13 23:15 UTC (permalink / raw)
To: Joel Brobecker; +Cc: gdb-patches
On 2/29/12 8:13 AM, Joel Brobecker wrote:
>> 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.
> Or like a tracepoint that does printf? Would it influence an operation
> such as "next", for instance?
>
>
There is a tracepoint similarity; one of the vague future-direction
ideas is to somehow refactor tracing and breaking so one could shift
back and forth freely, for instance to decide to collect some data at a
breakpoint, without having to do the formalized trace experiment setup.
I'm not sure what you mean by influencing "next"?
Stan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-03-13 23:15 ` Stan Shebs
@ 2012-03-13 23:40 ` Joel Brobecker
0 siblings, 0 replies; 15+ messages in thread
From: Joel Brobecker @ 2012-03-13 23:40 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
> >Or like a tracepoint that does printf? Would it influence an operation
> >such as "next", for instance?
> >
> >
>
> There is a tracepoint similarity; one of the vague future-direction
> ideas is to somehow refactor tracing and breaking so one could shift
> back and forth freely, for instance to decide to collect some data
> at a breakpoint, without having to do the formalized trace
> experiment setup.
>
> I'm not sure what you mean by influencing "next"?
I was referring to some suggestions that your new feature was equivalent
to a breakpoint with two commands that do the printing followed by a
continue. This method looks like a great way of tracing code (meaning
adding debugging traces), except that if the breakpoint is hit during
a "next", then you get your trace followed by your program running away
until hitting the next breakpoint, instead of stopping on the next line
of code. That's why I like referring to the tracepoint terminology...
--
Joel
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-02-29 9:14 [PATCH] dynamic printf Stan Shebs
2012-02-29 10:28 ` Andreas Schwab
2012-02-29 16:16 ` Joel Brobecker
@ 2012-02-29 18:21 ` Eli Zaretskii
2012-03-13 23:20 ` Stan Shebs
2012-03-01 14:04 ` Hui Zhu
2012-03-08 21:08 ` Tom Tromey
4 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2012-02-29 18:21 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
> Date: Tue, 28 Feb 2012 23:03:49 -0800
> From: Stan Shebs <stanshebs@earthlink.net>
>
> 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.
Thanks. I cannot say I like the name, though: the "dynamic" part has
no mnemonic value at all.
> + add_cmd ("agent-printf", class_maintenance, agent_printf_command,
> + _("Translate an expression into remote "
> + "agent bytecode for evaluation."),
> + &maintenancelist);
This new command seems to be undocumented.
> + 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\
How about listing the available styles with one line of description
for each one?
> +@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.)
And what happens if it isn't?
This needs a NEWS entry.
Thanks.
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH] dynamic printf
2012-02-29 18:21 ` Eli Zaretskii
@ 2012-03-13 23:20 ` Stan Shebs
0 siblings, 0 replies; 15+ messages in thread
From: Stan Shebs @ 2012-03-13 23:20 UTC (permalink / raw)
To: gdb-patches
On 2/29/12 10:22 AM, Eli Zaretskii wrote:
>> Date: Tue, 28 Feb 2012 23:03:49 -0800
>> From: Stan Shebs<stanshebs@earthlink.net>
>>
>> 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.
> Thanks. I cannot say I like the name, though: the "dynamic" part has
> no mnemonic value at all.
The idea is that it works like the "static" printf that has to be wired
into the code, but can be added and removed on the fly - aka
"dynamically". I'm not wedded to the terminology though, what else
would one call it?
>> + add_cmd ("agent-printf", class_maintenance, agent_printf_command,
>> + _("Translate an expression into remote "
>> + "agent bytecode for evaluation."),
>> + &maintenancelist);
> This new command seems to be undocumented.
Yes, thanks for catching that!
>> + 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\
> How about listing the available styles with one line of description
> for each one?
Yes, I forgot that part too...
>> +@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.)
> And what happens if it isn't?
>
It should report an error. I had in mind to add an option to specify
the formatting function, but the patch was starting to get complicated.
Stan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-02-29 9:14 [PATCH] dynamic printf Stan Shebs
` (2 preceding siblings ...)
2012-02-29 18:21 ` Eli Zaretskii
@ 2012-03-01 14:04 ` Hui Zhu
2012-03-04 6:31 ` Hui Zhu
2012-03-08 21:08 ` Tom Tromey
4 siblings, 1 reply; 15+ messages in thread
From: Hui Zhu @ 2012-03-01 14:04 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
Hi Stan,
This work is really cool!
I will try to make KGTP support it when it commit to trunk.
+ case gdb_agent_op_printf:
+ {
+ int chan, nargs, slen, i;
+ int args[100];
Just use support 10 args, I suggest this line should be: int args[10];
+ 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;
Suggest add a check in this part, then when i bigger than 9, stop set
top tp args[i]
+ if (--sp >= 0)
+ top = stack[sp];
+ }
I think this part need keep, because all to value that in the stack
need be clean.
+ /* (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;
+
Best,
Hui
On Wed, Feb 29, 2012 at 15:03, Stan Shebs <stanshebs@earthlink.net> wrote:
> 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 <location> <format>,<arg>,<arg>...
>
> 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 <stan@codesourcery.com>
>
> 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) <cond_breakpoints>: 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.
>
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH] dynamic printf
2012-03-01 14:04 ` Hui Zhu
@ 2012-03-04 6:31 ` Hui Zhu
0 siblings, 0 replies; 15+ messages in thread
From: Hui Zhu @ 2012-03-04 6:31 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
+/* Both the argument and consumed numbers are dynamic for this one. */
+DEFOP (printf, 0, 0, 0, 0, 0x34)
If I remember is right:
/* We need something here just to make the tables come out ok. */
DEFOP (invalid2, 0, 0, 0, 0, 0x31)
0x31 is keep for printf, I suggest use 0x31 as the opcode of printf.
Thanks,
Hui
On Thu, Mar 1, 2012 at 22:03, Hui Zhu <teawater@gmail.com> wrote:
> Hi Stan,
>
> This work is really cool!
>
> I will try to make KGTP support it when it commit to trunk.
>
>
> + case gdb_agent_op_printf:
> + {
> + int chan, nargs, slen, i;
> + int args[100];
>
> Just use support 10 args, I suggest this line should be: int args[10];
>
> + 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;
> Suggest add a check in this part, then when i bigger than 9, stop set
> top tp args[i]
>
> + if (--sp >= 0)
> + top = stack[sp];
> + }
> I think this part need keep, because all to value that in the stack
> need be clean.
>
>
> + /* (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;
> +
>
> Best,
> Hui
>
> On Wed, Feb 29, 2012 at 15:03, Stan Shebs <stanshebs@earthlink.net> wrote:
>> 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 <location> <format>,<arg>,<arg>...
>>
>> 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 <stan@codesourcery.com>
>>
>> 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) <cond_breakpoints>: 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.
>>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-02-29 9:14 [PATCH] dynamic printf Stan Shebs
` (3 preceding siblings ...)
2012-03-01 14:04 ` Hui Zhu
@ 2012-03-08 21:08 ` Tom Tromey
2012-03-13 23:51 ` Stan Shebs
4 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2012-03-08 21:08 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
>>>>> "Stan" == Stan Shebs <stanshebs@earthlink.net> writes:
Stan> This patch implements a "dynamic printf", which is basically a
Stan> breakpoint with a printf;continue as its command list - but with
Stan> additional features that make it more interesting.
Very nice. I implemented something like this in Python once, but
without the cool remote agent features.
Stan> dprintf <location> <format>,<arg>,<arg>...
Stan> where the location is as for breakpoints, while the format and args
Stan> are as for the printf command. So you could have something like
I think you have to have a comma after the location. That is the only
reliable linespec terminator.
Stan> The patch itself is somewhat of a hack-n-slash through the middle of
Stan> GDB, and there is much to critique. :-)
I have a few things, but nothing really major. Some of this is outside
areas I know much about, but I did at least skim it all and I think it
generally looks good.
Stan> +/* Parse the given expression, compile it into an agent expression
Stan> + that does a printf, and display the resulting expression. */
Stan> +
Stan> +extern char *parse_format_string (char **arg);
There must be a header that this could go in.
Stan> +#if 0
:)
Stan> +char *
Stan> +parse_format_string (char **arg)
Need intro comment.
This looks like bits were copied from printcmd.c:ui_printf.
Could the code be shared instead?
Stan> +/* Temporary hack to smuggle remainder of command line through. */
Stan> +char *glob_extra_string = NULL;
I'd much prefer something cleaner.
There's been a lot of work in recent times to clean up breakpoints in
various ways -- adding methods, refactorings, etc -- and there is more
to come. This goes against the trend.
Stan> + struct agent_expr *cmd_bytecode;
Needs a comment.
Perhaps subclassing bp_location is also doable?
Stan> + case gdb_agent_op_printf:
[...]
Stan> + /* (should re-check format before calling?) */
Stan> + printf (format,
Stan> + args[0], args[1], args[2], args[3], args[4],
Stan> + args[5], args[6], args[7], args[8], args[9]);
Yeah, this seems overly optimistic to me.
gdb used to have bugs where bad printf commands would crash it (IIRC
there are some closed PRs about this).
Maybe common-izing some code from ui_printf is the right thing? Or some
kind of format checking. Also this leaves you at the mercy of the host
printf for the '"%s", 0' case.
Tom
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH] dynamic printf
2012-03-08 21:08 ` Tom Tromey
@ 2012-03-13 23:51 ` Stan Shebs
2012-03-14 15:24 ` Tom Tromey
0 siblings, 1 reply; 15+ messages in thread
From: Stan Shebs @ 2012-03-13 23:51 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On 3/8/12 1:08 PM, Tom Tromey wrote:
>>>>>> "Stan" == Stan Shebs<stanshebs@earthlink.net> writes:
> Stan> This patch implements a "dynamic printf", which is basically a
> Stan> breakpoint with a printf;continue as its command list - but with
> Stan> additional features that make it more interesting.
>
> Very nice. I implemented something like this in Python once, but
> without the cool remote agent features.
>
> Stan> dprintf<location> <format>,<arg>,<arg>...
> Stan> where the location is as for breakpoints, while the format and args
> Stan> are as for the printf command. So you could have something like
>
> I think you have to have a comma after the location. That is the only
> reliable linespec terminator.
Hmmm. The format is a string though; would linespec parsing attempt to
proceed into that? How is "break <location> if foo" working these days?
>
> Stan> The patch itself is somewhat of a hack-n-slash through the middle of
> Stan> GDB, and there is much to critique. :-)
>
> I have a few things, but nothing really major. Some of this is outside
> areas I know much about, but I did at least skim it all and I think it
> generally looks good.
>
> Stan> +/* Parse the given expression, compile it into an agent expression
> Stan> + that does a printf, and display the resulting expression. */
> Stan> +
> Stan> +extern char *parse_format_string (char **arg);
>
> There must be a header that this could go in.
Yeah, I should make a separate patch to break out the format string parsing.
> Stan> +/* Temporary hack to smuggle remainder of command line through. */
> Stan> +char *glob_extra_string = NULL;
>
> I'd much prefer something cleaner.
> There's been a lot of work in recent times to clean up breakpoints in
> various ways -- adding methods, refactorings, etc -- and there is more
> to come. This goes against the trend.
I think the right thing is to make up a package for post-location
modifiers to break-type commands. We're up to condition, thread, task,
and now command, and maybe process and itset soon, all being passed as
separate arguments everywhere.
>
> Stan> + struct agent_expr *cmd_bytecode;
>
> Needs a comment.
> Perhaps subclassing bp_location is also doable?
I'm not sure what you mean by this?
>
> Stan> + case gdb_agent_op_printf:
> [...]
> Stan> + /* (should re-check format before calling?) */
> Stan> + printf (format,
> Stan> + args[0], args[1], args[2], args[3], args[4],
> Stan> + args[5], args[6], args[7], args[8], args[9]);
>
> Yeah, this seems overly optimistic to me.
> gdb used to have bugs where bad printf commands would crash it (IIRC
> there are some closed PRs about this).
> Maybe common-izing some code from ui_printf is the right thing? Or some
> kind of format checking. Also this leaves you at the mercy of the host
> printf for the '"%s", 0' case.
>
Yeah, literal printf is just having compounding problems. Among other
things, the args are all 64-bit values on the stack, so something like a
4-byte-getting "%d" can mess up vararg fetching bigtime. The ui_printf
code would make sense (and for the same reason), in that we are looking
at each format char so as to know how to cast the data value, but
ultimately relying on the library function to turn the value into
characters.
Stan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] dynamic printf
2012-03-13 23:51 ` Stan Shebs
@ 2012-03-14 15:24 ` Tom Tromey
0 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2012-03-14 15:24 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
>>>>> "Stan" == Stan Shebs <stanshebs@earthlink.net> writes:
Tom> I think you have to have a comma after the location. That is the only
Tom> reliable linespec terminator.
Stan> Hmmm. The format is a string though; would linespec parsing attempt
Stan> to proceed into that? How is "break <location> if foo" working these
Stan> days?
It is handled in multiple spots -- linespec.c:keep_name_info, but also
every language expression parser.
Probably linespec could be changed to make this syntax work, but I don't
see how the parsers could. E.g., the C parser will lex the printf
format string as a string token, then proceed to fail to parse, throwing
an exception.
That is, the case that breaks this is: dprintf *main "format".
This works, though, due to the fact that linespec uses
parse_to_comma_and_eval: dprintf *main, "format"
Stan> + struct agent_expr *cmd_bytecode;
Tom> Needs a comment.
Tom> Perhaps subclassing bp_location is also doable?
Stan> I'm not sure what you mean by this?
I thought it was possible to make a subclass of bp_location, arranging
for your breakpoint ops to override the appropriate allocation method.
Then only dprintf breakpoint locations would have the extra field.
I'm not totally sure, I haven't tried it.
Tom
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2012-03-14 15:28 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-29 9:14 [PATCH] dynamic printf Stan Shebs
2012-02-29 10:28 ` Andreas Schwab
2012-03-13 23:09 ` Stan Shebs
2012-03-14 14:47 ` Marc Khouzam
2012-03-14 15:28 ` Tom Tromey
2012-02-29 16:16 ` Joel Brobecker
2012-03-13 23:15 ` Stan Shebs
2012-03-13 23:40 ` Joel Brobecker
2012-02-29 18:21 ` Eli Zaretskii
2012-03-13 23:20 ` Stan Shebs
2012-03-01 14:04 ` Hui Zhu
2012-03-04 6:31 ` Hui Zhu
2012-03-08 21:08 ` Tom Tromey
2012-03-13 23:51 ` Stan Shebs
2012-03-14 15:24 ` Tom Tromey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox