* [PATCH] Dynamic printf for a target agent
@ 2012-05-30 1:10 Stan Shebs
2012-05-31 6:02 ` Yao Qi
0 siblings, 1 reply; 13+ messages in thread
From: Stan Shebs @ 2012-05-30 1:10 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 5120 bytes --]
This patch extends dynamic printf to run on the target, using a
debugging agent. Among other things, this lets the user disconnect GDB
from the target, and the prints continue to be active; so they are even
more like printf calls that don't have to be compiled into the code.
The user interface simply consists of an additional dprintf style
"agent", plus a disconnected-dprintf setshow to control the disconnected
printing behavior.
To address some of the issues raised previously, I took the moderately
drastic step of dismembering, uh, *refactoring* GDB's printf command,
separating out the format string parsing into a common/ function. So
now the code parses the format string on the host side and uses that to
create the correct list of agent expressions, then on the target side,
GDBserver does the same exact same parsing and prints each expression
result in the same way. Although it's a messy cut, there's no
user-visible behavior change, and no regressions from the testsuite.
This patch doesn't reflect the suggestion to special-case dprintf
behavior instead of using a command list; I'll respond to that
separately. The patch does set up general infrastructure to run
breakpoint commands on the target - that seems worth doing in any case,
as it will offer the same advantages for multicore debugging as is the
case for target-side breakpoint conditions.
Stan
2012-05-29 Stan Shebs <stan@codesourcery.com>
Add target-side support for dynamic printf.
* NEWS: Mention the additional style.
* breakpoint.h (struct bp_target_info): New fields tcommands, persist.
(struct bp_location): New field cmd_bytecode.
* breakpoint.c: Include format.h.
(disconnected_dprintf): New global.
(parse_cmd_to_aexpr): New function.
(build_target_command_list): New function.
(insert_bp_location): Call it.
(remove_breakpoints_pid): Skip dprintf breakpoints.
(print_one_breakpoint_location): Ditto.
(dprintf_style_agent): New global.
(dprintf_style_enums): Add dprintf_style_agent.
(update_dprintf_command_list): Add agent case.
(agent_printf_command): New function.
(_initialize_breakpoint): Add new commands.
* common/ax.def (printf): New bytecode.
* ax.h (ax_string): Declare.
* ax-gdb.h (gen_printf): Declare.
* ax-gdb.c: Include cli-utils.h, format.h.
(gen_printf): New function.
(maint_agent_print_command): New function.
(_initialize_ax_gdb): Add maint agent-printf command.
* ax-general.c (ax_string): New function.
(ax_print): Add printf disassembly.
* Makefile.in (SFILES): Add format.c
(COMMON_OBS): Add format.o.
* common/format.h: New file.
* common/format.c: New file.
* printcmd.c: Include format.h.
(ui_printf): Call parse_format_string.
* remote.c (remote_state): New field breakpoint_commands.
(PACKET_BreakpointCommands): 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]
* Makefile.in (WARN_CFLAGS_NO_FORMAT): Define.
(ax.o): Add it to build rule.
(ax-ipa.o): Ditto.
(OBS): Add format.o.
(IPA_OBS): Add format.o.
* 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.h (any_persistent_commands): Declare.
(add_breakpoint_commands): Declare.
(gdb_no_commands_at_breakpoint): Declare.
(run_breakpoint_commands): Declare.
* 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.
* linux-low.c (linux_wait_1): Test for and run breakpoint commands
locally.
* ax.c: Include format.h.
(ax_printf): New function.
(gdb_eval_agent_expr): Add printf opcode.
[doc]
* gdb.texinfo (Dynamic Printf): Mention agent style and
disconnected dprintf.
(Maintenance Commands): Describe maint agent-printf.
(General Query Packets): Mention BreakpointCommands feature.
(Packets): Document commands extension to Z0 packet.
* agentexpr.texi (Bytecode Descriptions): Document printf
bytecode.
[testsuite]
* gdb.base/dprintf.exp: Add agent style tests.
[-- Attachment #2: dprintf-patch-4 --]
[-- Type: text/plain, Size: 78821 bytes --]
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.526
diff -u -p -r1.526 NEWS
--- NEWS 24 May 2012 00:33:45 -0000 1.526
+++ NEWS 30 May 2012 00:20:55 -0000
@@ -228,11 +228,12 @@ set debug auto-load on|off
show debug auto-load
Control display of debugging info for auto-loading the files above.
-set dprintf-style gdb|call
+set dprintf-style gdb|call|agent
show dprintf-style
- Control the way in which a dynamic printf is performed; "gdb" requests
- a GDB printf command, while "call" causes dprintf to call a function
- in the inferior.
+ Control the way in which a dynamic printf is performed; "gdb"
+ requests a GDB printf command, while "call" causes dprintf to call a
+ function in the inferior. "agent" requests that the target agent
+ (such as GDBserver) do the printing.
set dprintf-function <expr>
show dprintf-function
@@ -241,6 +242,11 @@ show dprintf-channel
Set the function and optional first argument to the call when using
the "call" style of dynamic printf.
+set disconnected-dprintf on|off
+show disconnected-dprintf
+ Control whether agent-style dynamic printfs continue to be in effect
+ after GDB disconnects.
+
* New configure options
--with-auto-load-dir
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1208
diff -u -p -r1.1208 Makefile.in
--- Makefile.in 24 May 2012 00:33:45 -0000 1.1208
+++ Makefile.in 30 May 2012 00:20:55 -0000
@@ -744,7 +744,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \
regset.c sol-thread.c windows-termcap.c \
common/common-utils.c common/xml-utils.c \
- common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c
+ common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \
+ common/format.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -826,6 +827,7 @@ gnulib/import/extra/snippet/arg-nonnull.
gnulib/import/extra/snippet/warn-on-use.h \
gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \
common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \
+common/format.h \
common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h
# Header files that already have srcdir in them, or which are in objdir.
@@ -915,7 +917,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
inferior.o osdata.o gdb_usleep.o record.o gcore.o \
jit.o progspace.o skip.o probe.o \
- common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o
+ common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \
+ format.o
TSOBS = inflow.o
@@ -1914,6 +1917,10 @@ buffer.o: ${srcdir}/common/buffer.c
$(COMPILE) $(srcdir)/common/buffer.c
$(POSTCOMPILE)
+format.o: ${srcdir}/common/format.c
+ $(COMPILE) $(srcdir)/common/format.c
+ $(POSTCOMPILE)
+
linux-osdata.o: ${srcdir}/common/linux-osdata.c
$(COMPILE) $(srcdir)/common/linux-osdata.c
$(POSTCOMPILE)
Index: ax-gdb.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.c,v
retrieving revision 1.101
diff -u -p -r1.101 ax-gdb.c
--- ax-gdb.c 18 May 2012 21:02:47 -0000 1.101
+++ ax-gdb.c 30 May 2012 00:20:55 -0000
@@ -41,10 +41,13 @@
#include "tracepoint.h"
#include "cp-support.h"
#include "arch-utils.h"
+#include "cli/cli-utils.h"
#include "valprint.h"
#include "c-lang.h"
+#include "format.h"
+
/* To make sense of this file, you should read doc/agentexpr.texi.
Then look at the types and enums in ax-gdb.h. For the code itself,
look at gen_expr, towards the bottom; that's the main function that
@@ -2503,6 +2506,59 @@ 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, struct gdbarch *gdbarch,
+ CORE_ADDR function, LONGEST channel,
+ char *format, int fmtlen,
+ struct format_piece *frags,
+ int nargs, struct expression **exprs)
+{
+ struct expression *expr;
+ struct cleanup *old_chain = 0;
+ struct agent_expr *ax = new_agent_expr (gdbarch, scope);
+ union exp_element *pc;
+ struct axs_value value;
+ int i, tem, bot, fr, flen;
+ char *fmt;
+
+ 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);
+ }
+
+ /* Push function and channel. */
+ ax_const_l (ax, channel);
+ ax_const_l (ax, function);
+
+ /* Issue the printf bytecode proper. */
+ ax_simple (ax, aop_printf);
+ ax_simple (ax, nargs);
+ ax_string (ax, format, fmtlen);
+
+ /* 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)
{
@@ -2586,6 +2642,88 @@ 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. */
+
+static void
+maint_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;
+ char *format_start, *format_end;
+ struct format_piece *fpieces;
+ 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;
+
+ cmdrest = skip_spaces (cmdrest);
+
+ if (*cmdrest++ != '"')
+ error (_("Must start with a format string."));
+
+ format_start = cmdrest;
+
+ fpieces = parse_format_string (&cmdrest);
+
+ old_chain = make_cleanup (free_format_pieces_cleanup, &fpieces);
+
+ format_end = cmdrest;
+
+ if (*cmdrest++ != '"')
+ error (_("Bad format string, non-terminated '\"'."));
+
+ cmdrest = skip_spaces (cmdrest);
+
+ if (*cmdrest != ',' && *cmdrest != 0)
+ error (_("Invalid argument syntax"));
+
+ if (*cmdrest == ',')
+ cmdrest++;
+ cmdrest = skip_spaces (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;
+ /* else complain? */
+ }
+
+
+ agent = gen_printf (get_frame_pc (fi), get_current_arch (), 0, 0,
+ format_start, format_end - format_start,
+ fpieces, 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. */
+
+ do_cleanups (old_chain);
+ dont_repeat ();
+}
\f
/* Initialization code. */
@@ -2603,4 +2741,9 @@ _initialize_ax_gdb (void)
_("Translate an expression into remote "
"agent bytecode for evaluation."),
&maintenancelist);
+
+ add_cmd ("agent-printf", class_maintenance, maint_agent_printf_command,
+ _("Translate an expression into remote "
+ "agent bytecode for evaluation and display the bytecodes."),
+ &maintenancelist);
}
Index: ax-gdb.h
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.h,v
retrieving revision 1.24
diff -u -p -r1.24 ax-gdb.h
--- ax-gdb.h 27 Apr 2012 20:47:51 -0000 1.24
+++ ax-gdb.h 30 May 2012 00:20:55 -0000
@@ -115,6 +115,12 @@ extern void gen_expr (struct expression
extern void require_rvalue (struct agent_expr *ax, struct axs_value *value);
+struct format_piece;
+extern struct agent_expr *gen_printf (CORE_ADDR, struct gdbarch *,
+ CORE_ADDR, LONGEST, char *, int,
+ struct format_piece *,
+ 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 30 May 2012 00:20:55 -0000
@@ -330,6 +330,23 @@ ax_tsv (struct agent_expr *x, enum agent
x->buf[x->len + 2] = (num) & 0xff;
x->len += 3;
}
+
+/* Append a string to the expression. Note that the string is going
+ into the bytecodes directly, not on the stack. As a precaution,
+ include both length as prefix, and terminate with a NUL. */
+
+void
+ax_string (struct agent_expr *x, char *str, int slen)
+{
+ int i;
+
+ grow_expr (x, slen + 3);
+ x->buf[x->len++] = ((slen + 1) >> 8) & 0xff;
+ x->buf[x->len++] = (slen + 1) & 0xff;
+ for (i = 0; i < slen; ++i)
+ x->buf[x->len++] = str[i];
+ x->buf[x->len++] = '\0';
+}
\f
@@ -391,6 +408,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 slen, nargs;
+
+ ++i;
+ nargs = x->buf[i++];
+ slen = x->buf[i++];
+ slen = slen * 256 + x->buf[i++];
+ fprintf_filtered (f, " \"%s\", %d args",
+ &(x->buf[i]), nargs);
+ i += slen - 1;
+ }
fprintf_filtered (f, "\n");
i += 1 + aop_map[op].op_size;
Index: ax.h
===================================================================
RCS file: /cvs/src/src/gdb/ax.h,v
retrieving revision 1.21
diff -u -p -r1.21 ax.h
--- ax.h 24 Feb 2012 15:10:58 -0000 1.21
+++ ax.h 30 May 2012 00:20:55 -0000
@@ -219,6 +219,9 @@ extern void ax_reg_mask (struct agent_ex
/* Assemble code to operate on a trace state variable. */
extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num);
+
+/* Append a string to the bytecode stream. */
+extern void ax_string (struct agent_expr *x, char *str, int slen);
\f
/* Functions for printing out expressions, and otherwise debugging
Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.677
diff -u -p -r1.677 breakpoint.c
--- breakpoint.c 24 May 2012 16:51:34 -0000 1.677
+++ breakpoint.c 30 May 2012 00:20:56 -0000
@@ -69,6 +69,8 @@
#include "gdb_regex.h"
#include "ax-gdb.h"
+#include "format.h"
+
/* readline include files */
#include "readline/readline.h"
#include "readline/history.h"
@@ -297,6 +299,10 @@ static struct breakpoint_ops bkpt_probe_
/* Dynamic printf class type. */
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
@@ -2079,6 +2085,195 @@ build_target_condition_list (struct bp_l
return;
}
+/* 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. */
+
+static struct agent_expr *
+parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd)
+{
+ struct cleanup *old_cleanups = 0;
+ struct expression *expr, **argvec;
+ struct agent_expr *aexpr = NULL;
+ struct cleanup *old_chain = NULL;
+ volatile struct gdb_exception ex;
+ char *cmdrest;
+ char *format_start, *format_end;
+ struct format_piece *fpieces;
+ int nargs;
+ struct gdbarch *gdbarch = get_current_arch ();
+
+ if (!cmd)
+ return NULL;
+
+ cmdrest = cmd;
+
+ if (*cmdrest == ',')
+ ++cmdrest;
+ cmdrest = skip_spaces (cmdrest);
+
+ if (*cmdrest++ != '"')
+ error (_("No format string following the location"));
+
+ format_start = cmdrest;
+
+ fpieces = parse_format_string (&cmdrest);
+
+ old_cleanups = make_cleanup (free_format_pieces_cleanup, &fpieces);
+
+ format_end = cmdrest;
+
+ if (*cmdrest++ != '"')
+ error (_("Bad format string, non-terminated '\"'."));
+
+ cmdrest = skip_spaces (cmdrest);
+
+ if (!(*cmdrest == ',' || *cmdrest == '\0'))
+ error (_("Invalid argument syntax"));
+
+ if (*cmdrest == ',')
+ cmdrest++;
+ cmdrest = skip_spaces (cmdrest);
+
+ /* For each argument, make an expression. */
+
+ argvec = (struct expression **) alloca (strlen (cmd)
+ * sizeof (struct expression *));
+
+ 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, gdbarch, 0, 0,
+ format_start, format_end - format_start,
+ fpieces, nargs, argvec);
+ }
+
+ if (ex.reason < 0)
+ {
+ /* If we got here, it means the command 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 other commands. */
+ return NULL;
+ }
+
+ do_cleanups (old_cleanups);
+
+ /* We have a valid agent expression, return it. */
+ 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 commands since something changed. In that
+ case we already freed the command bytecodes (see
+ force_breakpoint_reinsertion). We just
+ need to parse the command 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 command 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 command 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 commands to the target. */
+ VEC_safe_push (agent_expr_p, bl->target_info.tcommands,
+ 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.
@@ -2119,7 +2314,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;
}
@@ -2638,6 +2834,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);
@@ -5769,6 +5968,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)
{
@@ -8405,9 +8613,11 @@ bp_loc_is_permanent (struct bp_location
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;
@@ -8452,9 +8662,9 @@ update_dprintf_command_list (struct brea
if (*dprintf_args != '"')
error (_("Bad format string, missing '\"'."));
- if (strcmp (dprintf_style, "gdb") == 0)
+ if (strcmp (dprintf_style, dprintf_style_gdb) == 0)
printf_line = xstrprintf ("printf %s", dprintf_args);
- else if (strcmp (dprintf_style, "call") == 0)
+ else if (strcmp (dprintf_style, dprintf_style_call) == 0)
{
if (!dprintf_function)
error (_("No function supplied for dprintf call"));
@@ -8469,6 +8679,16 @@ update_dprintf_command_list (struct brea
dprintf_function,
dprintf_args);
}
+ else if (strcmp (dprintf_style, dprintf_style_agent) == 0)
+ {
+ if (target_can_run_breakpoint_commands ())
+ printf_line = xstrprintf ("agent-printf %s", dprintf_args);
+ else
+ {
+ warning (_("Target cannot run dprintf commands, falling back to GDB printf"));
+ printf_line = xstrprintf ("printf %s", dprintf_args);
+ }
+ }
else
internal_error (__FILE__, __LINE__,
_("Invalid dprintf style."));
@@ -8478,12 +8698,15 @@ update_dprintf_command_list (struct brea
{
struct command_line *printf_cmd_line, *cont_cmd_line = NULL;
- cont_cmd_line = xmalloc (sizeof (struct command_line));
- cont_cmd_line->control_type = simple_control;
- cont_cmd_line->body_count = 0;
- cont_cmd_line->body_list = NULL;
- cont_cmd_line->next = NULL;
- cont_cmd_line->line = xstrdup ("continue");
+ if (strcmp (dprintf_style, dprintf_style_agent) != 0)
+ {
+ cont_cmd_line = xmalloc (sizeof (struct command_line));
+ cont_cmd_line->control_type = simple_control;
+ cont_cmd_line->body_count = 0;
+ cont_cmd_line->body_list = NULL;
+ cont_cmd_line->next = NULL;
+ cont_cmd_line->line = xstrdup ("continue");
+ }
printf_cmd_line = xmalloc (sizeof (struct command_line));
printf_cmd_line->control_type = simple_control;
@@ -9432,6 +9655,12 @@ dprintf_command (char *arg, int from_tty
0);
}
+static void
+agent_printf_command (char *arg, int from_tty)
+{
+ error (_("May only run agent-printf on the target"));
+}
+
/* Implement the "breakpoint_hit" breakpoint_ops method for
ranged breakpoints. */
@@ -16094,6 +16323,20 @@ Show the channel to use for dynamic prin
update_dprintf_commands, 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);
+
+ add_com ("agent-printf", class_vars, agent_printf_command, _("\
+agent-printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\
+(target agent only) This is useful for formatted output in user-defined commands."));
+
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.183
diff -u -p -r1.183 breakpoint.h
--- breakpoint.h 16 May 2012 14:35:03 -0000 1.183
+++ breakpoint.h 30 May 2012 00:20:56 -0000
@@ -271,6 +271,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) *tcommands;
+
+ /* 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
@@ -357,8 +365,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
Index: printcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/printcmd.c,v
retrieving revision 1.208
diff -u -p -r1.208 printcmd.c
--- printcmd.c 18 May 2012 21:02:50 -0000 1.208
+++ printcmd.c 30 May 2012 00:20:56 -0000
@@ -49,18 +49,12 @@
#include "charset.h"
#include "arch-utils.h"
#include "cli/cli-utils.h"
+#include "format.h"
#ifdef TUI
#include "tui/tui.h" /* For tui_active et al. */
#endif
-#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
-# define USE_PRINTF_I64 1
-# define PRINTF_HAS_LONG_LONG
-#else
-# define USE_PRINTF_I64 0
-#endif
-
struct format_data
{
int count;
@@ -2001,15 +1995,15 @@ print_variable_and_value (const char *na
static void
ui_printf (char *arg, struct ui_file *stream)
{
+ struct format_piece *fpieces;
char *f = NULL;
char *s = arg;
char *string = NULL;
struct value **val_args;
- char *substrings;
- char *current_substring;
int nargs = 0;
int allocated_args = 20;
struct cleanup *old_cleanups;
+ int nargs_wanted;
val_args = xmalloc (allocated_args * sizeof (struct value *));
old_cleanups = make_cleanup (free_current_contents, &val_args);
@@ -2023,64 +2017,13 @@ ui_printf (char *arg, struct ui_file *st
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 '\"'."));
+ fpieces = parse_format_string (&s);
- 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;
+ make_cleanup (free_format_pieces_cleanup, &fpieces);
- default:
- *f++ = c;
- }
- }
-
- /* Skip over " and following space and comma. */
- s++;
- *f++ = '\0';
+ if (*s++ != '"')
+ error (_("Bad format string, non-terminated '\"'."));
+
s = skip_spaces (s);
if (*s != ',' && *s != 0)
@@ -2090,240 +2033,14 @@ ui_printf (char *arg, struct ui_file *st
s++;
s = skip_spaces (s);
- /* Need extra space for the '\0's. Doubling the size is sufficient. */
- substrings = alloca (strlen (string) * 2);
- current_substring = substrings;
-
{
- /* Now scan the string for %-specs and see what kinds of args they want.
- argclass[I] classifies the %-specs so we can give printf_filtered
- something of the right size. */
-
- enum argclass
- {
- int_arg, long_arg, long_long_arg, ptr_arg,
- string_arg, wide_string_arg, wide_char_arg,
- double_arg, long_double_arg, decfloat_arg
- };
- enum argclass *argclass;
- enum argclass this_argclass;
- char *last_arg;
- int nargs_wanted;
- int i;
+ int i, fr;
+ char *current_substring;
- argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass);
nargs_wanted = 0;
- f = string;
- last_arg = string;
- while (*f)
- if (*f++ == '%')
- {
- int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
- int seen_space = 0, seen_plus = 0;
- int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
- int seen_big_d = 0, seen_double_big_d = 0;
- int bad = 0;
-
- /* Check the validity of the format specifier, and work
- out what argument it expects. We only accept C89
- format strings, with the exception of long long (which
- we autoconf for). */
-
- /* Skip over "%%". */
- if (*f == '%')
- {
- f++;
- continue;
- }
-
- /* The first part of a format specifier is a set of flag
- characters. */
- while (strchr ("0-+ #", *f))
- {
- if (*f == '#')
- seen_hash = 1;
- else if (*f == '0')
- seen_zero = 1;
- else if (*f == ' ')
- seen_space = 1;
- else if (*f == '+')
- seen_plus = 1;
- f++;
- }
-
- /* The next part of a format specifier is a width. */
- while (strchr ("0123456789", *f))
- f++;
-
- /* The next part of a format specifier is a precision. */
- if (*f == '.')
- {
- seen_prec = 1;
- f++;
- while (strchr ("0123456789", *f))
- f++;
- }
-
- /* The next part of a format specifier is a length modifier. */
- if (*f == 'h')
- {
- seen_h = 1;
- f++;
- }
- else if (*f == 'l')
- {
- f++;
- lcount++;
- if (*f == 'l')
- {
- f++;
- lcount++;
- }
- }
- else if (*f == 'L')
- {
- seen_big_l = 1;
- f++;
- }
- /* Decimal32 modifier. */
- else if (*f == 'H')
- {
- seen_big_h = 1;
- f++;
- }
- /* Decimal64 and Decimal128 modifiers. */
- else if (*f == 'D')
- {
- f++;
-
- /* Check for a Decimal128. */
- if (*f == 'D')
- {
- f++;
- seen_double_big_d = 1;
- }
- else
- seen_big_d = 1;
- }
-
- switch (*f)
- {
- case 'u':
- if (seen_hash)
- bad = 1;
- /* FALLTHROUGH */
-
- case 'o':
- case 'x':
- case 'X':
- if (seen_space || seen_plus)
- bad = 1;
- /* FALLTHROUGH */
-
- case 'd':
- case 'i':
- if (lcount == 0)
- this_argclass = int_arg;
- else if (lcount == 1)
- this_argclass = long_arg;
- else
- this_argclass = long_long_arg;
-
- if (seen_big_l)
- bad = 1;
- break;
-
- case 'c':
- this_argclass = lcount == 0 ? int_arg : wide_char_arg;
- if (lcount > 1 || seen_h || seen_big_l)
- bad = 1;
- if (seen_prec || seen_zero || seen_space || seen_plus)
- bad = 1;
- break;
-
- case 'p':
- this_argclass = ptr_arg;
- if (lcount || seen_h || seen_big_l)
- bad = 1;
- if (seen_prec || seen_zero || seen_space || seen_plus)
- bad = 1;
- break;
-
- case 's':
- this_argclass = lcount == 0 ? string_arg : wide_string_arg;
- if (lcount > 1 || seen_h || seen_big_l)
- bad = 1;
- if (seen_zero || seen_space || seen_plus)
- bad = 1;
- break;
-
- case 'e':
- case 'f':
- case 'g':
- case 'E':
- case 'G':
- if (seen_big_h || seen_big_d || seen_double_big_d)
- this_argclass = decfloat_arg;
- else if (seen_big_l)
- this_argclass = long_double_arg;
- else
- this_argclass = double_arg;
-
- if (lcount || seen_h)
- bad = 1;
- break;
-
- case '*':
- error (_("`*' not supported for precision or width in printf"));
-
- case 'n':
- error (_("Format specifier `n' not supported in printf"));
-
- case '\0':
- error (_("Incomplete format specifier at end of format string"));
-
- default:
- error (_("Unrecognized format specifier '%c' in printf"), *f);
- }
-
- if (bad)
- error (_("Inappropriate modifiers to "
- "format specifier '%c' in printf"),
- *f);
-
- f++;
-
- if (lcount > 1 && USE_PRINTF_I64)
- {
- /* Windows' printf does support long long, but not the usual way.
- Convert %lld to %I64d. */
- int length_before_ll = f - last_arg - 1 - lcount;
-
- strncpy (current_substring, last_arg, length_before_ll);
- strcpy (current_substring + length_before_ll, "I64");
- current_substring[length_before_ll + 3] =
- last_arg[length_before_ll + lcount];
- current_substring += length_before_ll + 4;
- }
- else if (this_argclass == wide_string_arg
- || this_argclass == wide_char_arg)
- {
- /* Convert %ls or %lc to %s. */
- int length_before_ls = f - last_arg - 2;
-
- strncpy (current_substring, last_arg, length_before_ls);
- strcpy (current_substring + length_before_ls, "s");
- current_substring += length_before_ls + 2;
- }
- else
- {
- strncpy (current_substring, last_arg, f - last_arg);
- current_substring += f - last_arg;
- }
- *current_substring++ = '\0';
- last_arg = f;
- argclass[nargs_wanted++] = this_argclass;
- }
+ for (fr = 0; fpieces[fr].string != NULL; fr++)
+ if (fpieces[fr].argclass != literal_piece)
+ ++nargs_wanted;
/* Now, parse all arguments and evaluate them.
Store the VALUEs in VAL_ARGS. */
@@ -2349,10 +2066,11 @@ ui_printf (char *arg, struct ui_file *st
error (_("Wrong number of arguments for specified format-string"));
/* Now actually print them. */
- current_substring = substrings;
- for (i = 0; i < nargs; i++)
+ i = 0;
+ for (fr = 0; fpieces[fr].string != NULL; fr++)
{
- switch (argclass[i])
+ current_substring = fpieces[fr].string;
+ switch (fpieces[fr].argclass)
{
case string_arg:
{
@@ -2687,20 +2405,25 @@ ui_printf (char *arg, struct ui_file *st
break;
}
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ fprintf_filtered (stream, current_substring, 0);
+ break;
default:
internal_error (__FILE__, __LINE__,
_("failed internal consistency check"));
}
- /* Skip to the next substring. */
- current_substring += strlen (current_substring) + 1;
+ /* Maybe advance to the next argument. */
+ if (fpieces[fr].argclass != literal_piece)
+ ++i;
}
- /* Print the portion of the format string after the last argument.
- Note that this will not include any ordinary %-specs, but it
- might include "%%". That is why we use printf_filtered and not
- puts_filtered here. Also, we pass a dummy argument because
- some platforms have modified GCC to include -Wformat-security
- by default, which will warn here if there is no argument. */
- fprintf_filtered (stream, last_arg, 0);
}
do_cleanups (old_cleanups);
}
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.499
diff -u -p -r1.499 remote.c
--- remote.c 24 May 2012 16:51:35 -0000 1.499
+++ remote.c 30 May 2012 00:20:56 -0000
@@ -242,6 +242,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. */
@@ -323,6 +325,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;
@@ -1274,6 +1280,7 @@ enum {
PACKET_qAttached,
PACKET_ConditionalTracepoints,
PACKET_ConditionalBreakpoints,
+ PACKET_BreakpointCommands,
PACKET_FastTracepoints,
PACKET_StaticTracepoints,
PACKET_InstallInTrace,
@@ -3801,6 +3808,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)
@@ -3898,6 +3915,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,
@@ -7847,6 +7866,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->tcommands))
+ 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->tcommands, 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->tcommands);
+}
+
/* 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. */
@@ -7884,6 +7934,9 @@ remote_insert_breakpoint (struct gdbarch
if (remote_supports_cond_breakpoints ())
remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf);
+ 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);
@@ -8125,6 +8178,9 @@ remote_insert_hw_breakpoint (struct gdba
if (remote_supports_cond_breakpoints ())
remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf);
+ 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);
@@ -10063,6 +10119,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)
{
@@ -10981,6 +11045,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;
@@ -11511,6 +11576,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.307
diff -u -p -r1.307 target.c
--- target.c 24 May 2012 16:39:11 -0000 1.307
+++ target.c 30 May 2012 00:20:56 -0000
@@ -703,6 +703,7 @@ update_current_target (void)
INHERIT (to_can_use_agent, t);
INHERIT (to_magic, t);
INHERIT (to_supports_evaluation_of_breakpoint_conditions, t);
+ INHERIT (to_can_run_breakpoint_commands, t);
/* Do not inherit to_memory_map. */
/* Do not inherit to_flash_erase. */
/* Do not inherit to_flash_done. */
@@ -932,6 +933,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_use_agent,
(int (*) (int))
tcomplain);
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.235
diff -u -p -r1.235 target.h
--- target.h 24 May 2012 16:51:36 -0000 1.235
+++ target.h 30 May 2012 00:20:56 -0000
@@ -672,6 +672,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
@@ -991,6 +995,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: doc/agentexpr.texi
===================================================================
RCS file: /cvs/src/src/gdb/doc/agentexpr.texi,v
retrieving revision 1.17
diff -u -p -r1.17 agentexpr.texi
--- doc/agentexpr.texi 4 Jan 2012 08:17:20 -0000 1.17
+++ doc/agentexpr.texi 30 May 2012 00:20:56 -0000
@@ -493,6 +493,22 @@ Record the bytes at @var{addr} in a trac
by GDB. Stop at either the first zero byte, or when @var{size} bytes
have been recorded, whichever occurs first.
+@item @code{printf} (0x34) @var{numargs} @var{string} @result{}
+Do a formatted print, in the style of the C function @code{printf}).
+The value of @var{numargs} is the number of arguments to expect on the
+stack, while @var{string} is the format string, prefixed with a
+two-byte length, and suffixed with a zero byte. The format string
+includes escaped sequences just as it appears in C source, so for
+instance the format string @code{"\t%d\n"} is six characters long, and
+the output will consist of a tab character, a decimal number, and a
+newline. At the top of the stack, above the values to be printed,
+this bytecode will pop a ``function'' and ``channel''. If the
+function is nonzero, then the target may treat it as a function and
+call it, passing the channel as a first argument, as with the C
+function @code{fprintf}. If the function is zero, then the target may
+simply call a standard printf function of its choice. In all, this
+bytecode pops 2 + @var{numargs} stack elements, and pushes nothing.
+
@item @code{end} (0x27): @result{}
Stop executing bytecode; the result should be the top element of the
stack. If the purpose of the expression was to compute an lvalue or a
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.971
diff -u -p -r1.971 gdb.texinfo
--- doc/gdb.texinfo 24 May 2012 00:33:46 -0000 1.971
+++ doc/gdb.texinfo 30 May 2012 00:20:57 -0000
@@ -4646,6 +4646,14 @@ program's @code{printf} function. This
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{}]
@@ -4669,6 +4677,12 @@ Handle the output using the @value{GDBN}
Handle the output by calling a function in your program (normally
@code{printf}).
+@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 running commands on the target.
+
@item set dprintf-function @var{function}
Set the function to call if the dprintf style is @code{call}. By
default its value is @code{printf}. You may set it to any expression.
@@ -4703,6 +4717,17 @@ Note that the @code{info break} displays
as normal breakpoint commands; you can thus easily see the effect of
the variable settings.
+@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. This only applies
+if the @code{dprintf-style} is @code{agent}.
+
+@item show disconnected-dprintf off
+@kindex show disconnected-dprintf
+Show the current choice for disconnected @code{dprintf}.
+
@end table
@value{GDBN} does not check the validity of function and channel,
@@ -34250,6 +34275,13 @@ of the addresses of @code{globa} and @co
the result of the addition, while an evaluation expression will do the
addition and return the sum.
+@kindex maint agent-printf
+@item maint agent-printf @var{format},@var{expr},...
+Translate the given format string and list of argument expressions
+into remote agent bytecodes and display them as a disassembled list.
+This command is useful for debugging the agent version of dynamic
+printf (@pxref{Dynamic Printf}.
+
@kindex maint info breakpoints
@item @anchor{maint info breakpoints}maint info breakpoints
Using the same format as @samp{info breakpoints}, display both the
@@ -35484,7 +35516,7 @@ avoid potential problems with duplicate
be implemented in an idempotent way.}
@item z0,@var{addr},@var{kind}
-@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]}
+@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]}@r{[};cmds:@var{persist},@var{cmd_list}@dots{}@r{]}
@cindex @samp{z0} packet
@cindex @samp{Z0} packet
Insert (@samp{Z0}) or remove (@samp{z0}) a memory breakpoint at address
@@ -35512,6 +35544,22 @@ actual conditional expression in bytecod
@end table
+The optional @var{cmd_list} parameter introduces commands that may be
+run on the target, rather than being reported back to @value{GDBN}.
+The parameter starts with a numeric flag @var{persist}; if the flag is
+nonzero, then the breakpoint may remain active and the commands
+continue to be run even when @value{GDBN} disconnects from the target.
+Following this flag is a series of expressions concatenated with no
+separators. Each expression has the following form:
+
+@table @samp
+
+@item X @var{len},@var{expr}
+@var{len} is the length of the bytecode expression and @var{expr} is the
+actual conditional expression in bytecode form.
+
+@end table
+
see @ref{Architecture-Specific Protocol Details}.
@emph{Implementation note: It is possible for a target to copy or move
@@ -36445,6 +36493,11 @@ These are the currently defined stub fea
@tab @samp{-}
@tab No
+@item @samp{BreakpointCommands}
+@tab No
+@tab @samp{-}
+@tab No
+
@end multitable
These are the currently defined stub features, in more detail:
@@ -36594,6 +36647,11 @@ to be enabled and disabled while a trace
The remote stub supports the @samp{tracenz} bytecode for collecting strings.
See @ref{Bytecode Descriptions} for details about the bytecode.
+@item BreakpointCommands
+@cindex breakpoint commands, in remote protocol
+The remote stub supports running a breakpoint's command list itself,
+rather than reporting the hit to @value{GDBN}.
+
@end table
@item qSymbol::
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 30 May 2012 00:20:58 -0000
@@ -18,6 +18,7 @@
#include "server.h"
#include "ax.h"
+#include "format.h"
static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2);
@@ -789,6 +790,123 @@ compile_bytecodes (struct agent_expr *ae
#endif
+/* Make printf-type calls using arguments supplied from the host. We
+ need to parse the format string ourselves, and call the formatting
+ function with one argument at a time, partly because there is no
+ safe portable way to construct a varargs call, and partly to serve
+ as a security barrier against bad format strings that might get
+ in. */
+
+static void
+ax_printf (CORE_ADDR fn, CORE_ADDR chan, char *format,
+ int nargs, ULONGEST *args)
+{
+ char *f = format;
+ struct format_piece *fpieces;
+ int i, fp;
+ char *current_substring;
+ int nargs_wanted;
+
+ ax_debug ("Printf of \"%s\" with %d args", format, nargs);
+
+ fpieces = parse_format_string (&f);
+
+ nargs_wanted = 0;
+ for (fp = 0; fpieces[fp].string != NULL; fp++)
+ if (fpieces[fp].argclass != literal_piece)
+ ++nargs_wanted;
+
+ if (nargs != nargs_wanted)
+ error (_("Wrong number of arguments for specified format-string"));
+
+ i = 0;
+ for (fp = 0; fpieces[fp].string != NULL; fp++)
+ {
+ current_substring = fpieces[fp].string;
+ ax_debug ("current substring is '%s', class is %d",
+ current_substring, fpieces[fp].argclass);
+ switch (fpieces[fp].argclass)
+ {
+ case string_arg:
+ {
+ gdb_byte *str;
+ CORE_ADDR tem;
+ int j;
+
+ tem = args[i];
+
+ /* This is a %s argument. Find the length of the string. */
+ for (j = 0;; j++)
+ {
+ gdb_byte c;
+
+ read_inferior_memory (tem + j, &c, 1);
+ if (c == 0)
+ break;
+ }
+
+ /* Copy the string contents into a string inside GDB. */
+ str = (gdb_byte *) alloca (j + 1);
+ if (j != 0)
+ read_inferior_memory (tem, str, j);
+ str[j] = 0;
+
+ printf (current_substring, (char *) str);
+ }
+ break;
+
+ case long_long_arg:
+#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG)
+ {
+ long long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+#else
+ error (_("long long not supported in agent printf"));
+#endif
+ case int_arg:
+ {
+ int val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case long_arg:
+ {
+ long val = args[i];
+
+ printf (current_substring, val);
+ break;
+ }
+
+ case literal_piece:
+ /* Print a portion of the format string that has no
+ directives. Note that this will not include any
+ ordinary %-specs, but it might include "%%". That is
+ why we use printf_filtered and not puts_filtered here.
+ Also, we pass a dummy argument because some platforms
+ have modified GCC to include -Wformat-security by
+ default, which will warn here if there is no
+ argument. */
+ printf (current_substring, 0);
+ break;
+
+ default:
+ error (_("Format directive in '%s' not supported in agent printf"),
+ current_substring);
+ }
+
+ /* Maybe advance to the next argument. */
+ if (fpieces[fp].argclass != literal_piece)
+ ++i;
+ }
+
+ free_format_pieces (fpieces);
+}
+
/* The agent expression evaluator, as specified by the GDB docs. It
returns 0 if everything went OK, and a nonzero error code
otherwise. */
@@ -1152,6 +1270,37 @@ gdb_eval_agent_expr (struct regcache *re
top = stack[sp];
break;
+ case gdb_agent_op_printf:
+ {
+ int nargs, slen, i;
+ CORE_ADDR fn = 0, chan = 0;
+ /* Can't have more args than the entire size of the stack. */
+ ULONGEST args[STACK_MAX];
+ char *format;
+
+ nargs = aexpr->bytes[pc++];
+ slen = aexpr->bytes[pc++];
+ slen = (slen << 8) + aexpr->bytes[pc++];
+ format = (char *) &(aexpr->bytes[pc]);
+ pc += slen;
+ /* Pop function and channel. */
+ fn = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ chan = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ /* Pop arguments into a dedicated array. */
+ for (i = 0; i < nargs; ++i)
+ {
+ args[i] = top;
+ if (--sp >= 0)
+ top = stack[sp];
+ }
+ ax_printf (fn, chan, format, nargs, args);
+ }
+ 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.221
diff -u -p -r1.221 linux-low.c
--- gdbserver/linux-low.c 28 May 2012 20:37:29 -0000 1.221
+++ gdbserver/linux-low.c 30 May 2012 00:20:59 -0000
@@ -2618,7 +2618,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
@@ -3498,7 +3501,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/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v
retrieving revision 1.133
diff -u -p -r1.133 Makefile.in
--- gdbserver/Makefile.in 29 Apr 2012 06:28:30 -0000 1.133
+++ gdbserver/Makefile.in 30 May 2012 00:20:59 -0000
@@ -100,6 +100,9 @@ GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS
WARN_CFLAGS = @WARN_CFLAGS@
WERROR_CFLAGS = @WERROR_CFLAGS@
+WARN_CFLAGS_NO_FORMAT = `echo " $(WARN_CFLAGS) " \
+ | sed "s/ -Wformat-nonliteral / -Wno-format-nonliteral /g"`
+
# CFLAGS is specifically reserved for setting from the command line
# when running make. I.E. "make CFLAGS=-Wmissing-prototypes".
CFLAGS = @CFLAGS@
@@ -151,7 +154,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPAR
OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o target.o \
utils.o version.o vec.o \
mem-break.o hostio.o event-loop.o tracepoint.o \
- xml-utils.o common-utils.o ptid.o buffer.o \
+ xml-utils.o common-utils.o ptid.o buffer.o format.o \
dll.o \
$(XML_BUILTIN) \
$(DEPFILES) $(LIBOBJS)
@@ -272,7 +275,7 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS)
${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \
$(XM_CLIBS)
-IPA_OBJS=ax-ipa.o tracepoint-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
+IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES}
IPA_LIB=libinproctrace.so
@@ -446,11 +449,13 @@ IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_
# In-process agent object rules
ax-ipa.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
- $(CC) -c $(IPAGENT_CFLAGS) $< -o ax-ipa.o
+ $(CC) -c $(IPAGENT_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< -o ax-ipa.o
tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h}
$(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o
utils-ipa.o: utils.c $(server_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o
+format-ipa.o: ../common/format.c $(server_h) ${ax_h}
+ $(CC) -c $(IPAGENT_CFLAGS) $< -o format-ipa.o
common-utils-ipa.o: ../common/common-utils.c $(server_h)
$(CC) -c $(IPAGENT_CFLAGS) $< -o common-utils-ipa.o
remote-utils-ipa.o: remote-utils.c $(server_h)
@@ -467,6 +472,7 @@ amd64-linux-ipa.o : amd64-linux.c $(regd
$(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o
ax.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def
+ $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $<
event-loop.o: event-loop.c $(server_h)
hostio.o: hostio.c $(server_h)
hostio-errno.o: hostio-errno.c $(server_h)
@@ -511,6 +517,9 @@ ptid.o: ../common/ptid.c $(ptid_h)
buffer.o: ../common/buffer.c $(server_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
+format.o: ../common/format.c $(server_h)
+ $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
+
agent.o: ../common/agent.c $(server_h) $(agent_h)
$(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER
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 30 May 2012 00:20:59 -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 30 May 2012 00:20:59 -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.171
diff -u -p -r1.171 server.c
--- gdbserver/server.c 24 May 2012 16:51:37 -0000 1.171
+++ gdbserver/server.c 30 May 2012 00:20:59 -0000
@@ -1687,8 +1687,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+");
if (target_supports_agent ())
strcat (own_buf, ";QAgent+");
@@ -2909,6 +2910,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 != ';')
@@ -2918,22 +2920,33 @@ process_point_options (CORE_ADDR point_a
while (*dataptr)
{
- switch (*dataptr)
+ if (*dataptr == ';')
+ ++dataptr;
+
+ if (*dataptr == 'X')
{
- case 'X':
- /* Conditional expression. */
- if (remote_debug)
- 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;
@@ -2999,7 +3012,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);
@@ -3010,9 +3023,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
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 30 May 2012 00:20:59 -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: common/format.c
===================================================================
RCS file: common/format.c
diff -N common/format.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ common/format.c 30 May 2012 00:20:59 -0000
@@ -0,0 +1,400 @@
+/* Parse a printf-style format string.
+
+ Copyright (C) 1986-2012 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef GDBSERVER
+#include "server.h"
+#else
+#include "defs.h"
+#endif
+
+#include <string.h>
+
+#include "format.h"
+
+struct format_piece *
+parse_format_string (char **arg)
+{
+ char *s, *f, *string;
+ char *prev_start;
+ char *percent_loc;
+ char *sub_start, *current_substring;
+ struct format_piece *pieces;
+ int next_frag;
+ int max_pieces;
+ enum argclass this_argclass;
+
+ s = *arg;
+
+ /* 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 != '"' && *s != '\0')
+ {
+ int c = *s++;
+ switch (c)
+ {
+ case '\0':
+ continue;
+
+ 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;
+ }
+ }
+
+ /* Terminate our escape-processed copy. */
+ *f++ = '\0';
+
+ /* Whether the format string ended with double-quote or zero, we're
+ done with it; it's up to callers to complain about syntax. */
+ *arg = s;
+
+ /* Need extra space for the '\0's. Doubling the size is sufficient. */
+
+ current_substring = xmalloc (strlen (string) * 2 + 1000);
+
+ max_pieces = strlen (string) + 2;
+
+ pieces = (struct format_piece *)
+ xmalloc (max_pieces * sizeof (struct format_piece));
+
+ next_frag = 0;
+
+ /* Now scan the string for %-specs and see what kinds of args they want.
+ argclass classifies the %-specs so we can give printf-type functions
+ something of the right size. */
+
+ f = string;
+ prev_start = string;
+ while (*f)
+ if (*f++ == '%')
+ {
+ int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0;
+ int seen_space = 0, seen_plus = 0;
+ int seen_big_l = 0, seen_h = 0, seen_big_h = 0;
+ int seen_big_d = 0, seen_double_big_d = 0;
+ int bad = 0;
+
+ /* Skip over "%%", it will become part of a literal piece. */
+ if (*f == '%')
+ {
+ f++;
+ continue;
+ }
+
+ sub_start = current_substring;
+
+ strncpy (current_substring, prev_start, f - 1 - prev_start);
+ current_substring += f - 1 - prev_start;
+ *current_substring++ = '\0';
+
+ pieces[next_frag].string = sub_start;
+ pieces[next_frag].argclass = literal_piece;
+ next_frag++;
+
+ percent_loc = f - 1;
+
+ /* Check the validity of the format specifier, and work
+ out what argument it expects. We only accept C89
+ format strings, with the exception of long long (which
+ we autoconf for). */
+
+ /* The first part of a format specifier is a set of flag
+ characters. */
+ while (strchr ("0-+ #", *f))
+ {
+ if (*f == '#')
+ seen_hash = 1;
+ else if (*f == '0')
+ seen_zero = 1;
+ else if (*f == ' ')
+ seen_space = 1;
+ else if (*f == '+')
+ seen_plus = 1;
+ f++;
+ }
+
+ /* The next part of a format specifier is a width. */
+ while (strchr ("0123456789", *f))
+ f++;
+
+ /* The next part of a format specifier is a precision. */
+ if (*f == '.')
+ {
+ seen_prec = 1;
+ f++;
+ while (strchr ("0123456789", *f))
+ f++;
+ }
+
+ /* The next part of a format specifier is a length modifier. */
+ if (*f == 'h')
+ {
+ seen_h = 1;
+ f++;
+ }
+ else if (*f == 'l')
+ {
+ f++;
+ lcount++;
+ if (*f == 'l')
+ {
+ f++;
+ lcount++;
+ }
+ }
+ else if (*f == 'L')
+ {
+ seen_big_l = 1;
+ f++;
+ }
+ /* Decimal32 modifier. */
+ else if (*f == 'H')
+ {
+ seen_big_h = 1;
+ f++;
+ }
+ /* Decimal64 and Decimal128 modifiers. */
+ else if (*f == 'D')
+ {
+ f++;
+
+ /* Check for a Decimal128. */
+ if (*f == 'D')
+ {
+ f++;
+ seen_double_big_d = 1;
+ }
+ else
+ seen_big_d = 1;
+ }
+
+ switch (*f)
+ {
+ case 'u':
+ if (seen_hash)
+ bad = 1;
+ /* FALLTHROUGH */
+
+ case 'o':
+ case 'x':
+ case 'X':
+ if (seen_space || seen_plus)
+ bad = 1;
+ /* FALLTHROUGH */
+
+ case 'd':
+ case 'i':
+ if (lcount == 0)
+ this_argclass = int_arg;
+ else if (lcount == 1)
+ this_argclass = long_arg;
+ else
+ this_argclass = long_long_arg;
+
+ if (seen_big_l)
+ bad = 1;
+ break;
+
+ case 'c':
+ this_argclass = lcount == 0 ? int_arg : wide_char_arg;
+ if (lcount > 1 || seen_h || seen_big_l)
+ bad = 1;
+ if (seen_prec || seen_zero || seen_space || seen_plus)
+ bad = 1;
+ break;
+
+ case 'p':
+ this_argclass = ptr_arg;
+ if (lcount || seen_h || seen_big_l)
+ bad = 1;
+ if (seen_prec || seen_zero || seen_space || seen_plus)
+ bad = 1;
+ break;
+
+ case 's':
+ this_argclass = lcount == 0 ? string_arg : wide_string_arg;
+ if (lcount > 1 || seen_h || seen_big_l)
+ bad = 1;
+ if (seen_zero || seen_space || seen_plus)
+ bad = 1;
+ break;
+
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'E':
+ case 'G':
+ if (seen_big_h || seen_big_d || seen_double_big_d)
+ this_argclass = decfloat_arg;
+ else if (seen_big_l)
+ this_argclass = long_double_arg;
+ else
+ this_argclass = double_arg;
+
+ if (lcount || seen_h)
+ bad = 1;
+ break;
+
+ case '*':
+ error (_("`*' not supported for precision or width in printf"));
+
+ case 'n':
+ error (_("Format specifier `n' not supported in printf"));
+
+ case '\0':
+ error (_("Incomplete format specifier at end of format string"));
+
+ default:
+ error (_("Unrecognized format specifier '%c' in printf"), *f);
+ }
+
+ if (bad)
+ error (_("Inappropriate modifiers to "
+ "format specifier '%c' in printf"),
+ *f);
+
+ f++;
+
+ sub_start = current_substring;
+
+ if (lcount > 1 && USE_PRINTF_I64)
+ {
+ /* Windows' printf does support long long, but not the usual way.
+ Convert %lld to %I64d. */
+ int length_before_ll = f - percent_loc - 1 - lcount;
+
+ strncpy (current_substring, percent_loc, length_before_ll);
+ strcpy (current_substring + length_before_ll, "I64");
+ current_substring[length_before_ll + 3] =
+ percent_loc[length_before_ll + lcount];
+ current_substring += length_before_ll + 4;
+ }
+ else if (this_argclass == wide_string_arg
+ || this_argclass == wide_char_arg)
+ {
+ /* Convert %ls or %lc to %s. */
+ int length_before_ls = f - percent_loc - 2;
+
+ strncpy (current_substring, percent_loc, length_before_ls);
+ strcpy (current_substring + length_before_ls, "s");
+ current_substring += length_before_ls + 2;
+ }
+ else
+ {
+ strncpy (current_substring, percent_loc, f - percent_loc);
+ current_substring += f - percent_loc;
+ }
+
+ *current_substring++ = '\0';
+
+ prev_start = f;
+
+ pieces[next_frag].string = sub_start;
+ pieces[next_frag].argclass = this_argclass;
+ next_frag++;
+ }
+
+ /* Record the remainder of the string. */
+
+ sub_start = current_substring;
+
+ strncpy (current_substring, prev_start, f - prev_start);
+ current_substring += f - prev_start;
+ *current_substring++ = '\0';
+
+ pieces[next_frag].string = sub_start;
+ pieces[next_frag].argclass = literal_piece;
+ next_frag++;
+
+ /* Record an end-of-array marker. */
+
+ pieces[next_frag].string = NULL;
+ pieces[next_frag].argclass = literal_piece;
+
+ return pieces;
+}
+
+void
+free_format_pieces (struct format_piece *pieces)
+{
+ if (!pieces)
+ return;
+
+ /* We happen to know that all the string pieces are in the block
+ pointed to by the first string piece. */
+ if (pieces[0].string)
+ xfree (pieces[0].string);
+
+ xfree (pieces);
+}
+
+void
+free_format_pieces_cleanup (void *ptr)
+{
+ void **location = ptr;
+
+ if (location == NULL)
+ return;
+
+ if (*location != NULL)
+ {
+ free_format_pieces (*location);
+ *location = NULL;
+ }
+}
+
Index: common/format.h
===================================================================
RCS file: common/format.h
diff -N common/format.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ common/format.h 30 May 2012 00:20:59 -0000
@@ -0,0 +1,63 @@
+/* Parse a printf-style format string.
+
+ Copyright (C) 1986-2012 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG)
+# define USE_PRINTF_I64 1
+# define PRINTF_HAS_LONG_LONG
+#else
+# define USE_PRINTF_I64 0
+#endif
+
+/* The argclass represents the general type of data that goes with a
+ format directive; int_arg for %d, long_arg for %l, and so forth.
+ Note that these primarily distinguish types by size and need for
+ special handling, so for instance %u and %x are (at present) also
+ classed as int_arg. */
+
+enum argclass
+ {
+ literal_piece,
+ int_arg, long_arg, long_long_arg, ptr_arg,
+ string_arg, wide_string_arg, wide_char_arg,
+ double_arg, long_double_arg, decfloat_arg
+ };
+
+/* A format piece is a section of the format string that may include a
+ single print directive somewhere in it, and the associated class
+ for the argument. */
+
+struct format_piece
+{
+ char *string;
+ enum argclass argclass;
+};
+
+/* Return an array of printf fragments found at the given string, and
+ rewrite ARG with a pointer to the end of the format string. */
+
+extern struct format_piece *parse_format_string (char **arg);
+
+/* Given a pointer to an array of format pieces, free any memory that
+ would have been allocated by parse_format_string. */
+
+extern void free_format_pieces (struct format_piece *frags);
+
+/* Freeing, cast as a cleanup. */
+
+extern void free_format_pieces_cleanup (void *);
Index: testsuite/gdb.base/dprintf.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/dprintf.exp,v
retrieving revision 1.3
diff -u -p -r1.3 dprintf.exp
--- testsuite/gdb.base/dprintf.exp 15 May 2012 13:36:18 -0000 1.3
+++ testsuite/gdb.base/dprintf.exp 30 May 2012 00:20:59 -0000
@@ -85,6 +85,30 @@ if ![target_info exists gdb,noinferiorio
"2nd dprintf, fprintf"
}
+set target_can_dprintf 1
+set msg "Set dprintf style to agent"
+gdb_test_multiple "set dprintf-style agent" $msg {
+ -re "warning: Target cannot run dprintf commands.*" {
+ set target_can_dprintf 0
+ pass "$msg - cannot do"
+ }
+ -re ".*$gdb_prompt $" {
+ pass "$msg - can do"
+ }
+}
+
+if $target_can_dprintf {
+
+ gdb_run_cmd
+
+ gdb_test "" "Breakpoint"
+
+ gdb_test "continue" "At foo entry.*arg=1234, g=1234.*" "1st dprintf, agent"
+
+ gdb_test "continue" "At foo entry.*arg=1235, g=2222.*" "2nd dprintf, agent"
+
+}
+
gdb_test "set dprintf-style foobar" "Undefined item: \"foobar\"." \
"Set dprintf style to an unrecognized type"
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH] Dynamic printf for a target agent 2012-05-30 1:10 [PATCH] Dynamic printf for a target agent Stan Shebs @ 2012-05-31 6:02 ` Yao Qi 2012-06-25 15:31 ` Stan Shebs 0 siblings, 1 reply; 13+ messages in thread From: Yao Qi @ 2012-05-31 6:02 UTC (permalink / raw) To: Stan Shebs; +Cc: gdb-patches On 05/30/2012 09:09 AM, Stan Shebs wrote: > Index: breakpoint.c > =================================================================== > RCS file: /cvs/src/src/gdb/breakpoint.c,v > retrieving revision 1.677 > diff -u -p -r1.677 breakpoint.c > --- breakpoint.c 24 May 2012 16:51:34 -0000 1.677 > +++ breakpoint.c 30 May 2012 00:20:56 -0000 > > +/* 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. */ > + > +static struct agent_expr * > +parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd) > +{ > + struct cleanup *old_cleanups = 0; > + struct expression *expr, **argvec; > + struct agent_expr *aexpr = NULL; > + struct cleanup *old_chain = NULL; > + volatile struct gdb_exception ex; > + char *cmdrest; > + char *format_start, *format_end; > + struct format_piece *fpieces; > + int nargs; > + struct gdbarch *gdbarch = get_current_arch (); > + > + if (!cmd) > + return NULL; > + > + cmdrest = cmd; > + > + if (*cmdrest == ',') > + ++cmdrest; > + cmdrest = skip_spaces (cmdrest); > + > + if (*cmdrest++ != '"') > + error (_("No format string following the location")); > + > + format_start = cmdrest; > + > + fpieces = parse_format_string (&cmdrest); > + > + old_cleanups = make_cleanup (free_format_pieces_cleanup, &fpieces); > + > + format_end = cmdrest; > + > + if (*cmdrest++ != '"') > + error (_("Bad format string, non-terminated '\"'.")); > + > + cmdrest = skip_spaces (cmdrest); > + > + if (!(*cmdrest == ',' || *cmdrest == '\0')) > + error (_("Invalid argument syntax")); > + > + if (*cmdrest == ',') > + cmdrest++; > + cmdrest = skip_spaces (cmdrest); > + > + /* For each argument, make an expression. */ > + > + argvec = (struct expression **) alloca (strlen (cmd) > + * sizeof (struct expression *)); > + > + nargs = 0; > + while (*cmdrest != '\0') > + { > + char *cmd1; > + > + cmd1 = cmdrest; > + expr = parse_exp_1 (&cmd1, (struct block *) 0, 1); We need an instance of block, like expr = parse_exp_1 (&cmd1, block_for_pc (scope), 1); otherwise, we'll see some fails when running dprintf.exp with gdbserver, because of the warning below, "No symbol "arg" in specified context." > + 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, gdbarch, 0, 0, > + format_start, format_end - format_start, > + fpieces, nargs, argvec); > + } > + > + if (ex.reason < 0) > + { > + /* If we got here, it means the command 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 other commands. */ > + return NULL; > + } > + > + do_cleanups (old_cleanups); > + > + /* We have a valid agent expression, return it. */ > + return aexpr; > +} > + > @@ -2001,15 +1995,15 @@ print_variable_and_value (const char *na > static void > ui_printf (char *arg, struct ui_file *stream) > { > + struct format_piece *fpieces; > char *f = NULL; > char *s = arg; > char *string = NULL; local variables 'f' and 'string' can be removed. > struct value **val_args; > - char *substrings; > - char *current_substring; > int nargs = 0; local variable 'nargs' can be defined in ... > int allocated_args = 20; > struct cleanup *old_cleanups; > + int nargs_wanted; local variable 'nargs_wanted' can be defined in .... > > val_args = xmalloc (allocated_args * sizeof (struct value *)); > old_cleanups = make_cleanup (free_current_contents, &val_args); > @@ -2023,64 +2017,13 @@ ui_printf (char *arg, struct ui_file *st > 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 '\"'.")); > + fpieces = parse_format_string (&s); > > - 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; > + make_cleanup (free_format_pieces_cleanup, &fpieces); > > - default: > - *f++ = c; > - } > - } > - > - /* Skip over " and following space and comma. */ > - s++; > - *f++ = '\0'; > + if (*s++ != '"') > + error (_("Bad format string, non-terminated '\"'.")); > + > s = skip_spaces (s); > > if (*s != ',' && *s != 0) > @@ -2090,240 +2033,14 @@ ui_printf (char *arg, struct ui_file *st > s++; > s = skip_spaces (s); > > - /* Need extra space for the '\0's. Doubling the size is sufficient. */ > - substrings = alloca (strlen (string) * 2); > - current_substring = substrings; > - > { > - /* Now scan the string for %-specs and see what kinds of args they want. > - argclass[I] classifies the %-specs so we can give printf_filtered > - something of the right size. */ > - > - enum argclass > - { > - int_arg, long_arg, long_long_arg, ptr_arg, > - string_arg, wide_string_arg, wide_char_arg, > - double_arg, long_double_arg, decfloat_arg > - }; > - enum argclass *argclass; > - enum argclass this_argclass; > - char *last_arg; > - int nargs_wanted; > - int i; > + int i, fr; > + char *current_substring; ... this block. > > - argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass); > nargs_wanted = 0; > - f = string; > - last_arg = string; > - while (*f) > - if (*f++ == '%') > - { > - int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; > - int seen_space = 0, seen_plus = 0; > - int seen_big_l = 0, seen_h = 0, seen_big_h = 0; > - int seen_big_d = 0, seen_double_big_d = 0; > - int bad = 0; > - > - /* Check the validity of the format specifier, and work > - out what argument it expects. We only accept C89 > - format strings, with the exception of long long (which > - we autoconf for). */ > - > - /* Skip over "%%". */ > - if (*f == '%') > - { > - f++; > - continue; > - } > - > - /* The first part of a format specifier is a set of flag > - characters. */ > - while (strchr ("0-+ #", *f)) > - { > - if (*f == '#') > - seen_hash = 1; > - else if (*f == '0') > - seen_zero = 1; > - else if (*f == ' ') > - seen_space = 1; > - else if (*f == '+') > - seen_plus = 1; > - f++; > - } > - > - /* The next part of a format specifier is a width. */ > - while (strchr ("0123456789", *f)) > - f++; > - > - /* The next part of a format specifier is a precision. */ > - if (*f == '.') > - { > - seen_prec = 1; > - f++; > - while (strchr ("0123456789", *f)) > - f++; > - } > - > - /* The next part of a format specifier is a length modifier. */ > - if (*f == 'h') > - { > - seen_h = 1; > - f++; > - } > - else if (*f == 'l') > - { > - f++; > - lcount++; > - if (*f == 'l') > - { > - f++; > - lcount++; > - } > - } > - else if (*f == 'L') > - { > - seen_big_l = 1; > - f++; > - } > - /* Decimal32 modifier. */ > - else if (*f == 'H') > - { > - seen_big_h = 1; > - f++; > - } > - /* Decimal64 and Decimal128 modifiers. */ > - else if (*f == 'D') > - { > - f++; > - > - /* Check for a Decimal128. */ > - if (*f == 'D') > - { > - f++; > - seen_double_big_d = 1; > - } > - else > - seen_big_d = 1; > - } > - > - switch (*f) > - { > - case 'u': > - if (seen_hash) > - bad = 1; > - /* FALLTHROUGH */ > - > - case 'o': > - case 'x': > - case 'X': > - if (seen_space || seen_plus) > - bad = 1; > - /* FALLTHROUGH */ > - > - case 'd': > - case 'i': > - if (lcount == 0) > - this_argclass = int_arg; > - else if (lcount == 1) > - this_argclass = long_arg; > - else > - this_argclass = long_long_arg; > - > - if (seen_big_l) > - bad = 1; > - break; > - > - case 'c': > - this_argclass = lcount == 0 ? int_arg : wide_char_arg; > - if (lcount > 1 || seen_h || seen_big_l) > - bad = 1; > - if (seen_prec || seen_zero || seen_space || seen_plus) > - bad = 1; > - break; > - > - case 'p': > - this_argclass = ptr_arg; > - if (lcount || seen_h || seen_big_l) > - bad = 1; > - if (seen_prec || seen_zero || seen_space || seen_plus) > - bad = 1; > - break; > - > - case 's': > - this_argclass = lcount == 0 ? string_arg : wide_string_arg; > - if (lcount > 1 || seen_h || seen_big_l) > - bad = 1; > - if (seen_zero || seen_space || seen_plus) > - bad = 1; > - break; > - > - case 'e': > - case 'f': > - case 'g': > - case 'E': > - case 'G': > - if (seen_big_h || seen_big_d || seen_double_big_d) > - this_argclass = decfloat_arg; > - else if (seen_big_l) > - this_argclass = long_double_arg; > - else > - this_argclass = double_arg; > - > - if (lcount || seen_h) > - bad = 1; > - break; > - > - case '*': > - error (_("`*' not supported for precision or width in printf")); > - > - case 'n': > - error (_("Format specifier `n' not supported in printf")); > - > - case '\0': > - error (_("Incomplete format specifier at end of format string")); > - > - default: > - error (_("Unrecognized format specifier '%c' in printf"), *f); > - } > - > - if (bad) > - error (_("Inappropriate modifiers to " > - "format specifier '%c' in printf"), > - *f); > - > - f++; > - > - if (lcount > 1 && USE_PRINTF_I64) > - { > - /* Windows' printf does support long long, but not the usual way. > - Convert %lld to %I64d. */ > - int length_before_ll = f - last_arg - 1 - lcount; > - > - strncpy (current_substring, last_arg, length_before_ll); > - strcpy (current_substring + length_before_ll, "I64"); > - current_substring[length_before_ll + 3] = > - last_arg[length_before_ll + lcount]; > - current_substring += length_before_ll + 4; > - } > - else if (this_argclass == wide_string_arg > - || this_argclass == wide_char_arg) > - { > - /* Convert %ls or %lc to %s. */ > - int length_before_ls = f - last_arg - 2; > - > - strncpy (current_substring, last_arg, length_before_ls); > - strcpy (current_substring + length_before_ls, "s"); > - current_substring += length_before_ls + 2; > - } > - else > - { > - strncpy (current_substring, last_arg, f - last_arg); > - current_substring += f - last_arg; > - } > - *current_substring++ = '\0'; > - last_arg = f; > - argclass[nargs_wanted++] = this_argclass; > - } > + for (fr = 0; fpieces[fr].string != NULL; fr++) > + if (fpieces[fr].argclass != literal_piece) > + ++nargs_wanted; > > /* Now, parse all arguments and evaluate them. > Store the VALUEs in VAL_ARGS. */ > Index: remote.c > =================================================================== > RCS file: /cvs/src/src/gdb/remote.c,v > retrieving revision 1.499 > diff -u -p -r1.499 remote.c > --- remote.c 24 May 2012 16:51:35 -0000 1.499 > +++ remote.c 30 May 2012 00:20:56 -0000 > @@ -7884,6 +7934,9 @@ remote_insert_breakpoint (struct gdbarch > if (remote_supports_cond_breakpoints ()) > remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); > > + if (remote_can_run_breakpoint_commands ()) > + remote_add_target_side_commands (gdbarch, bp_tgt, p); > + If dprintf_style is "gdb", we shouldn't add target side commands, otherwise print is executed in target side, rather than GDB side. This causes two regressions in testing with gdbserver, FAIL: gdb.base/dprintf.exp: 1st dprintf, gdb FAIL: gdb.base/dprintf.exp: 2nd dprintf, gdb We only add target side commands only if target supports breakpoint commands, and dprintf_style is not "gdb". if (remote_can_run_breakpoint_commands () && strcmp (dprintf_style, "gdb") != 0) remote_add_target_side_commands (gdbarch, bp_tgt, p); > putpkt (rs->buf); > getpkt (&rs->buf, &rs->buf_size, 0); > > @@ -8125,6 +8178,9 @@ remote_insert_hw_breakpoint (struct gdba > if (remote_supports_cond_breakpoints ()) > remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); > > + 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); > > @@ -10063,6 +10119,14 @@ remote_supports_string_tracing (void) > return rs->string_tracing; > } > > Index: testsuite/gdb.base/dprintf.exp > =================================================================== > RCS file: /cvs/src/src/gdb/testsuite/gdb.base/dprintf.exp,v > retrieving revision 1.3 > diff -u -p -r1.3 dprintf.exp > --- testsuite/gdb.base/dprintf.exp 15 May 2012 13:36:18 -0000 1.3 > +++ testsuite/gdb.base/dprintf.exp 30 May 2012 00:20:59 -0000 > @@ -85,6 +85,30 @@ if ![target_info exists gdb,noinferiorio > "2nd dprintf, fprintf" > } > > +set target_can_dprintf 1 > +set msg "Set dprintf style to agent" > +gdb_test_multiple "set dprintf-style agent" $msg { > + -re "warning: Target cannot run dprintf commands.*" { > + set target_can_dprintf 0 > + pass "$msg - cannot do" > + } > + -re ".*$gdb_prompt $" { > + pass "$msg - can do" > + } > +} > + > +if $target_can_dprintf { > + > + gdb_run_cmd > + > + gdb_test "" "Breakpoint" > + > + gdb_test "continue" "At foo entry.*arg=1234, g=1234.*" "1st dprintf, agent" > + > + gdb_test "continue" "At foo entry.*arg=1235, g=2222.*" "2nd dprintf, agent" When we set dprintf style to agent, GDB can't receive such outputs, so these two tests above should fail. We may need more thoughts on testing output from agent. -- Yao (é½å°§) ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] Dynamic printf for a target agent 2012-05-31 6:02 ` Yao Qi @ 2012-06-25 15:31 ` Stan Shebs 2012-06-27 20:45 ` Tom Tromey 2012-07-02 16:41 ` Build regression on 64-bit hosts " Jan Kratochvil 0 siblings, 2 replies; 13+ messages in thread From: Stan Shebs @ 2012-06-25 15:31 UTC (permalink / raw) To: Yao Qi; +Cc: gdb-patches [-- Attachment #1: Type: text/plain, Size: 6792 bytes --] On 5/30/12 11:02 PM, Yao Qi wrote: Thanks for the comments! > On 05/30/2012 09:09 AM, Stan Shebs wrote: > > + cmd1 = cmdrest; > + expr = parse_exp_1 (&cmd1, (struct block *) 0, 1); > We need an instance of block, like > > expr = parse_exp_1 (&cmd1, block_for_pc (scope), 1); > > otherwise, we'll see some fails when running dprintf.exp with gdbserver, > because of the warning below, > > "No symbol "arg" in specified context." Good idea! > >> @@ -2001,15 +1995,15 @@ print_variable_and_value (const char *na >> static void >> ui_printf (char *arg, struct ui_file *stream) >> { >> + struct format_piece *fpieces; >> char *f = NULL; >> char *s = arg; >> char *string = NULL; > local variables 'f' and 'string' can be removed. Done, and the nargs variables are moved down. > >> Index: remote.c >> =================================================================== >> RCS file: /cvs/src/src/gdb/remote.c,v >> retrieving revision 1.499 >> diff -u -p -r1.499 remote.c >> --- remote.c 24 May 2012 16:51:35 -0000 1.499 >> +++ remote.c 30 May 2012 00:20:56 -0000 >> @@ -7884,6 +7934,9 @@ remote_insert_breakpoint (struct gdbarch >> if (remote_supports_cond_breakpoints ()) >> remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); >> >> + if (remote_can_run_breakpoint_commands ()) >> + remote_add_target_side_commands (gdbarch, bp_tgt, p); >> + > If dprintf_style is "gdb", we shouldn't add target side commands, > otherwise print is executed in target side, rather than GDB side. This > causes two regressions in testing with gdbserver, > > FAIL: gdb.base/dprintf.exp: 1st dprintf, gdb > FAIL: gdb.base/dprintf.exp: 2nd dprintf, gdb > > We only add target side commands only if target supports breakpoint > commands, and dprintf_style is not "gdb". > > if (remote_can_run_breakpoint_commands () > && strcmp (dprintf_style, "gdb") != 0) > remote_add_target_side_commands (gdbarch, bp_tgt, p); Ideally, remote.c shouldn't have to know about dprintf details, so I put an equivalent test at the top of build_target_command_list, which is what one would expect to modify when the time comes to generalize. (I tried to get a contract to do the generalization this year, but no luck - maybe next year. :-) ) > + > + gdb_test "continue" "At foo entry.*arg=1234, g=1234.*" "1st dprintf, agent" > + > + gdb_test "continue" "At foo entry.*arg=1235, g=2222.*" "2nd dprintf, agent" > When we set dprintf style to agent, GDB can't receive such outputs, so > these two tests above should fail. We may need more thoughts on testing > output from agent. > Yeah, that's a head-scratcher; I looked around and didn't see any good examples of something doing that already. It seems like it ought to be possible though, seeing as how the agent dprintf output does end up in testsuite/gdb.log. In the meantime, I tweaked the test to just expect the usual result from a continue. I'll commit the updated patch below later this week if there are no obstacles. Stan stan@codesourcery.com 2012-06-25 Stan Shebs <stan@codesourcery.com> Add target-side support for dynamic printf. * NEWS: Mention the additional style. * breakpoint.h (struct bp_target_info): New fields tcommands, persist. (struct bp_location): New field cmd_bytecode. * breakpoint.c: Include format.h. (disconnected_dprintf): New global. (parse_cmd_to_aexpr): New function. (build_target_command_list): New function. (insert_bp_location): Call it. (remove_breakpoints_pid): Skip dprintf breakpoints. (print_one_breakpoint_location): Ditto. (dprintf_style_agent): New global. (dprintf_style_enums): Add dprintf_style_agent. (update_dprintf_command_list): Add agent case. (agent_printf_command): New function. (_initialize_breakpoint): Add new commands. * common/ax.def (printf): New bytecode. * ax.h (ax_string): Declare. * ax-gdb.h (gen_printf): Declare. * ax-gdb.c: Include cli-utils.h, format.h. (gen_printf): New function. (maint_agent_print_command): New function. (_initialize_ax_gdb): Add maint agent-printf command. * ax-general.c (ax_string): New function. (ax_print): Add printf disassembly. * Makefile.in (SFILES): Add format.c (COMMON_OBS): Add format.o. * common/format.h: New file. * common/format.c: New file. * printcmd.c: Include format.h. (ui_printf): Call parse_format_string. * remote.c (remote_state): New field breakpoint_commands. (PACKET_BreakpointCommands): 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] * Makefile.in (WARN_CFLAGS_NO_FORMAT): Define. (ax.o): Add it to build rule. (ax-ipa.o): Ditto. (OBS): Add format.o. (IPA_OBS): Add format.o. * 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.h (any_persistent_commands): Declare. (add_breakpoint_commands): Declare. (gdb_no_commands_at_breakpoint): Declare. (run_breakpoint_commands): Declare. * 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. * linux-low.c (linux_wait_1): Test for and run breakpoint commands locally. * ax.c: Include format.h. (ax_printf): New function. (gdb_eval_agent_expr): Add printf opcode. [doc] * gdb.texinfo (Dynamic Printf): Mention agent style and disconnected dprintf. (Maintenance Commands): Describe maint agent-printf. (General Query Packets): Mention BreakpointCommands feature. (Packets): Document commands extension to Z0 packet. * agentexpr.texi (Bytecode Descriptions): Document printf bytecode. [testsuite] * gdb.base/dprintf.exp: Add agent style tests. [-- Attachment #2: dprintf-patch-5 --] [-- Type: text/plain, Size: 81630 bytes --] Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.528 diff -u -p -r1.528 NEWS --- NEWS 23 Jun 2012 22:23:46 -0000 1.528 +++ NEWS 25 Jun 2012 15:12:57 -0000 @@ -239,11 +239,12 @@ set debug auto-load on|off show debug auto-load Control display of debugging info for auto-loading the files above. -set dprintf-style gdb|call +set dprintf-style gdb|call|agent show dprintf-style - Control the way in which a dynamic printf is performed; "gdb" requests - a GDB printf command, while "call" causes dprintf to call a function - in the inferior. + Control the way in which a dynamic printf is performed; "gdb" + requests a GDB printf command, while "call" causes dprintf to call a + function in the inferior. "agent" requests that the target agent + (such as GDBserver) do the printing. set dprintf-function <expr> show dprintf-function @@ -252,6 +253,11 @@ show dprintf-channel Set the function and optional first argument to the call when using the "call" style of dynamic printf. +set disconnected-dprintf on|off +show disconnected-dprintf + Control whether agent-style dynamic printfs continue to be in effect + after GDB disconnects. + * New configure options --with-auto-load-dir Index: Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/Makefile.in,v retrieving revision 1.1209 diff -u -p -r1.1209 Makefile.in --- Makefile.in 30 May 2012 19:31:44 -0000 1.1209 +++ Makefile.in 25 Jun 2012 15:12:58 -0000 @@ -745,7 +745,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \ regset.c sol-thread.c windows-termcap.c \ common/common-utils.c common/xml-utils.c \ - common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c + common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \ + common/format.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -827,6 +828,7 @@ gnulib/import/extra/snippet/arg-nonnull. gnulib/import/extra/snippet/warn-on-use.h \ gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ +common/format.h \ common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h # Header files that already have srcdir in them, or which are in objdir. @@ -916,7 +918,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ inferior.o osdata.o gdb_usleep.o record.o gcore.o \ jit.o progspace.o skip.o probe.o \ - common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o + common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ + format.o TSOBS = inflow.o @@ -1916,6 +1919,10 @@ buffer.o: ${srcdir}/common/buffer.c $(COMPILE) $(srcdir)/common/buffer.c $(POSTCOMPILE) +format.o: ${srcdir}/common/format.c + $(COMPILE) $(srcdir)/common/format.c + $(POSTCOMPILE) + linux-osdata.o: ${srcdir}/common/linux-osdata.c $(COMPILE) $(srcdir)/common/linux-osdata.c $(POSTCOMPILE) Index: ax-gdb.c =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.c,v retrieving revision 1.101 diff -u -p -r1.101 ax-gdb.c --- ax-gdb.c 18 May 2012 21:02:47 -0000 1.101 +++ ax-gdb.c 25 Jun 2012 15:12:58 -0000 @@ -41,10 +41,13 @@ #include "tracepoint.h" #include "cp-support.h" #include "arch-utils.h" +#include "cli/cli-utils.h" #include "valprint.h" #include "c-lang.h" +#include "format.h" + /* To make sense of this file, you should read doc/agentexpr.texi. Then look at the types and enums in ax-gdb.h. For the code itself, look at gen_expr, towards the bottom; that's the main function that @@ -2503,6 +2506,59 @@ 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, struct gdbarch *gdbarch, + CORE_ADDR function, LONGEST channel, + char *format, int fmtlen, + struct format_piece *frags, + int nargs, struct expression **exprs) +{ + struct expression *expr; + struct cleanup *old_chain = 0; + struct agent_expr *ax = new_agent_expr (gdbarch, scope); + union exp_element *pc; + struct axs_value value; + int i, tem, bot, fr, flen; + char *fmt; + + 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); + } + + /* Push function and channel. */ + ax_const_l (ax, channel); + ax_const_l (ax, function); + + /* Issue the printf bytecode proper. */ + ax_simple (ax, aop_printf); + ax_simple (ax, nargs); + ax_string (ax, format, fmtlen); + + /* 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) { @@ -2586,6 +2642,88 @@ 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. */ + +static void +maint_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; + char *format_start, *format_end; + struct format_piece *fpieces; + 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; + + cmdrest = skip_spaces (cmdrest); + + if (*cmdrest++ != '"') + error (_("Must start with a format string.")); + + format_start = cmdrest; + + fpieces = parse_format_string (&cmdrest); + + old_chain = make_cleanup (free_format_pieces_cleanup, &fpieces); + + format_end = cmdrest; + + if (*cmdrest++ != '"') + error (_("Bad format string, non-terminated '\"'.")); + + cmdrest = skip_spaces (cmdrest); + + if (*cmdrest != ',' && *cmdrest != 0) + error (_("Invalid argument syntax")); + + if (*cmdrest == ',') + cmdrest++; + cmdrest = skip_spaces (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; + /* else complain? */ + } + + + agent = gen_printf (get_frame_pc (fi), get_current_arch (), 0, 0, + format_start, format_end - format_start, + fpieces, 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. */ + + do_cleanups (old_chain); + dont_repeat (); +} \f /* Initialization code. */ @@ -2603,4 +2741,9 @@ _initialize_ax_gdb (void) _("Translate an expression into remote " "agent bytecode for evaluation."), &maintenancelist); + + add_cmd ("agent-printf", class_maintenance, maint_agent_printf_command, + _("Translate an expression into remote " + "agent bytecode for evaluation and display the bytecodes."), + &maintenancelist); } Index: ax-gdb.h =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.h,v retrieving revision 1.24 diff -u -p -r1.24 ax-gdb.h --- ax-gdb.h 27 Apr 2012 20:47:51 -0000 1.24 +++ ax-gdb.h 25 Jun 2012 15:12:58 -0000 @@ -115,6 +115,12 @@ extern void gen_expr (struct expression extern void require_rvalue (struct agent_expr *ax, struct axs_value *value); +struct format_piece; +extern struct agent_expr *gen_printf (CORE_ADDR, struct gdbarch *, + CORE_ADDR, LONGEST, char *, int, + struct format_piece *, + 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 25 Jun 2012 15:12:58 -0000 @@ -330,6 +330,23 @@ ax_tsv (struct agent_expr *x, enum agent x->buf[x->len + 2] = (num) & 0xff; x->len += 3; } + +/* Append a string to the expression. Note that the string is going + into the bytecodes directly, not on the stack. As a precaution, + include both length as prefix, and terminate with a NUL. */ + +void +ax_string (struct agent_expr *x, char *str, int slen) +{ + int i; + + grow_expr (x, slen + 3); + x->buf[x->len++] = ((slen + 1) >> 8) & 0xff; + x->buf[x->len++] = (slen + 1) & 0xff; + for (i = 0; i < slen; ++i) + x->buf[x->len++] = str[i]; + x->buf[x->len++] = '\0'; +} \f @@ -391,6 +408,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 slen, nargs; + + ++i; + nargs = x->buf[i++]; + slen = x->buf[i++]; + slen = slen * 256 + x->buf[i++]; + fprintf_filtered (f, " \"%s\", %d args", + &(x->buf[i]), nargs); + i += slen - 1; + } fprintf_filtered (f, "\n"); i += 1 + aop_map[op].op_size; Index: ax.h =================================================================== RCS file: /cvs/src/src/gdb/ax.h,v retrieving revision 1.21 diff -u -p -r1.21 ax.h --- ax.h 24 Feb 2012 15:10:58 -0000 1.21 +++ ax.h 25 Jun 2012 15:12:58 -0000 @@ -219,6 +219,9 @@ extern void ax_reg_mask (struct agent_ex /* Assemble code to operate on a trace state variable. */ extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num); + +/* Append a string to the bytecode stream. */ +extern void ax_string (struct agent_expr *x, char *str, int slen); \f /* Functions for printing out expressions, and otherwise debugging Index: breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.684 diff -u -p -r1.684 breakpoint.c --- breakpoint.c 18 Jun 2012 17:28:36 -0000 1.684 +++ breakpoint.c 25 Jun 2012 15:12:58 -0000 @@ -70,6 +70,8 @@ #include "ax-gdb.h" #include "dummy-frame.h" +#include "format.h" + /* readline include files */ #include "readline/readline.h" #include "readline/history.h" @@ -301,6 +303,45 @@ static struct breakpoint_ops bkpt_probe_ /* Dynamic printf class type. */ static struct breakpoint_ops dprintf_breakpoint_ops; +/* The style in which to perform a dynamic printf. This is a user + option because different output options have different tradeoffs; + if GDB does the printing, there is better error handling if there + is a problem with any of the arguments, but using an inferior + function lets you have special-purpose printers and sending of + output to the same place as compiled-in print functions. */ + +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; + +/* The function to use for dynamic printf if the preferred style is to + call into the inferior. The value is simply a string that is + copied into the command, so it can be anything that GDB can + evaluate to a callable address, not necessarily a function name. */ + +static char *dprintf_function = ""; + +/* The channel to use for dynamic printf if the preferred style is to + call into the inferior; if a nonempty string, it will be passed to + the call as the first argument, with the format string as the + second. As with the dprintf function, this can be anything that + GDB knows how to evaluate, so in addition to common choices like + "stderr", this could be an app-specific expression like + "mystreams[curlogger]". */ + +static char *dprintf_channel = ""; + +/* 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 @@ -2139,6 +2180,196 @@ build_target_condition_list (struct bp_l return; } +/* 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. */ + +static struct agent_expr * +parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd) +{ + struct cleanup *old_cleanups = 0; + struct expression *expr, **argvec; + struct agent_expr *aexpr = NULL; + struct cleanup *old_chain = NULL; + volatile struct gdb_exception ex; + char *cmdrest; + char *format_start, *format_end; + struct format_piece *fpieces; + int nargs; + struct gdbarch *gdbarch = get_current_arch (); + + if (!cmd) + return NULL; + + cmdrest = cmd; + + if (*cmdrest == ',') + ++cmdrest; + cmdrest = skip_spaces (cmdrest); + + if (*cmdrest++ != '"') + error (_("No format string following the location")); + + format_start = cmdrest; + + fpieces = parse_format_string (&cmdrest); + + old_cleanups = make_cleanup (free_format_pieces_cleanup, &fpieces); + + format_end = cmdrest; + + if (*cmdrest++ != '"') + error (_("Bad format string, non-terminated '\"'.")); + + cmdrest = skip_spaces (cmdrest); + + if (!(*cmdrest == ',' || *cmdrest == '\0')) + error (_("Invalid argument syntax")); + + if (*cmdrest == ',') + cmdrest++; + cmdrest = skip_spaces (cmdrest); + + /* For each argument, make an expression. */ + + argvec = (struct expression **) alloca (strlen (cmd) + * sizeof (struct expression *)); + + nargs = 0; + while (*cmdrest != '\0') + { + char *cmd1; + + cmd1 = cmdrest; + expr = parse_exp_1 (&cmd1, block_for_pc (scope), 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, gdbarch, 0, 0, + format_start, format_end - format_start, + fpieces, nargs, argvec); + } + + if (ex.reason < 0) + { + /* If we got here, it means the command 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 other commands. */ + return NULL; + } + + do_cleanups (old_cleanups); + + /* We have a valid agent expression, return it. */ + 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 agent-style dprintf breakpoints. */ + if (bl->owner->type != bp_dprintf + || strcmp (dprintf_style, dprintf_style_agent) != 0) + 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 commands since something changed. In that + case we already freed the command bytecodes (see + force_breakpoint_reinsertion). We just + need to parse the command 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 command 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 command 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 commands to the target. */ + VEC_safe_push (agent_expr_p, bl->target_info.tcommands, + 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. @@ -2179,7 +2410,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; } @@ -2698,6 +2930,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); @@ -5834,6 +6069,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) { @@ -8532,40 +8776,6 @@ bp_loc_is_permanent (struct bp_location return retval; } -/* The style in which to perform a dynamic printf. This is a user - option because different output options have different tradeoffs; - if GDB does the printing, there is better error handling if there - is a problem with any of the arguments, but using an inferior - function lets you have special-purpose printers and sending of - output to the same place as compiled-in print functions. (Future - styles may include the ability to do a target-side printf.) */ - -static const char dprintf_style_gdb[] = "gdb"; -static const char dprintf_style_call[] = "call"; -static const char *const dprintf_style_enums[] = { - dprintf_style_gdb, - dprintf_style_call, - NULL -}; -static const char *dprintf_style = dprintf_style_gdb; - -/* The function to use for dynamic printf if the preferred style is to - call into the inferior. The value is simply a string that is - copied into the command, so it can be anything that GDB can - evaluate to a callable address, not necessarily a function name. */ - -static char *dprintf_function = ""; - -/* The channel to use for dynamic printf if the preferred style is to - call into the inferior; if a nonempty string, it will be passed to - the call as the first argument, with the format string as the - second. As with the dprintf function, this can be anything that - GDB knows how to evaluate, so in addition to common choices like - "stderr", this could be an app-specific expression like - "mystreams[curlogger]". */ - -static char *dprintf_channel = ""; - /* Build a command list for the dprintf corresponding to the current settings of the dprintf style options. */ @@ -8589,9 +8799,9 @@ update_dprintf_command_list (struct brea if (*dprintf_args != '"') error (_("Bad format string, missing '\"'.")); - if (strcmp (dprintf_style, "gdb") == 0) + if (strcmp (dprintf_style, dprintf_style_gdb) == 0) printf_line = xstrprintf ("printf %s", dprintf_args); - else if (strcmp (dprintf_style, "call") == 0) + else if (strcmp (dprintf_style, dprintf_style_call) == 0) { if (!dprintf_function) error (_("No function supplied for dprintf call")); @@ -8606,6 +8816,16 @@ update_dprintf_command_list (struct brea dprintf_function, dprintf_args); } + else if (strcmp (dprintf_style, dprintf_style_agent) == 0) + { + if (target_can_run_breakpoint_commands ()) + printf_line = xstrprintf ("agent-printf %s", dprintf_args); + else + { + warning (_("Target cannot run dprintf commands, falling back to GDB printf")); + printf_line = xstrprintf ("printf %s", dprintf_args); + } + } else internal_error (__FILE__, __LINE__, _("Invalid dprintf style.")); @@ -8615,12 +8835,15 @@ update_dprintf_command_list (struct brea { struct command_line *printf_cmd_line, *cont_cmd_line = NULL; - cont_cmd_line = xmalloc (sizeof (struct command_line)); - cont_cmd_line->control_type = simple_control; - cont_cmd_line->body_count = 0; - cont_cmd_line->body_list = NULL; - cont_cmd_line->next = NULL; - cont_cmd_line->line = xstrdup ("continue"); + if (strcmp (dprintf_style, dprintf_style_agent) != 0) + { + cont_cmd_line = xmalloc (sizeof (struct command_line)); + cont_cmd_line->control_type = simple_control; + cont_cmd_line->body_count = 0; + cont_cmd_line->body_list = NULL; + cont_cmd_line->next = NULL; + cont_cmd_line->line = xstrdup ("continue"); + } printf_cmd_line = xmalloc (sizeof (struct command_line)); printf_cmd_line->control_type = simple_control; @@ -9576,6 +9799,12 @@ dprintf_command (char *arg, int from_tty 0); } +static void +agent_printf_command (char *arg, int from_tty) +{ + error (_("May only run agent-printf on the target")); +} + /* Implement the "breakpoint_hit" breakpoint_ops method for ranged breakpoints. */ @@ -16266,6 +16495,20 @@ Show the channel to use for dynamic prin update_dprintf_commands, 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); + + add_com ("agent-printf", class_vars, agent_printf_command, _("\ +agent-printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\ +(target agent only) This is useful for formatted output in user-defined commands.")); + 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.185 diff -u -p -r1.185 breakpoint.h --- breakpoint.h 18 Jun 2012 17:28:36 -0000 1.185 +++ breakpoint.h 25 Jun 2012 15:12:58 -0000 @@ -272,6 +272,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) *tcommands; + + /* 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 @@ -358,8 +366,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 Index: printcmd.c =================================================================== RCS file: /cvs/src/src/gdb/printcmd.c,v retrieving revision 1.208 diff -u -p -r1.208 printcmd.c --- printcmd.c 18 May 2012 21:02:50 -0000 1.208 +++ printcmd.c 25 Jun 2012 15:12:58 -0000 @@ -49,18 +49,12 @@ #include "charset.h" #include "arch-utils.h" #include "cli/cli-utils.h" +#include "format.h" #ifdef TUI #include "tui/tui.h" /* For tui_active et al. */ #endif -#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) -# define USE_PRINTF_I64 1 -# define PRINTF_HAS_LONG_LONG -#else -# define USE_PRINTF_I64 0 -#endif - struct format_data { int count; @@ -2001,13 +1995,9 @@ print_variable_and_value (const char *na static void ui_printf (char *arg, struct ui_file *stream) { - char *f = NULL; + struct format_piece *fpieces; char *s = arg; - char *string = NULL; struct value **val_args; - char *substrings; - char *current_substring; - int nargs = 0; int allocated_args = 20; struct cleanup *old_cleanups; @@ -2023,64 +2013,13 @@ ui_printf (char *arg, struct ui_file *st 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 '\"'.")); + fpieces = parse_format_string (&s); - 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; + make_cleanup (free_format_pieces_cleanup, &fpieces); - default: - *f++ = c; - } - } - - /* Skip over " and following space and comma. */ - s++; - *f++ = '\0'; + if (*s++ != '"') + error (_("Bad format string, non-terminated '\"'.")); + s = skip_spaces (s); if (*s != ',' && *s != 0) @@ -2090,240 +2029,16 @@ ui_printf (char *arg, struct ui_file *st s++; s = skip_spaces (s); - /* Need extra space for the '\0's. Doubling the size is sufficient. */ - substrings = alloca (strlen (string) * 2); - current_substring = substrings; - { - /* Now scan the string for %-specs and see what kinds of args they want. - argclass[I] classifies the %-specs so we can give printf_filtered - something of the right size. */ - - enum argclass - { - int_arg, long_arg, long_long_arg, ptr_arg, - string_arg, wide_string_arg, wide_char_arg, - double_arg, long_double_arg, decfloat_arg - }; - enum argclass *argclass; - enum argclass this_argclass; - char *last_arg; + int nargs = 0; int nargs_wanted; - int i; + int i, fr; + char *current_substring; - argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass); nargs_wanted = 0; - f = string; - last_arg = string; - while (*f) - if (*f++ == '%') - { - int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; - int seen_space = 0, seen_plus = 0; - int seen_big_l = 0, seen_h = 0, seen_big_h = 0; - int seen_big_d = 0, seen_double_big_d = 0; - int bad = 0; - - /* Check the validity of the format specifier, and work - out what argument it expects. We only accept C89 - format strings, with the exception of long long (which - we autoconf for). */ - - /* Skip over "%%". */ - if (*f == '%') - { - f++; - continue; - } - - /* The first part of a format specifier is a set of flag - characters. */ - while (strchr ("0-+ #", *f)) - { - if (*f == '#') - seen_hash = 1; - else if (*f == '0') - seen_zero = 1; - else if (*f == ' ') - seen_space = 1; - else if (*f == '+') - seen_plus = 1; - f++; - } - - /* The next part of a format specifier is a width. */ - while (strchr ("0123456789", *f)) - f++; - - /* The next part of a format specifier is a precision. */ - if (*f == '.') - { - seen_prec = 1; - f++; - while (strchr ("0123456789", *f)) - f++; - } - - /* The next part of a format specifier is a length modifier. */ - if (*f == 'h') - { - seen_h = 1; - f++; - } - else if (*f == 'l') - { - f++; - lcount++; - if (*f == 'l') - { - f++; - lcount++; - } - } - else if (*f == 'L') - { - seen_big_l = 1; - f++; - } - /* Decimal32 modifier. */ - else if (*f == 'H') - { - seen_big_h = 1; - f++; - } - /* Decimal64 and Decimal128 modifiers. */ - else if (*f == 'D') - { - f++; - - /* Check for a Decimal128. */ - if (*f == 'D') - { - f++; - seen_double_big_d = 1; - } - else - seen_big_d = 1; - } - - switch (*f) - { - case 'u': - if (seen_hash) - bad = 1; - /* FALLTHROUGH */ - - case 'o': - case 'x': - case 'X': - if (seen_space || seen_plus) - bad = 1; - /* FALLTHROUGH */ - - case 'd': - case 'i': - if (lcount == 0) - this_argclass = int_arg; - else if (lcount == 1) - this_argclass = long_arg; - else - this_argclass = long_long_arg; - - if (seen_big_l) - bad = 1; - break; - - case 'c': - this_argclass = lcount == 0 ? int_arg : wide_char_arg; - if (lcount > 1 || seen_h || seen_big_l) - bad = 1; - if (seen_prec || seen_zero || seen_space || seen_plus) - bad = 1; - break; - - case 'p': - this_argclass = ptr_arg; - if (lcount || seen_h || seen_big_l) - bad = 1; - if (seen_prec || seen_zero || seen_space || seen_plus) - bad = 1; - break; - - case 's': - this_argclass = lcount == 0 ? string_arg : wide_string_arg; - if (lcount > 1 || seen_h || seen_big_l) - bad = 1; - if (seen_zero || seen_space || seen_plus) - bad = 1; - break; - - case 'e': - case 'f': - case 'g': - case 'E': - case 'G': - if (seen_big_h || seen_big_d || seen_double_big_d) - this_argclass = decfloat_arg; - else if (seen_big_l) - this_argclass = long_double_arg; - else - this_argclass = double_arg; - - if (lcount || seen_h) - bad = 1; - break; - - case '*': - error (_("`*' not supported for precision or width in printf")); - - case 'n': - error (_("Format specifier `n' not supported in printf")); - - case '\0': - error (_("Incomplete format specifier at end of format string")); - - default: - error (_("Unrecognized format specifier '%c' in printf"), *f); - } - - if (bad) - error (_("Inappropriate modifiers to " - "format specifier '%c' in printf"), - *f); - - f++; - - if (lcount > 1 && USE_PRINTF_I64) - { - /* Windows' printf does support long long, but not the usual way. - Convert %lld to %I64d. */ - int length_before_ll = f - last_arg - 1 - lcount; - - strncpy (current_substring, last_arg, length_before_ll); - strcpy (current_substring + length_before_ll, "I64"); - current_substring[length_before_ll + 3] = - last_arg[length_before_ll + lcount]; - current_substring += length_before_ll + 4; - } - else if (this_argclass == wide_string_arg - || this_argclass == wide_char_arg) - { - /* Convert %ls or %lc to %s. */ - int length_before_ls = f - last_arg - 2; - - strncpy (current_substring, last_arg, length_before_ls); - strcpy (current_substring + length_before_ls, "s"); - current_substring += length_before_ls + 2; - } - else - { - strncpy (current_substring, last_arg, f - last_arg); - current_substring += f - last_arg; - } - *current_substring++ = '\0'; - last_arg = f; - argclass[nargs_wanted++] = this_argclass; - } + for (fr = 0; fpieces[fr].string != NULL; fr++) + if (fpieces[fr].argclass != literal_piece) + ++nargs_wanted; /* Now, parse all arguments and evaluate them. Store the VALUEs in VAL_ARGS. */ @@ -2349,10 +2064,11 @@ ui_printf (char *arg, struct ui_file *st error (_("Wrong number of arguments for specified format-string")); /* Now actually print them. */ - current_substring = substrings; - for (i = 0; i < nargs; i++) + i = 0; + for (fr = 0; fpieces[fr].string != NULL; fr++) { - switch (argclass[i]) + current_substring = fpieces[fr].string; + switch (fpieces[fr].argclass) { case string_arg: { @@ -2687,20 +2403,25 @@ ui_printf (char *arg, struct ui_file *st break; } + case literal_piece: + /* Print a portion of the format string that has no + directives. Note that this will not include any + ordinary %-specs, but it might include "%%". That is + why we use printf_filtered and not puts_filtered here. + Also, we pass a dummy argument because some platforms + have modified GCC to include -Wformat-security by + default, which will warn here if there is no + argument. */ + fprintf_filtered (stream, current_substring, 0); + break; default: internal_error (__FILE__, __LINE__, _("failed internal consistency check")); } - /* Skip to the next substring. */ - current_substring += strlen (current_substring) + 1; + /* Maybe advance to the next argument. */ + if (fpieces[fr].argclass != literal_piece) + ++i; } - /* Print the portion of the format string after the last argument. - Note that this will not include any ordinary %-specs, but it - might include "%%". That is why we use printf_filtered and not - puts_filtered here. Also, we pass a dummy argument because - some platforms have modified GCC to include -Wformat-security - by default, which will warn here if there is no argument. */ - fprintf_filtered (stream, last_arg, 0); } do_cleanups (old_cleanups); } Index: remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.500 diff -u -p -r1.500 remote.c --- remote.c 5 Jun 2012 21:22:31 -0000 1.500 +++ remote.c 25 Jun 2012 15:12:58 -0000 @@ -242,6 +242,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. */ @@ -323,6 +325,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; @@ -1274,6 +1280,7 @@ enum { PACKET_qAttached, PACKET_ConditionalTracepoints, PACKET_ConditionalBreakpoints, + PACKET_BreakpointCommands, PACKET_FastTracepoints, PACKET_StaticTracepoints, PACKET_InstallInTrace, @@ -3801,6 +3808,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) @@ -3898,6 +3915,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, @@ -7847,6 +7866,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->tcommands)) + 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->tcommands, 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->tcommands); +} + /* 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. */ @@ -7884,6 +7934,9 @@ remote_insert_breakpoint (struct gdbarch if (remote_supports_cond_breakpoints ()) remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); + 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); @@ -8125,6 +8178,9 @@ remote_insert_hw_breakpoint (struct gdba if (remote_supports_cond_breakpoints ()) remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); + 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); @@ -10063,6 +10119,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) { @@ -10981,6 +11045,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; @@ -11511,6 +11576,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.310 diff -u -p -r1.310 target.c --- target.c 5 Jun 2012 21:22:31 -0000 1.310 +++ target.c 25 Jun 2012 15:12:58 -0000 @@ -703,6 +703,7 @@ update_current_target (void) INHERIT (to_can_use_agent, t); INHERIT (to_magic, t); INHERIT (to_supports_evaluation_of_breakpoint_conditions, t); + INHERIT (to_can_run_breakpoint_commands, t); /* Do not inherit to_memory_map. */ /* Do not inherit to_flash_erase. */ /* Do not inherit to_flash_done. */ @@ -932,6 +933,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_use_agent, (int (*) (int)) tcomplain); Index: target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.238 diff -u -p -r1.238 target.h --- target.h 5 Jun 2012 21:22:31 -0000 1.238 +++ target.h 25 Jun 2012 15:12:58 -0000 @@ -672,6 +672,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 @@ -991,6 +995,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: doc/agentexpr.texi =================================================================== RCS file: /cvs/src/src/gdb/doc/agentexpr.texi,v retrieving revision 1.17 diff -u -p -r1.17 agentexpr.texi --- doc/agentexpr.texi 4 Jan 2012 08:17:20 -0000 1.17 +++ doc/agentexpr.texi 25 Jun 2012 15:12:58 -0000 @@ -493,6 +493,22 @@ Record the bytes at @var{addr} in a trac by GDB. Stop at either the first zero byte, or when @var{size} bytes have been recorded, whichever occurs first. +@item @code{printf} (0x34) @var{numargs} @var{string} @result{} +Do a formatted print, in the style of the C function @code{printf}). +The value of @var{numargs} is the number of arguments to expect on the +stack, while @var{string} is the format string, prefixed with a +two-byte length, and suffixed with a zero byte. The format string +includes escaped sequences just as it appears in C source, so for +instance the format string @code{"\t%d\n"} is six characters long, and +the output will consist of a tab character, a decimal number, and a +newline. At the top of the stack, above the values to be printed, +this bytecode will pop a ``function'' and ``channel''. If the +function is nonzero, then the target may treat it as a function and +call it, passing the channel as a first argument, as with the C +function @code{fprintf}. If the function is zero, then the target may +simply call a standard printf function of its choice. In all, this +bytecode pops 2 + @var{numargs} stack elements, and pushes nothing. + @item @code{end} (0x27): @result{} Stop executing bytecode; the result should be the top element of the stack. If the purpose of the expression was to compute an lvalue or a Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.979 diff -u -p -r1.979 gdb.texinfo --- doc/gdb.texinfo 23 Jun 2012 22:23:47 -0000 1.979 +++ doc/gdb.texinfo 25 Jun 2012 15:13:00 -0000 @@ -4649,6 +4649,14 @@ program's @code{printf} function. This 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{}] @@ -4672,6 +4680,12 @@ Handle the output using the @value{GDBN} Handle the output by calling a function in your program (normally @code{printf}). +@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 running commands on the target. + @item set dprintf-function @var{function} Set the function to call if the dprintf style is @code{call}. By default its value is @code{printf}. You may set it to any expression. @@ -4706,6 +4720,17 @@ Note that the @code{info break} displays as normal breakpoint commands; you can thus easily see the effect of the variable settings. +@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. This only applies +if the @code{dprintf-style} is @code{agent}. + +@item show disconnected-dprintf off +@kindex show disconnected-dprintf +Show the current choice for disconnected @code{dprintf}. + @end table @value{GDBN} does not check the validity of function and channel, @@ -34410,6 +34435,13 @@ of the addresses of @code{globa} and @co the result of the addition, while an evaluation expression will do the addition and return the sum. +@kindex maint agent-printf +@item maint agent-printf @var{format},@var{expr},... +Translate the given format string and list of argument expressions +into remote agent bytecodes and display them as a disassembled list. +This command is useful for debugging the agent version of dynamic +printf (@pxref{Dynamic Printf}. + @kindex maint info breakpoints @item @anchor{maint info breakpoints}maint info breakpoints Using the same format as @samp{info breakpoints}, display both the @@ -35644,7 +35676,7 @@ avoid potential problems with duplicate be implemented in an idempotent way.} @item z0,@var{addr},@var{kind} -@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]} +@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]}@r{[};cmds:@var{persist},@var{cmd_list}@dots{}@r{]} @cindex @samp{z0} packet @cindex @samp{Z0} packet Insert (@samp{Z0}) or remove (@samp{z0}) a memory breakpoint at address @@ -35672,6 +35704,22 @@ actual conditional expression in bytecod @end table +The optional @var{cmd_list} parameter introduces commands that may be +run on the target, rather than being reported back to @value{GDBN}. +The parameter starts with a numeric flag @var{persist}; if the flag is +nonzero, then the breakpoint may remain active and the commands +continue to be run even when @value{GDBN} disconnects from the target. +Following this flag is a series of expressions concatenated with no +separators. Each expression has the following form: + +@table @samp + +@item X @var{len},@var{expr} +@var{len} is the length of the bytecode expression and @var{expr} is the +actual conditional expression in bytecode form. + +@end table + see @ref{Architecture-Specific Protocol Details}. @emph{Implementation note: It is possible for a target to copy or move @@ -36605,6 +36653,11 @@ These are the currently defined stub fea @tab @samp{-} @tab No +@item @samp{BreakpointCommands} +@tab No +@tab @samp{-} +@tab No + @end multitable These are the currently defined stub features, in more detail: @@ -36754,6 +36807,11 @@ to be enabled and disabled while a trace The remote stub supports the @samp{tracenz} bytecode for collecting strings. See @ref{Bytecode Descriptions} for details about the bytecode. +@item BreakpointCommands +@cindex breakpoint commands, in remote protocol +The remote stub supports running a breakpoint's command list itself, +rather than reporting the hit to @value{GDBN}. + @end table @item qSymbol:: Index: gdbserver/ax.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/ax.c,v retrieving revision 1.2 diff -u -p -r1.2 ax.c --- gdbserver/ax.c 6 Jun 2012 08:31:31 -0000 1.2 +++ gdbserver/ax.c 25 Jun 2012 15:13:01 -0000 @@ -18,6 +18,7 @@ #include "server.h" #include "ax.h" +#include "format.h" static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2); @@ -789,6 +790,123 @@ compile_bytecodes (struct agent_expr *ae #endif +/* Make printf-type calls using arguments supplied from the host. We + need to parse the format string ourselves, and call the formatting + function with one argument at a time, partly because there is no + safe portable way to construct a varargs call, and partly to serve + as a security barrier against bad format strings that might get + in. */ + +static void +ax_printf (CORE_ADDR fn, CORE_ADDR chan, char *format, + int nargs, ULONGEST *args) +{ + char *f = format; + struct format_piece *fpieces; + int i, fp; + char *current_substring; + int nargs_wanted; + + ax_debug ("Printf of \"%s\" with %d args", format, nargs); + + fpieces = parse_format_string (&f); + + nargs_wanted = 0; + for (fp = 0; fpieces[fp].string != NULL; fp++) + if (fpieces[fp].argclass != literal_piece) + ++nargs_wanted; + + if (nargs != nargs_wanted) + error (_("Wrong number of arguments for specified format-string")); + + i = 0; + for (fp = 0; fpieces[fp].string != NULL; fp++) + { + current_substring = fpieces[fp].string; + ax_debug ("current substring is '%s', class is %d", + current_substring, fpieces[fp].argclass); + switch (fpieces[fp].argclass) + { + case string_arg: + { + gdb_byte *str; + CORE_ADDR tem; + int j; + + tem = args[i]; + + /* This is a %s argument. Find the length of the string. */ + for (j = 0;; j++) + { + gdb_byte c; + + read_inferior_memory (tem + j, &c, 1); + if (c == 0) + break; + } + + /* Copy the string contents into a string inside GDB. */ + str = (gdb_byte *) alloca (j + 1); + if (j != 0) + read_inferior_memory (tem, str, j); + str[j] = 0; + + printf (current_substring, (char *) str); + } + break; + + case long_long_arg: +#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG) + { + long long val = args[i]; + + printf (current_substring, val); + break; + } +#else + error (_("long long not supported in agent printf")); +#endif + case int_arg: + { + int val = args[i]; + + printf (current_substring, val); + break; + } + + case long_arg: + { + long val = args[i]; + + printf (current_substring, val); + break; + } + + case literal_piece: + /* Print a portion of the format string that has no + directives. Note that this will not include any + ordinary %-specs, but it might include "%%". That is + why we use printf_filtered and not puts_filtered here. + Also, we pass a dummy argument because some platforms + have modified GCC to include -Wformat-security by + default, which will warn here if there is no + argument. */ + printf (current_substring, 0); + break; + + default: + error (_("Format directive in '%s' not supported in agent printf"), + current_substring); + } + + /* Maybe advance to the next argument. */ + if (fpieces[fp].argclass != literal_piece) + ++i; + } + + free_format_pieces (fpieces); +} + /* The agent expression evaluator, as specified by the GDB docs. It returns 0 if everything went OK, and a nonzero error code otherwise. */ @@ -1152,6 +1270,37 @@ gdb_eval_agent_expr (struct regcache *re top = stack[sp]; break; + case gdb_agent_op_printf: + { + int nargs, slen, i; + CORE_ADDR fn = 0, chan = 0; + /* Can't have more args than the entire size of the stack. */ + ULONGEST args[STACK_MAX]; + char *format; + + nargs = aexpr->bytes[pc++]; + slen = aexpr->bytes[pc++]; + slen = (slen << 8) + aexpr->bytes[pc++]; + format = (char *) &(aexpr->bytes[pc]); + pc += slen; + /* Pop function and channel. */ + fn = top; + if (--sp >= 0) + top = stack[sp]; + chan = top; + if (--sp >= 0) + top = stack[sp]; + /* Pop arguments into a dedicated array. */ + for (i = 0; i < nargs; ++i) + { + args[i] = top; + if (--sp >= 0) + top = stack[sp]; + } + ax_printf (fn, chan, format, nargs, args); + } + 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.224 diff -u -p -r1.224 linux-low.c --- gdbserver/linux-low.c 12 Jun 2012 14:02:15 -0000 1.224 +++ gdbserver/linux-low.c 25 Jun 2012 15:13:01 -0000 @@ -2618,7 +2618,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 @@ -3499,7 +3502,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/Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v retrieving revision 1.134 diff -u -p -r1.134 Makefile.in --- gdbserver/Makefile.in 30 May 2012 19:43:15 -0000 1.134 +++ gdbserver/Makefile.in 25 Jun 2012 15:13:01 -0000 @@ -100,6 +100,9 @@ GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ +WARN_CFLAGS_NO_FORMAT = `echo " $(WARN_CFLAGS) " \ + | sed "s/ -Wformat-nonliteral / -Wno-format-nonliteral /g"` + # CFLAGS is specifically reserved for setting from the command line # when running make. I.E. "make CFLAGS=-Wmissing-prototypes". CFLAGS = @CFLAGS@ @@ -152,7 +155,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPAR OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ utils.o version.o vec.o \ mem-break.o hostio.o event-loop.o tracepoint.o \ - xml-utils.o common-utils.o ptid.o buffer.o \ + xml-utils.o common-utils.o ptid.o buffer.o format.o \ dll.o \ $(XML_BUILTIN) \ $(DEPFILES) $(LIBOBJS) @@ -273,7 +276,7 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS) ${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \ $(XM_CLIBS) -IPA_OBJS=ax-ipa.o tracepoint-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES} +IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES} IPA_LIB=libinproctrace.so @@ -447,11 +450,13 @@ IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_ # In-process agent object rules ax-ipa.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def - $(CC) -c $(IPAGENT_CFLAGS) $< -o ax-ipa.o + $(CC) -c $(IPAGENT_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< -o ax-ipa.o tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h} $(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o utils-ipa.o: utils.c $(server_h) $(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o +format-ipa.o: ../common/format.c $(server_h) ${ax_h} + $(CC) -c $(IPAGENT_CFLAGS) $< -o format-ipa.o common-utils-ipa.o: ../common/common-utils.c $(server_h) $(CC) -c $(IPAGENT_CFLAGS) $< -o common-utils-ipa.o remote-utils-ipa.o: remote-utils.c $(server_h) @@ -468,6 +473,7 @@ amd64-linux-ipa.o : amd64-linux.c $(regd $(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o ax.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< event-loop.o: event-loop.c $(server_h) hostio.o: hostio.c $(server_h) hostio-errno.o: hostio-errno.c $(server_h) @@ -512,6 +518,9 @@ ptid.o: ../common/ptid.c $(ptid_h) buffer.o: ../common/buffer.c $(server_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER +format.o: ../common/format.c $(server_h) + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER + agent.o: ../common/agent.c $(server_h) $(agent_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER 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 25 Jun 2012 15:13:01 -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 25 Jun 2012 15:13:01 -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.174 diff -u -p -r1.174 server.c --- gdbserver/server.c 13 Jun 2012 02:35:01 -0000 1.174 +++ gdbserver/server.c 25 Jun 2012 15:13:01 -0000 @@ -1685,8 +1685,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+"); if (target_supports_agent ()) strcat (own_buf, ";QAgent+"); @@ -2907,6 +2908,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 != ';') @@ -2916,22 +2918,33 @@ process_point_options (CORE_ADDR point_a while (*dataptr) { - switch (*dataptr) + if (*dataptr == ';') + ++dataptr; + + if (*dataptr == 'X') { - case 'X': - /* Conditional expression. */ - if (remote_debug) - 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; @@ -2997,7 +3010,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); @@ -3008,9 +3021,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 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 25 Jun 2012 15:13:01 -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: common/format.c =================================================================== RCS file: common/format.c diff -N common/format.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ common/format.c 25 Jun 2012 15:13:01 -0000 @@ -0,0 +1,400 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef GDBSERVER +#include "server.h" +#else +#include "defs.h" +#endif + +#include <string.h> + +#include "format.h" + +struct format_piece * +parse_format_string (char **arg) +{ + char *s, *f, *string; + char *prev_start; + char *percent_loc; + char *sub_start, *current_substring; + struct format_piece *pieces; + int next_frag; + int max_pieces; + enum argclass this_argclass; + + s = *arg; + + /* 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 != '"' && *s != '\0') + { + int c = *s++; + switch (c) + { + case '\0': + continue; + + 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; + } + } + + /* Terminate our escape-processed copy. */ + *f++ = '\0'; + + /* Whether the format string ended with double-quote or zero, we're + done with it; it's up to callers to complain about syntax. */ + *arg = s; + + /* Need extra space for the '\0's. Doubling the size is sufficient. */ + + current_substring = xmalloc (strlen (string) * 2 + 1000); + + max_pieces = strlen (string) + 2; + + pieces = (struct format_piece *) + xmalloc (max_pieces * sizeof (struct format_piece)); + + next_frag = 0; + + /* Now scan the string for %-specs and see what kinds of args they want. + argclass classifies the %-specs so we can give printf-type functions + something of the right size. */ + + f = string; + prev_start = string; + while (*f) + if (*f++ == '%') + { + int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; + int seen_space = 0, seen_plus = 0; + int seen_big_l = 0, seen_h = 0, seen_big_h = 0; + int seen_big_d = 0, seen_double_big_d = 0; + int bad = 0; + + /* Skip over "%%", it will become part of a literal piece. */ + if (*f == '%') + { + f++; + continue; + } + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - 1 - prev_start); + current_substring += f - 1 - prev_start; + *current_substring++ = '\0'; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = literal_piece; + next_frag++; + + percent_loc = f - 1; + + /* Check the validity of the format specifier, and work + out what argument it expects. We only accept C89 + format strings, with the exception of long long (which + we autoconf for). */ + + /* The first part of a format specifier is a set of flag + characters. */ + while (strchr ("0-+ #", *f)) + { + if (*f == '#') + seen_hash = 1; + else if (*f == '0') + seen_zero = 1; + else if (*f == ' ') + seen_space = 1; + else if (*f == '+') + seen_plus = 1; + f++; + } + + /* The next part of a format specifier is a width. */ + while (strchr ("0123456789", *f)) + f++; + + /* The next part of a format specifier is a precision. */ + if (*f == '.') + { + seen_prec = 1; + f++; + while (strchr ("0123456789", *f)) + f++; + } + + /* The next part of a format specifier is a length modifier. */ + if (*f == 'h') + { + seen_h = 1; + f++; + } + else if (*f == 'l') + { + f++; + lcount++; + if (*f == 'l') + { + f++; + lcount++; + } + } + else if (*f == 'L') + { + seen_big_l = 1; + f++; + } + /* Decimal32 modifier. */ + else if (*f == 'H') + { + seen_big_h = 1; + f++; + } + /* Decimal64 and Decimal128 modifiers. */ + else if (*f == 'D') + { + f++; + + /* Check for a Decimal128. */ + if (*f == 'D') + { + f++; + seen_double_big_d = 1; + } + else + seen_big_d = 1; + } + + switch (*f) + { + case 'u': + if (seen_hash) + bad = 1; + /* FALLTHROUGH */ + + case 'o': + case 'x': + case 'X': + if (seen_space || seen_plus) + bad = 1; + /* FALLTHROUGH */ + + case 'd': + case 'i': + if (lcount == 0) + this_argclass = int_arg; + else if (lcount == 1) + this_argclass = long_arg; + else + this_argclass = long_long_arg; + + if (seen_big_l) + bad = 1; + break; + + case 'c': + this_argclass = lcount == 0 ? int_arg : wide_char_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'p': + this_argclass = ptr_arg; + if (lcount || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 's': + this_argclass = lcount == 0 ? string_arg : wide_string_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + if (seen_big_h || seen_big_d || seen_double_big_d) + this_argclass = decfloat_arg; + else if (seen_big_l) + this_argclass = long_double_arg; + else + this_argclass = double_arg; + + if (lcount || seen_h) + bad = 1; + break; + + case '*': + error (_("`*' not supported for precision or width in printf")); + + case 'n': + error (_("Format specifier `n' not supported in printf")); + + case '\0': + error (_("Incomplete format specifier at end of format string")); + + default: + error (_("Unrecognized format specifier '%c' in printf"), *f); + } + + if (bad) + error (_("Inappropriate modifiers to " + "format specifier '%c' in printf"), + *f); + + f++; + + sub_start = current_substring; + + if (lcount > 1 && USE_PRINTF_I64) + { + /* Windows' printf does support long long, but not the usual way. + Convert %lld to %I64d. */ + int length_before_ll = f - percent_loc - 1 - lcount; + + strncpy (current_substring, percent_loc, length_before_ll); + strcpy (current_substring + length_before_ll, "I64"); + current_substring[length_before_ll + 3] = + percent_loc[length_before_ll + lcount]; + current_substring += length_before_ll + 4; + } + else if (this_argclass == wide_string_arg + || this_argclass == wide_char_arg) + { + /* Convert %ls or %lc to %s. */ + int length_before_ls = f - percent_loc - 2; + + strncpy (current_substring, percent_loc, length_before_ls); + strcpy (current_substring + length_before_ls, "s"); + current_substring += length_before_ls + 2; + } + else + { + strncpy (current_substring, percent_loc, f - percent_loc); + current_substring += f - percent_loc; + } + + *current_substring++ = '\0'; + + prev_start = f; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = this_argclass; + next_frag++; + } + + /* Record the remainder of the string. */ + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - prev_start); + current_substring += f - prev_start; + *current_substring++ = '\0'; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = literal_piece; + next_frag++; + + /* Record an end-of-array marker. */ + + pieces[next_frag].string = NULL; + pieces[next_frag].argclass = literal_piece; + + return pieces; +} + +void +free_format_pieces (struct format_piece *pieces) +{ + if (!pieces) + return; + + /* We happen to know that all the string pieces are in the block + pointed to by the first string piece. */ + if (pieces[0].string) + xfree (pieces[0].string); + + xfree (pieces); +} + +void +free_format_pieces_cleanup (void *ptr) +{ + void **location = ptr; + + if (location == NULL) + return; + + if (*location != NULL) + { + free_format_pieces (*location); + *location = NULL; + } +} + Index: common/format.h =================================================================== RCS file: common/format.h diff -N common/format.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ common/format.h 25 Jun 2012 15:13:01 -0000 @@ -0,0 +1,63 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) +# define USE_PRINTF_I64 1 +# define PRINTF_HAS_LONG_LONG +#else +# define USE_PRINTF_I64 0 +#endif + +/* The argclass represents the general type of data that goes with a + format directive; int_arg for %d, long_arg for %l, and so forth. + Note that these primarily distinguish types by size and need for + special handling, so for instance %u and %x are (at present) also + classed as int_arg. */ + +enum argclass + { + literal_piece, + int_arg, long_arg, long_long_arg, ptr_arg, + string_arg, wide_string_arg, wide_char_arg, + double_arg, long_double_arg, decfloat_arg + }; + +/* A format piece is a section of the format string that may include a + single print directive somewhere in it, and the associated class + for the argument. */ + +struct format_piece +{ + char *string; + enum argclass argclass; +}; + +/* Return an array of printf fragments found at the given string, and + rewrite ARG with a pointer to the end of the format string. */ + +extern struct format_piece *parse_format_string (char **arg); + +/* Given a pointer to an array of format pieces, free any memory that + would have been allocated by parse_format_string. */ + +extern void free_format_pieces (struct format_piece *frags); + +/* Freeing, cast as a cleanup. */ + +extern void free_format_pieces_cleanup (void *); Index: testsuite/gdb.base/dprintf.exp =================================================================== RCS file: /cvs/src/src/gdb/testsuite/gdb.base/dprintf.exp,v retrieving revision 1.3 diff -u -p -r1.3 dprintf.exp --- testsuite/gdb.base/dprintf.exp 15 May 2012 13:36:18 -0000 1.3 +++ testsuite/gdb.base/dprintf.exp 25 Jun 2012 15:13:01 -0000 @@ -85,6 +85,30 @@ if ![target_info exists gdb,noinferiorio "2nd dprintf, fprintf" } +set target_can_dprintf 1 +set msg "Set dprintf style to agent" +gdb_test_multiple "set dprintf-style agent" $msg { + -re "warning: Target cannot run dprintf commands.*" { + set target_can_dprintf 0 + pass "$msg - cannot do" + } + -re ".*$gdb_prompt $" { + pass "$msg - can do" + } +} + +if $target_can_dprintf { + + gdb_run_cmd + + gdb_test "" "Breakpoint" + + gdb_test "continue" "Breakpoint \[0-9\]+, foo .*" "1st dprintf, agent" + + gdb_test "continue" "Breakpoint \[0-9\]+, foo .*" "2nd dprintf, agent" + +} + gdb_test "set dprintf-style foobar" "Undefined item: \"foobar\"." \ "Set dprintf style to an unrecognized type" ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] Dynamic printf for a target agent 2012-06-25 15:31 ` Stan Shebs @ 2012-06-27 20:45 ` Tom Tromey 2012-07-02 18:19 ` Stan Shebs 2012-07-02 16:41 ` Build regression on 64-bit hosts " Jan Kratochvil 1 sibling, 1 reply; 13+ messages in thread From: Tom Tromey @ 2012-06-27 20:45 UTC (permalink / raw) To: Stan Shebs; +Cc: Yao Qi, gdb-patches >>>>> "Stan" == Stan Shebs <stanshebs@earthlink.net> writes: Stan> +static void Stan> +maint_agent_printf_command (char *exp, int from_tty) Stan> +{ [...] Stan> + expr = parse_exp_1 (&cmd1, (struct block *) 0, 1); Now that Hui's "-at" patch is going in, perhaps this function should have the same treatment. Stan> +void Stan> +ax_string (struct agent_expr *x, char *str, int slen) Stan> +{ Stan> + int i; Stan> + Stan> + grow_expr (x, slen + 3); Stan> + x->buf[x->len++] = ((slen + 1) >> 8) & 0xff; Stan> + x->buf[x->len++] = (slen + 1) & 0xff; I think this should check that the length fits in 2 bytes. Stan> +@item @code{printf} (0x34) @var{numargs} @var{string} @result{} Stan> +Do a formatted print, in the style of the C function @code{printf}). Stan> +The value of @var{numargs} is the number of arguments to expect on the Stan> +stack, while @var{string} is the format string, prefixed with a Stan> +two-byte length, and suffixed with a zero byte. The format string I think the docs should whether the length includes the zero byte. Stan> + case string_arg: Stan> + { Stan> + gdb_byte *str; Stan> + CORE_ADDR tem; Stan> + int j; Stan> + Stan> + tem = args[i]; Stan> + Stan> + /* This is a %s argument. Find the length of the string. */ Stan> + for (j = 0;; j++) Stan> + { Stan> + gdb_byte c; Stan> + Stan> + read_inferior_memory (tem + j, &c, 1); Stan> + if (c == 0) Stan> + break; Stan> + } Stan> + Stan> + /* Copy the string contents into a string inside GDB. */ Stan> + str = (gdb_byte *) alloca (j + 1); Stan> + if (j != 0) Stan> + read_inferior_memory (tem, str, j); Stan> + str[j] = 0; Stan> + Stan> + printf (current_substring, (char *) str); Is it ever possible for the argument to "%s" to be NULL? It seems like it should be; but then the length-finding code seems wrong, and the printing of a NULL should be handled directly, not left to the host printf. Stan> + case gdb_agent_op_printf: Stan> + { Stan> + int nargs, slen, i; Stan> + CORE_ADDR fn = 0, chan = 0; Stan> + /* Can't have more args than the entire size of the stack. */ Stan> + ULONGEST args[STACK_MAX]; Stan> + char *format; Stan> + Stan> + nargs = aexpr->bytes[pc++]; Stan> + slen = aexpr->bytes[pc++]; Stan> + slen = (slen << 8) + aexpr->bytes[pc++]; Stan> + format = (char *) &(aexpr->bytes[pc]); Perhaps double-check that the terminating \0 byte is in fact present. Stan> +if $target_can_dprintf { Stan> + Stan> + gdb_run_cmd Stan> + Stan> + gdb_test "" "Breakpoint" This seems weird. FWIW I rather liked the format-parsing refactoring. Tom ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH] Dynamic printf for a target agent 2012-06-27 20:45 ` Tom Tromey @ 2012-07-02 18:19 ` Stan Shebs 2012-07-18 19:18 ` 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] Jan Kratochvil 0 siblings, 1 reply; 13+ messages in thread From: Stan Shebs @ 2012-07-02 18:19 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches [-- Attachment #1: Type: text/plain, Size: 7114 bytes --] On 6/27/12 1:44 PM, Tom Tromey wrote: >>>>>> "Stan" == Stan Shebs <stanshebs@earthlink.net> writes: > Stan> +static void > Stan> +maint_agent_printf_command (char *exp, int from_tty) > Stan> +{ > [...] > Stan> + expr = parse_exp_1 (&cmd1, (struct block *) 0, 1); > > Now that Hui's "-at" patch is going in, perhaps this function should > have the same treatment. Yeah, that would be good for him to do. :-) > > Stan> +void > Stan> +ax_string (struct agent_expr *x, char *str, int slen) > Stan> +{ > Stan> + int i; > Stan> + > Stan> + grow_expr (x, slen + 3); > Stan> + x->buf[x->len++] = ((slen + 1) >> 8) & 0xff; > Stan> + x->buf[x->len++] = (slen + 1) & 0xff; > > I think this should check that the length fits in 2 bytes. Done. > > Stan> +@item @code{printf} (0x34) @var{numargs} @var{string} @result{} > Stan> +Do a formatted print, in the style of the C function @code{printf}). > Stan> +The value of @var{numargs} is the number of arguments to expect on the > Stan> +stack, while @var{string} is the format string, prefixed with a > Stan> +two-byte length, and suffixed with a zero byte. The format string > > I think the docs should whether the length includes the zero byte. I clarified the doc. > > Stan> + case string_arg: > Stan> + { > Stan> + gdb_byte *str; > Stan> + CORE_ADDR tem; > Stan> + int j; > Stan> + > Stan> + tem = args[i]; > Stan> + > Stan> + /* This is a %s argument. Find the length of the string. */ > Stan> + for (j = 0;; j++) > Stan> + { > Stan> + gdb_byte c; > Stan> + > Stan> + read_inferior_memory (tem + j, &c, 1); > Stan> + if (c == 0) > Stan> + break; > Stan> + } > Stan> + > Stan> + /* Copy the string contents into a string inside GDB. */ > Stan> + str = (gdb_byte *) alloca (j + 1); > Stan> + if (j != 0) > Stan> + read_inferior_memory (tem, str, j); > Stan> + str[j] = 0; > Stan> + > Stan> + printf (current_substring, (char *) str); > > Is it ever possible for the argument to "%s" to be NULL? It seems like > it should be; but then the length-finding code seems wrong, and the > printing of a NULL should be handled directly, not left to the host > printf. A NULL argument to %s is not necessarily a problem, depending on the target. In any case, it's left up to read_inferior_memory to error out if any part of the string is not at a valid memory address. > > Stan> + case gdb_agent_op_printf: > Stan> + { > Stan> + int nargs, slen, i; > Stan> + CORE_ADDR fn = 0, chan = 0; > Stan> + /* Can't have more args than the entire size of the stack. */ > Stan> + ULONGEST args[STACK_MAX]; > Stan> + char *format; > Stan> + > Stan> + nargs = aexpr->bytes[pc++]; > Stan> + slen = aexpr->bytes[pc++]; > Stan> + slen = (slen << 8) + aexpr->bytes[pc++]; > Stan> + format = (char *) &(aexpr->bytes[pc]); > > Perhaps double-check that the terminating \0 byte is in fact present. Also a good idea, and done. > > Stan> +if $target_can_dprintf { > Stan> + > Stan> + gdb_run_cmd > Stan> + > Stan> + gdb_test "" "Breakpoint" > > This seems weird. > It does look a little weird, but seems to be done a fair amount, not always obviously because it's often broken up over several lines. I went ahead and committed with these changes, so we can start collecting some user experience. Stan stan@codesourcery.com 2012-07-02 Stan Shebs <stan@codesourcery.com> Add target-side support for dynamic printf. * NEWS: Mention the additional style. * breakpoint.h (struct bp_target_info): New fields tcommands, persist. (struct bp_location): New field cmd_bytecode. * breakpoint.c: Include format.h. (disconnected_dprintf): New global. (parse_cmd_to_aexpr): New function. (build_target_command_list): New function. (insert_bp_location): Call it. (remove_breakpoints_pid): Skip dprintf breakpoints. (print_one_breakpoint_location): Ditto. (dprintf_style_agent): New global. (dprintf_style_enums): Add dprintf_style_agent. (update_dprintf_command_list): Add agent case. (agent_printf_command): New function. (_initialize_breakpoint): Add new commands. * common/ax.def (printf): New bytecode. * ax.h (ax_string): Declare. * ax-gdb.h (gen_printf): Declare. * ax-gdb.c: Include cli-utils.h, format.h. (gen_printf): New function. (maint_agent_print_command): New function. (_initialize_ax_gdb): Add maint agent-printf command. * ax-general.c (ax_string): New function. (ax_print): Add printf disassembly. * Makefile.in (SFILES): Add format.c (COMMON_OBS): Add format.o. * common/format.h: New file. * common/format.c: New file. * printcmd.c: Include format.h. (ui_printf): Call parse_format_string. * remote.c (remote_state): New field breakpoint_commands. (PACKET_BreakpointCommands): 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] * Makefile.in (WARN_CFLAGS_NO_FORMAT): Define. (ax.o): Add it to build rule. (ax-ipa.o): Ditto. (OBS): Add format.o. (IPA_OBS): Add format.o. * 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.h (any_persistent_commands): Declare. (add_breakpoint_commands): Declare. (gdb_no_commands_at_breakpoint): Declare. (run_breakpoint_commands): Declare. * 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. * linux-low.c (linux_wait_1): Test for and run breakpoint commands locally. * ax.c: Include format.h. (ax_printf): New function. (gdb_eval_agent_expr): Add printf opcode. [doc] * gdb.texinfo (Dynamic Printf): Mention agent style and disconnected dprintf. (Maintenance Commands): Describe maint agent-printf. (General Query Packets): Mention BreakpointCommands feature. (Packets): Document commands extension to Z0 packet. * agentexpr.texi (Bytecode Descriptions): Document printf bytecode. [testsuite] * gdb.base/dprintf.exp: Add agent style tests. [-- Attachment #2: dprintf-patch-6 --] [-- Type: text/plain, Size: 82151 bytes --] Index: Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/Makefile.in,v retrieving revision 1.1209 diff -u -p -r1.1209 Makefile.in --- Makefile.in 30 May 2012 19:31:44 -0000 1.1209 +++ Makefile.in 2 Jul 2012 15:25:07 -0000 @@ -745,7 +745,8 @@ SFILES = ada-exp.y ada-lang.c ada-typepr annotate.c common/signals.c copying.c dfp.c gdb.c inf-child.c \ regset.c sol-thread.c windows-termcap.c \ common/common-utils.c common/xml-utils.c \ - common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c + common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \ + common/format.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -827,6 +828,7 @@ gnulib/import/extra/snippet/arg-nonnull. gnulib/import/extra/snippet/warn-on-use.h \ gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ +common/format.h \ common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h # Header files that already have srcdir in them, or which are in objdir. @@ -916,7 +918,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ inferior.o osdata.o gdb_usleep.o record.o gcore.o \ jit.o progspace.o skip.o probe.o \ - common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o + common-utils.o buffer.o ptid.o gdb-dlfcn.o common-agent.o \ + format.o TSOBS = inflow.o @@ -1916,6 +1919,10 @@ buffer.o: ${srcdir}/common/buffer.c $(COMPILE) $(srcdir)/common/buffer.c $(POSTCOMPILE) +format.o: ${srcdir}/common/format.c + $(COMPILE) $(srcdir)/common/format.c + $(POSTCOMPILE) + linux-osdata.o: ${srcdir}/common/linux-osdata.c $(COMPILE) $(srcdir)/common/linux-osdata.c $(POSTCOMPILE) Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.531 diff -u -p -r1.531 NEWS --- NEWS 28 Jun 2012 22:11:21 -0000 1.531 +++ NEWS 2 Jul 2012 15:25:07 -0000 @@ -244,11 +244,12 @@ set debug auto-load on|off show debug auto-load Control display of debugging info for auto-loading the files above. -set dprintf-style gdb|call +set dprintf-style gdb|call|agent show dprintf-style - Control the way in which a dynamic printf is performed; "gdb" requests - a GDB printf command, while "call" causes dprintf to call a function - in the inferior. + Control the way in which a dynamic printf is performed; "gdb" + requests a GDB printf command, while "call" causes dprintf to call a + function in the inferior. "agent" requests that the target agent + (such as GDBserver) do the printing. set dprintf-function <expr> show dprintf-function @@ -257,6 +258,11 @@ show dprintf-channel Set the function and optional first argument to the call when using the "call" style of dynamic printf. +set disconnected-dprintf on|off +show disconnected-dprintf + Control whether agent-style dynamic printfs continue to be in effect + after GDB disconnects. + * New configure options --with-auto-load-dir Index: ax-gdb.c =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.c,v retrieving revision 1.101 diff -u -p -r1.101 ax-gdb.c --- ax-gdb.c 18 May 2012 21:02:47 -0000 1.101 +++ ax-gdb.c 2 Jul 2012 15:25:07 -0000 @@ -41,10 +41,13 @@ #include "tracepoint.h" #include "cp-support.h" #include "arch-utils.h" +#include "cli/cli-utils.h" #include "valprint.h" #include "c-lang.h" +#include "format.h" + /* To make sense of this file, you should read doc/agentexpr.texi. Then look at the types and enums in ax-gdb.h. For the code itself, look at gen_expr, towards the bottom; that's the main function that @@ -2503,6 +2506,59 @@ 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, struct gdbarch *gdbarch, + CORE_ADDR function, LONGEST channel, + char *format, int fmtlen, + struct format_piece *frags, + int nargs, struct expression **exprs) +{ + struct expression *expr; + struct cleanup *old_chain = 0; + struct agent_expr *ax = new_agent_expr (gdbarch, scope); + union exp_element *pc; + struct axs_value value; + int i, tem, bot, fr, flen; + char *fmt; + + 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); + } + + /* Push function and channel. */ + ax_const_l (ax, channel); + ax_const_l (ax, function); + + /* Issue the printf bytecode proper. */ + ax_simple (ax, aop_printf); + ax_simple (ax, nargs); + ax_string (ax, format, fmtlen); + + /* 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) { @@ -2586,6 +2642,88 @@ 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. */ + +static void +maint_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; + char *format_start, *format_end; + struct format_piece *fpieces; + 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; + + cmdrest = skip_spaces (cmdrest); + + if (*cmdrest++ != '"') + error (_("Must start with a format string.")); + + format_start = cmdrest; + + fpieces = parse_format_string (&cmdrest); + + old_chain = make_cleanup (free_format_pieces_cleanup, &fpieces); + + format_end = cmdrest; + + if (*cmdrest++ != '"') + error (_("Bad format string, non-terminated '\"'.")); + + cmdrest = skip_spaces (cmdrest); + + if (*cmdrest != ',' && *cmdrest != 0) + error (_("Invalid argument syntax")); + + if (*cmdrest == ',') + cmdrest++; + cmdrest = skip_spaces (cmdrest); + + nargs = 0; + while (*cmdrest != '\0') + { + char *cmd1; + + cmd1 = cmdrest; + expr = parse_exp_1 (&cmd1, 0, (struct block *) 0, 1); + argvec[nargs] = expr; + ++nargs; + cmdrest = cmd1; + if (*cmdrest == ',') + ++cmdrest; + /* else complain? */ + } + + + agent = gen_printf (get_frame_pc (fi), get_current_arch (), 0, 0, + format_start, format_end - format_start, + fpieces, 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. */ + + do_cleanups (old_chain); + dont_repeat (); +} \f /* Initialization code. */ @@ -2603,4 +2741,9 @@ _initialize_ax_gdb (void) _("Translate an expression into remote " "agent bytecode for evaluation."), &maintenancelist); + + add_cmd ("agent-printf", class_maintenance, maint_agent_printf_command, + _("Translate an expression into remote " + "agent bytecode for evaluation and display the bytecodes."), + &maintenancelist); } Index: ax-gdb.h =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.h,v retrieving revision 1.24 diff -u -p -r1.24 ax-gdb.h --- ax-gdb.h 27 Apr 2012 20:47:51 -0000 1.24 +++ ax-gdb.h 2 Jul 2012 15:25:07 -0000 @@ -115,6 +115,12 @@ extern void gen_expr (struct expression extern void require_rvalue (struct agent_expr *ax, struct axs_value *value); +struct format_piece; +extern struct agent_expr *gen_printf (CORE_ADDR, struct gdbarch *, + CORE_ADDR, LONGEST, char *, int, + struct format_piece *, + 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 2 Jul 2012 15:25:07 -0000 @@ -330,6 +330,30 @@ ax_tsv (struct agent_expr *x, enum agent x->buf[x->len + 2] = (num) & 0xff; x->len += 3; } + +/* Append a string to the expression. Note that the string is going + into the bytecodes directly, not on the stack. As a precaution, + include both length as prefix, and terminate with a NUL. (The NUL + is counted in the length.) */ + +void +ax_string (struct agent_expr *x, char *str, int slen) +{ + int i; + + /* Make sure the string length is reasonable. */ + if (slen < 0 || slen > 0xffff) + internal_error (__FILE__, __LINE__, + _("ax-general.c (ax_string): string " + "length is %d, out of allowed range"), slen); + + grow_expr (x, 2 + slen + 1); + x->buf[x->len++] = ((slen + 1) >> 8) & 0xff; + x->buf[x->len++] = (slen + 1) & 0xff; + for (i = 0; i < slen; ++i) + x->buf[x->len++] = str[i]; + x->buf[x->len++] = '\0'; +} \f @@ -391,6 +415,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 slen, nargs; + + i++; + nargs = x->buf[i++]; + slen = x->buf[i++]; + slen = slen * 256 + x->buf[i++]; + fprintf_filtered (f, _(" \"%s\", %d args"), + &(x->buf[i]), nargs); + i += slen - 1; + } fprintf_filtered (f, "\n"); i += 1 + aop_map[op].op_size; Index: ax.h =================================================================== RCS file: /cvs/src/src/gdb/ax.h,v retrieving revision 1.21 diff -u -p -r1.21 ax.h --- ax.h 24 Feb 2012 15:10:58 -0000 1.21 +++ ax.h 2 Jul 2012 15:25:07 -0000 @@ -219,6 +219,9 @@ extern void ax_reg_mask (struct agent_ex /* Assemble code to operate on a trace state variable. */ extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num); + +/* Append a string to the bytecode stream. */ +extern void ax_string (struct agent_expr *x, char *str, int slen); \f /* Functions for printing out expressions, and otherwise debugging Index: breakpoint.c =================================================================== RCS file: /cvs/src/src/gdb/breakpoint.c,v retrieving revision 1.688 diff -u -p -r1.688 breakpoint.c --- breakpoint.c 2 Jul 2012 03:36:04 -0000 1.688 +++ breakpoint.c 2 Jul 2012 15:25:07 -0000 @@ -70,6 +70,8 @@ #include "ax-gdb.h" #include "dummy-frame.h" +#include "format.h" + /* readline include files */ #include "readline/readline.h" #include "readline/history.h" @@ -301,6 +303,45 @@ static struct breakpoint_ops bkpt_probe_ /* Dynamic printf class type. */ static struct breakpoint_ops dprintf_breakpoint_ops; +/* The style in which to perform a dynamic printf. This is a user + option because different output options have different tradeoffs; + if GDB does the printing, there is better error handling if there + is a problem with any of the arguments, but using an inferior + function lets you have special-purpose printers and sending of + output to the same place as compiled-in print functions. */ + +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; + +/* The function to use for dynamic printf if the preferred style is to + call into the inferior. The value is simply a string that is + copied into the command, so it can be anything that GDB can + evaluate to a callable address, not necessarily a function name. */ + +static char *dprintf_function = ""; + +/* The channel to use for dynamic printf if the preferred style is to + call into the inferior; if a nonempty string, it will be passed to + the call as the first argument, with the format string as the + second. As with the dprintf function, this can be anything that + GDB knows how to evaluate, so in addition to common choices like + "stderr", this could be an app-specific expression like + "mystreams[curlogger]". */ + +static char *dprintf_channel = ""; + +/* 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 @@ -2132,6 +2173,196 @@ build_target_condition_list (struct bp_l return; } +/* 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. */ + +static struct agent_expr * +parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd) +{ + struct cleanup *old_cleanups = 0; + struct expression *expr, **argvec; + struct agent_expr *aexpr = NULL; + struct cleanup *old_chain = NULL; + volatile struct gdb_exception ex; + char *cmdrest; + char *format_start, *format_end; + struct format_piece *fpieces; + int nargs; + struct gdbarch *gdbarch = get_current_arch (); + + if (!cmd) + return NULL; + + cmdrest = cmd; + + if (*cmdrest == ',') + ++cmdrest; + cmdrest = skip_spaces (cmdrest); + + if (*cmdrest++ != '"') + error (_("No format string following the location")); + + format_start = cmdrest; + + fpieces = parse_format_string (&cmdrest); + + old_cleanups = make_cleanup (free_format_pieces_cleanup, &fpieces); + + format_end = cmdrest; + + if (*cmdrest++ != '"') + error (_("Bad format string, non-terminated '\"'.")); + + cmdrest = skip_spaces (cmdrest); + + if (!(*cmdrest == ',' || *cmdrest == '\0')) + error (_("Invalid argument syntax")); + + if (*cmdrest == ',') + cmdrest++; + cmdrest = skip_spaces (cmdrest); + + /* For each argument, make an expression. */ + + argvec = (struct expression **) alloca (strlen (cmd) + * sizeof (struct expression *)); + + nargs = 0; + while (*cmdrest != '\0') + { + char *cmd1; + + cmd1 = cmdrest; + expr = parse_exp_1 (&cmd1, scope, block_for_pc (scope), 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, gdbarch, 0, 0, + format_start, format_end - format_start, + fpieces, nargs, argvec); + } + + if (ex.reason < 0) + { + /* If we got here, it means the command 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 other commands. */ + return NULL; + } + + do_cleanups (old_cleanups); + + /* We have a valid agent expression, return it. */ + 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 agent-style dprintf breakpoints. */ + if (bl->owner->type != bp_dprintf + || strcmp (dprintf_style, dprintf_style_agent) != 0) + 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 commands since something changed. In that + case we already freed the command bytecodes (see + force_breakpoint_reinsertion). We just + need to parse the command 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 command 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 command 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 commands to the target. */ + VEC_safe_push (agent_expr_p, bl->target_info.tcommands, + 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. @@ -2172,7 +2403,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; } @@ -2691,6 +2923,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); @@ -5827,6 +6062,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) { @@ -8525,40 +8769,6 @@ bp_loc_is_permanent (struct bp_location return retval; } -/* The style in which to perform a dynamic printf. This is a user - option because different output options have different tradeoffs; - if GDB does the printing, there is better error handling if there - is a problem with any of the arguments, but using an inferior - function lets you have special-purpose printers and sending of - output to the same place as compiled-in print functions. (Future - styles may include the ability to do a target-side printf.) */ - -static const char dprintf_style_gdb[] = "gdb"; -static const char dprintf_style_call[] = "call"; -static const char *const dprintf_style_enums[] = { - dprintf_style_gdb, - dprintf_style_call, - NULL -}; -static const char *dprintf_style = dprintf_style_gdb; - -/* The function to use for dynamic printf if the preferred style is to - call into the inferior. The value is simply a string that is - copied into the command, so it can be anything that GDB can - evaluate to a callable address, not necessarily a function name. */ - -static char *dprintf_function = ""; - -/* The channel to use for dynamic printf if the preferred style is to - call into the inferior; if a nonempty string, it will be passed to - the call as the first argument, with the format string as the - second. As with the dprintf function, this can be anything that - GDB knows how to evaluate, so in addition to common choices like - "stderr", this could be an app-specific expression like - "mystreams[curlogger]". */ - -static char *dprintf_channel = ""; - /* Build a command list for the dprintf corresponding to the current settings of the dprintf style options. */ @@ -8582,9 +8792,9 @@ update_dprintf_command_list (struct brea if (*dprintf_args != '"') error (_("Bad format string, missing '\"'.")); - if (strcmp (dprintf_style, "gdb") == 0) + if (strcmp (dprintf_style, dprintf_style_gdb) == 0) printf_line = xstrprintf ("printf %s", dprintf_args); - else if (strcmp (dprintf_style, "call") == 0) + else if (strcmp (dprintf_style, dprintf_style_call) == 0) { if (!dprintf_function) error (_("No function supplied for dprintf call")); @@ -8599,6 +8809,16 @@ update_dprintf_command_list (struct brea dprintf_function, dprintf_args); } + else if (strcmp (dprintf_style, dprintf_style_agent) == 0) + { + if (target_can_run_breakpoint_commands ()) + printf_line = xstrprintf ("agent-printf %s", dprintf_args); + else + { + warning (_("Target cannot run dprintf commands, falling back to GDB printf")); + printf_line = xstrprintf ("printf %s", dprintf_args); + } + } else internal_error (__FILE__, __LINE__, _("Invalid dprintf style.")); @@ -8608,12 +8828,15 @@ update_dprintf_command_list (struct brea { struct command_line *printf_cmd_line, *cont_cmd_line = NULL; - cont_cmd_line = xmalloc (sizeof (struct command_line)); - cont_cmd_line->control_type = simple_control; - cont_cmd_line->body_count = 0; - cont_cmd_line->body_list = NULL; - cont_cmd_line->next = NULL; - cont_cmd_line->line = xstrdup ("continue"); + if (strcmp (dprintf_style, dprintf_style_agent) != 0) + { + cont_cmd_line = xmalloc (sizeof (struct command_line)); + cont_cmd_line->control_type = simple_control; + cont_cmd_line->body_count = 0; + cont_cmd_line->body_list = NULL; + cont_cmd_line->next = NULL; + cont_cmd_line->line = xstrdup ("continue"); + } printf_cmd_line = xmalloc (sizeof (struct command_line)); printf_cmd_line->control_type = simple_control; @@ -9570,6 +9793,12 @@ dprintf_command (char *arg, int from_tty 0); } +static void +agent_printf_command (char *arg, int from_tty) +{ + error (_("May only run agent-printf on the target")); +} + /* Implement the "breakpoint_hit" breakpoint_ops method for ranged breakpoints. */ @@ -16244,6 +16473,20 @@ Show the channel to use for dynamic prin update_dprintf_commands, 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); + + add_com ("agent-printf", class_vars, agent_printf_command, _("\ +agent-printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\ +(target agent only) This is useful for formatted output in user-defined commands.")); + 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.185 diff -u -p -r1.185 breakpoint.h --- breakpoint.h 18 Jun 2012 17:28:36 -0000 1.185 +++ breakpoint.h 2 Jul 2012 15:25:07 -0000 @@ -272,6 +272,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) *tcommands; + + /* 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 @@ -358,8 +366,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 Index: printcmd.c =================================================================== RCS file: /cvs/src/src/gdb/printcmd.c,v retrieving revision 1.208 diff -u -p -r1.208 printcmd.c --- printcmd.c 18 May 2012 21:02:50 -0000 1.208 +++ printcmd.c 2 Jul 2012 15:25:07 -0000 @@ -49,18 +49,12 @@ #include "charset.h" #include "arch-utils.h" #include "cli/cli-utils.h" +#include "format.h" #ifdef TUI #include "tui/tui.h" /* For tui_active et al. */ #endif -#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) -# define USE_PRINTF_I64 1 -# define PRINTF_HAS_LONG_LONG -#else -# define USE_PRINTF_I64 0 -#endif - struct format_data { int count; @@ -2001,13 +1995,9 @@ print_variable_and_value (const char *na static void ui_printf (char *arg, struct ui_file *stream) { - char *f = NULL; + struct format_piece *fpieces; char *s = arg; - char *string = NULL; struct value **val_args; - char *substrings; - char *current_substring; - int nargs = 0; int allocated_args = 20; struct cleanup *old_cleanups; @@ -2023,64 +2013,13 @@ ui_printf (char *arg, struct ui_file *st 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 '\"'.")); + fpieces = parse_format_string (&s); - 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; + make_cleanup (free_format_pieces_cleanup, &fpieces); - default: - *f++ = c; - } - } - - /* Skip over " and following space and comma. */ - s++; - *f++ = '\0'; + if (*s++ != '"') + error (_("Bad format string, non-terminated '\"'.")); + s = skip_spaces (s); if (*s != ',' && *s != 0) @@ -2090,240 +2029,16 @@ ui_printf (char *arg, struct ui_file *st s++; s = skip_spaces (s); - /* Need extra space for the '\0's. Doubling the size is sufficient. */ - substrings = alloca (strlen (string) * 2); - current_substring = substrings; - { - /* Now scan the string for %-specs and see what kinds of args they want. - argclass[I] classifies the %-specs so we can give printf_filtered - something of the right size. */ - - enum argclass - { - int_arg, long_arg, long_long_arg, ptr_arg, - string_arg, wide_string_arg, wide_char_arg, - double_arg, long_double_arg, decfloat_arg - }; - enum argclass *argclass; - enum argclass this_argclass; - char *last_arg; + int nargs = 0; int nargs_wanted; - int i; + int i, fr; + char *current_substring; - argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass); nargs_wanted = 0; - f = string; - last_arg = string; - while (*f) - if (*f++ == '%') - { - int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; - int seen_space = 0, seen_plus = 0; - int seen_big_l = 0, seen_h = 0, seen_big_h = 0; - int seen_big_d = 0, seen_double_big_d = 0; - int bad = 0; - - /* Check the validity of the format specifier, and work - out what argument it expects. We only accept C89 - format strings, with the exception of long long (which - we autoconf for). */ - - /* Skip over "%%". */ - if (*f == '%') - { - f++; - continue; - } - - /* The first part of a format specifier is a set of flag - characters. */ - while (strchr ("0-+ #", *f)) - { - if (*f == '#') - seen_hash = 1; - else if (*f == '0') - seen_zero = 1; - else if (*f == ' ') - seen_space = 1; - else if (*f == '+') - seen_plus = 1; - f++; - } - - /* The next part of a format specifier is a width. */ - while (strchr ("0123456789", *f)) - f++; - - /* The next part of a format specifier is a precision. */ - if (*f == '.') - { - seen_prec = 1; - f++; - while (strchr ("0123456789", *f)) - f++; - } - - /* The next part of a format specifier is a length modifier. */ - if (*f == 'h') - { - seen_h = 1; - f++; - } - else if (*f == 'l') - { - f++; - lcount++; - if (*f == 'l') - { - f++; - lcount++; - } - } - else if (*f == 'L') - { - seen_big_l = 1; - f++; - } - /* Decimal32 modifier. */ - else if (*f == 'H') - { - seen_big_h = 1; - f++; - } - /* Decimal64 and Decimal128 modifiers. */ - else if (*f == 'D') - { - f++; - - /* Check for a Decimal128. */ - if (*f == 'D') - { - f++; - seen_double_big_d = 1; - } - else - seen_big_d = 1; - } - - switch (*f) - { - case 'u': - if (seen_hash) - bad = 1; - /* FALLTHROUGH */ - - case 'o': - case 'x': - case 'X': - if (seen_space || seen_plus) - bad = 1; - /* FALLTHROUGH */ - - case 'd': - case 'i': - if (lcount == 0) - this_argclass = int_arg; - else if (lcount == 1) - this_argclass = long_arg; - else - this_argclass = long_long_arg; - - if (seen_big_l) - bad = 1; - break; - - case 'c': - this_argclass = lcount == 0 ? int_arg : wide_char_arg; - if (lcount > 1 || seen_h || seen_big_l) - bad = 1; - if (seen_prec || seen_zero || seen_space || seen_plus) - bad = 1; - break; - - case 'p': - this_argclass = ptr_arg; - if (lcount || seen_h || seen_big_l) - bad = 1; - if (seen_prec || seen_zero || seen_space || seen_plus) - bad = 1; - break; - - case 's': - this_argclass = lcount == 0 ? string_arg : wide_string_arg; - if (lcount > 1 || seen_h || seen_big_l) - bad = 1; - if (seen_zero || seen_space || seen_plus) - bad = 1; - break; - - case 'e': - case 'f': - case 'g': - case 'E': - case 'G': - if (seen_big_h || seen_big_d || seen_double_big_d) - this_argclass = decfloat_arg; - else if (seen_big_l) - this_argclass = long_double_arg; - else - this_argclass = double_arg; - - if (lcount || seen_h) - bad = 1; - break; - - case '*': - error (_("`*' not supported for precision or width in printf")); - - case 'n': - error (_("Format specifier `n' not supported in printf")); - - case '\0': - error (_("Incomplete format specifier at end of format string")); - - default: - error (_("Unrecognized format specifier '%c' in printf"), *f); - } - - if (bad) - error (_("Inappropriate modifiers to " - "format specifier '%c' in printf"), - *f); - - f++; - - if (lcount > 1 && USE_PRINTF_I64) - { - /* Windows' printf does support long long, but not the usual way. - Convert %lld to %I64d. */ - int length_before_ll = f - last_arg - 1 - lcount; - - strncpy (current_substring, last_arg, length_before_ll); - strcpy (current_substring + length_before_ll, "I64"); - current_substring[length_before_ll + 3] = - last_arg[length_before_ll + lcount]; - current_substring += length_before_ll + 4; - } - else if (this_argclass == wide_string_arg - || this_argclass == wide_char_arg) - { - /* Convert %ls or %lc to %s. */ - int length_before_ls = f - last_arg - 2; - - strncpy (current_substring, last_arg, length_before_ls); - strcpy (current_substring + length_before_ls, "s"); - current_substring += length_before_ls + 2; - } - else - { - strncpy (current_substring, last_arg, f - last_arg); - current_substring += f - last_arg; - } - *current_substring++ = '\0'; - last_arg = f; - argclass[nargs_wanted++] = this_argclass; - } + for (fr = 0; fpieces[fr].string != NULL; fr++) + if (fpieces[fr].argclass != literal_piece) + ++nargs_wanted; /* Now, parse all arguments and evaluate them. Store the VALUEs in VAL_ARGS. */ @@ -2349,10 +2064,11 @@ ui_printf (char *arg, struct ui_file *st error (_("Wrong number of arguments for specified format-string")); /* Now actually print them. */ - current_substring = substrings; - for (i = 0; i < nargs; i++) + i = 0; + for (fr = 0; fpieces[fr].string != NULL; fr++) { - switch (argclass[i]) + current_substring = fpieces[fr].string; + switch (fpieces[fr].argclass) { case string_arg: { @@ -2687,20 +2403,25 @@ ui_printf (char *arg, struct ui_file *st break; } + case literal_piece: + /* Print a portion of the format string that has no + directives. Note that this will not include any + ordinary %-specs, but it might include "%%". That is + why we use printf_filtered and not puts_filtered here. + Also, we pass a dummy argument because some platforms + have modified GCC to include -Wformat-security by + default, which will warn here if there is no + argument. */ + fprintf_filtered (stream, current_substring, 0); + break; default: internal_error (__FILE__, __LINE__, _("failed internal consistency check")); } - /* Skip to the next substring. */ - current_substring += strlen (current_substring) + 1; + /* Maybe advance to the next argument. */ + if (fpieces[fr].argclass != literal_piece) + ++i; } - /* Print the portion of the format string after the last argument. - Note that this will not include any ordinary %-specs, but it - might include "%%". That is why we use printf_filtered and not - puts_filtered here. Also, we pass a dummy argument because - some platforms have modified GCC to include -Wformat-security - by default, which will warn here if there is no argument. */ - fprintf_filtered (stream, last_arg, 0); } do_cleanups (old_cleanups); } Index: remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.501 diff -u -p -r1.501 remote.c --- remote.c 28 Jun 2012 16:34:03 -0000 1.501 +++ remote.c 2 Jul 2012 15:25:08 -0000 @@ -242,6 +242,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. */ @@ -323,6 +325,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; @@ -1274,6 +1280,7 @@ enum { PACKET_qAttached, PACKET_ConditionalTracepoints, PACKET_ConditionalBreakpoints, + PACKET_BreakpointCommands, PACKET_FastTracepoints, PACKET_StaticTracepoints, PACKET_InstallInTrace, @@ -3801,6 +3808,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) @@ -3898,6 +3915,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, @@ -7873,6 +7892,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->tcommands)) + 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->tcommands, 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->tcommands); +} + /* 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. */ @@ -7910,6 +7960,9 @@ remote_insert_breakpoint (struct gdbarch if (remote_supports_cond_breakpoints ()) remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); + 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); @@ -8151,6 +8204,9 @@ remote_insert_hw_breakpoint (struct gdba if (remote_supports_cond_breakpoints ()) remote_add_target_side_condition (gdbarch, bp_tgt, p, endbuf); + 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); @@ -10089,6 +10145,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) { @@ -11007,6 +11071,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; @@ -11537,6 +11602,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.310 diff -u -p -r1.310 target.c --- target.c 5 Jun 2012 21:22:31 -0000 1.310 +++ target.c 2 Jul 2012 15:25:08 -0000 @@ -703,6 +703,7 @@ update_current_target (void) INHERIT (to_can_use_agent, t); INHERIT (to_magic, t); INHERIT (to_supports_evaluation_of_breakpoint_conditions, t); + INHERIT (to_can_run_breakpoint_commands, t); /* Do not inherit to_memory_map. */ /* Do not inherit to_flash_erase. */ /* Do not inherit to_flash_done. */ @@ -932,6 +933,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_use_agent, (int (*) (int)) tcomplain); Index: target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.239 diff -u -p -r1.239 target.h --- target.h 28 Jun 2012 16:34:03 -0000 1.239 +++ target.h 2 Jul 2012 15:25:08 -0000 @@ -672,6 +672,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 @@ -997,6 +1001,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: doc/agentexpr.texi =================================================================== RCS file: /cvs/src/src/gdb/doc/agentexpr.texi,v retrieving revision 1.17 diff -u -p -r1.17 agentexpr.texi --- doc/agentexpr.texi 4 Jan 2012 08:17:20 -0000 1.17 +++ doc/agentexpr.texi 2 Jul 2012 15:25:08 -0000 @@ -493,6 +493,23 @@ Record the bytes at @var{addr} in a trac by GDB. Stop at either the first zero byte, or when @var{size} bytes have been recorded, whichever occurs first. +@item @code{printf} (0x34) @var{numargs} @var{string} @result{} +Do a formatted print, in the style of the C function @code{printf}). +The value of @var{numargs} is the number of arguments to expect on the +stack, while @var{string} is the format string, prefixed with a +two-byte length. The last byte of the string must be zero, and is +included in the length. The format string includes escaped sequences +just as it appears in C source, so for instance the format string +@code{"\t%d\n"} is six characters long, and the output will consist of +a tab character, a decimal number, and a newline. At the top of the +stack, above the values to be printed, this bytecode will pop a +``function'' and ``channel''. If the function is nonzero, then the +target may treat it as a function and call it, passing the channel as +a first argument, as with the C function @code{fprintf}. If the +function is zero, then the target may simply call a standard formatted +print function of its choice. In all, this bytecode pops 2 + +@var{numargs} stack elements, and pushes nothing. + @item @code{end} (0x27): @result{} Stop executing bytecode; the result should be the top element of the stack. If the purpose of the expression was to compute an lvalue or a Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.987 diff -u -p -r1.987 gdb.texinfo --- doc/gdb.texinfo 2 Jul 2012 12:05:01 -0000 1.987 +++ doc/gdb.texinfo 2 Jul 2012 15:25:09 -0000 @@ -4649,6 +4649,14 @@ program's @code{printf} function. This 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{}] @@ -4672,6 +4680,12 @@ Handle the output using the @value{GDBN} Handle the output by calling a function in your program (normally @code{printf}). +@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 running commands on the target. + @item set dprintf-function @var{function} Set the function to call if the dprintf style is @code{call}. By default its value is @code{printf}. You may set it to any expression. @@ -4706,6 +4720,17 @@ Note that the @code{info break} displays as normal breakpoint commands; you can thus easily see the effect of the variable settings. +@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. This only applies +if the @code{dprintf-style} is @code{agent}. + +@item show disconnected-dprintf off +@kindex show disconnected-dprintf +Show the current choice for disconnected @code{dprintf}. + @end table @value{GDBN} does not check the validity of function and channel, @@ -34447,6 +34472,13 @@ of the addresses of @code{globa} and @co the result of the addition, while an evaluation expression will do the addition and return the sum. +@kindex maint agent-printf +@item maint agent-printf @var{format},@var{expr},... +Translate the given format string and list of argument expressions +into remote agent bytecodes and display them as a disassembled list. +This command is useful for debugging the agent version of dynamic +printf (@pxref{Dynamic Printf}. + @kindex maint info breakpoints @item @anchor{maint info breakpoints}maint info breakpoints Using the same format as @samp{info breakpoints}, display both the @@ -35681,7 +35713,7 @@ avoid potential problems with duplicate be implemented in an idempotent way.} @item z0,@var{addr},@var{kind} -@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]} +@itemx Z0,@var{addr},@var{kind}@r{[};@var{cond_list}@dots{}@r{]}@r{[};cmds:@var{persist},@var{cmd_list}@dots{}@r{]} @cindex @samp{z0} packet @cindex @samp{Z0} packet Insert (@samp{Z0}) or remove (@samp{z0}) a memory breakpoint at address @@ -35709,6 +35741,22 @@ actual conditional expression in bytecod @end table +The optional @var{cmd_list} parameter introduces commands that may be +run on the target, rather than being reported back to @value{GDBN}. +The parameter starts with a numeric flag @var{persist}; if the flag is +nonzero, then the breakpoint may remain active and the commands +continue to be run even when @value{GDBN} disconnects from the target. +Following this flag is a series of expressions concatenated with no +separators. Each expression has the following form: + +@table @samp + +@item X @var{len},@var{expr} +@var{len} is the length of the bytecode expression and @var{expr} is the +actual conditional expression in bytecode form. + +@end table + see @ref{Architecture-Specific Protocol Details}. @emph{Implementation note: It is possible for a target to copy or move @@ -36642,6 +36690,11 @@ These are the currently defined stub fea @tab @samp{-} @tab No +@item @samp{BreakpointCommands} +@tab No +@tab @samp{-} +@tab No + @end multitable These are the currently defined stub features, in more detail: @@ -36791,6 +36844,11 @@ to be enabled and disabled while a trace The remote stub supports the @samp{tracenz} bytecode for collecting strings. See @ref{Bytecode Descriptions} for details about the bytecode. +@item BreakpointCommands +@cindex breakpoint commands, in remote protocol +The remote stub supports running a breakpoint's command list itself, +rather than reporting the hit to @value{GDBN}. + @end table @item qSymbol:: Index: gdbserver/Makefile.in =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v retrieving revision 1.134 diff -u -p -r1.134 Makefile.in --- gdbserver/Makefile.in 30 May 2012 19:43:15 -0000 1.134 +++ gdbserver/Makefile.in 2 Jul 2012 15:25:10 -0000 @@ -100,6 +100,9 @@ GLOBAL_CFLAGS = ${MT_CFLAGS} ${MH_CFLAGS WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ +WARN_CFLAGS_NO_FORMAT = `echo " $(WARN_CFLAGS) " \ + | sed "s/ -Wformat-nonliteral / -Wno-format-nonliteral /g"` + # CFLAGS is specifically reserved for setting from the command line # when running make. I.E. "make CFLAGS=-Wmissing-prototypes". CFLAGS = @CFLAGS@ @@ -152,7 +155,7 @@ TAGFILES = $(SOURCES) ${HFILES} ${ALLPAR OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o target.o \ utils.o version.o vec.o \ mem-break.o hostio.o event-loop.o tracepoint.o \ - xml-utils.o common-utils.o ptid.o buffer.o \ + xml-utils.o common-utils.o ptid.o buffer.o format.o \ dll.o \ $(XML_BUILTIN) \ $(DEPFILES) $(LIBOBJS) @@ -273,7 +276,7 @@ gdbreplay$(EXEEXT): $(GDBREPLAY_OBS) ${CC-LD} $(INTERNAL_CFLAGS) $(INTERNAL_LDFLAGS) -o gdbreplay$(EXEEXT) $(GDBREPLAY_OBS) \ $(XM_CLIBS) -IPA_OBJS=ax-ipa.o tracepoint-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES} +IPA_OBJS=ax-ipa.o tracepoint-ipa.o format-ipa.o utils-ipa.o regcache-ipa.o remote-utils-ipa.o common-utils-ipa.o ${IPA_DEPFILES} IPA_LIB=libinproctrace.so @@ -447,11 +450,13 @@ IPAGENT_CFLAGS = $(CPPFLAGS) $(INTERNAL_ # In-process agent object rules ax-ipa.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def - $(CC) -c $(IPAGENT_CFLAGS) $< -o ax-ipa.o + $(CC) -c $(IPAGENT_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< -o ax-ipa.o tracepoint-ipa.o: tracepoint.c $(server_h) ${ax_h} $(CC) -c $(IPAGENT_CFLAGS) $< -o tracepoint-ipa.o utils-ipa.o: utils.c $(server_h) $(CC) -c $(IPAGENT_CFLAGS) $< -o utils-ipa.o +format-ipa.o: ../common/format.c $(server_h) ${ax_h} + $(CC) -c $(IPAGENT_CFLAGS) $< -o format-ipa.o common-utils-ipa.o: ../common/common-utils.c $(server_h) $(CC) -c $(IPAGENT_CFLAGS) $< -o common-utils-ipa.o remote-utils-ipa.o: remote-utils.c $(server_h) @@ -468,6 +473,7 @@ amd64-linux-ipa.o : amd64-linux.c $(regd $(CC) -c $(IPAGENT_CFLAGS) $< -o amd64-linux-ipa.o ax.o: ax.c $(server_h) $(ax_h) $(srcdir)/../common/ax.def + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $(WARN_CFLAGS_NO_FORMAT) $< event-loop.o: event-loop.c $(server_h) hostio.o: hostio.c $(server_h) hostio-errno.o: hostio-errno.c $(server_h) @@ -512,6 +518,9 @@ ptid.o: ../common/ptid.c $(ptid_h) buffer.o: ../common/buffer.c $(server_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER +format.o: ../common/format.c $(server_h) + $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER + agent.o: ../common/agent.c $(server_h) $(agent_h) $(CC) -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< -DGDBSERVER Index: gdbserver/ax.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/ax.c,v retrieving revision 1.2 diff -u -p -r1.2 ax.c --- gdbserver/ax.c 6 Jun 2012 08:31:31 -0000 1.2 +++ gdbserver/ax.c 2 Jul 2012 15:25:10 -0000 @@ -18,6 +18,7 @@ #include "server.h" #include "ax.h" +#include "format.h" static void ax_vdebug (const char *, ...) ATTR_FORMAT (printf, 1, 2); @@ -789,6 +790,123 @@ compile_bytecodes (struct agent_expr *ae #endif +/* Make printf-type calls using arguments supplied from the host. We + need to parse the format string ourselves, and call the formatting + function with one argument at a time, partly because there is no + safe portable way to construct a varargs call, and partly to serve + as a security barrier against bad format strings that might get + in. */ + +static void +ax_printf (CORE_ADDR fn, CORE_ADDR chan, char *format, + int nargs, ULONGEST *args) +{ + char *f = format; + struct format_piece *fpieces; + int i, fp; + char *current_substring; + int nargs_wanted; + + ax_debug ("Printf of \"%s\" with %d args", format, nargs); + + fpieces = parse_format_string (&f); + + nargs_wanted = 0; + for (fp = 0; fpieces[fp].string != NULL; fp++) + if (fpieces[fp].argclass != literal_piece) + ++nargs_wanted; + + if (nargs != nargs_wanted) + error (_("Wrong number of arguments for specified format-string")); + + i = 0; + for (fp = 0; fpieces[fp].string != NULL; fp++) + { + current_substring = fpieces[fp].string; + ax_debug ("current substring is '%s', class is %d", + current_substring, fpieces[fp].argclass); + switch (fpieces[fp].argclass) + { + case string_arg: + { + gdb_byte *str; + CORE_ADDR tem; + int j; + + tem = args[i]; + + /* This is a %s argument. Find the length of the string. */ + for (j = 0;; j++) + { + gdb_byte c; + + read_inferior_memory (tem + j, &c, 1); + if (c == 0) + break; + } + + /* Copy the string contents into a string inside GDB. */ + str = (gdb_byte *) alloca (j + 1); + if (j != 0) + read_inferior_memory (tem, str, j); + str[j] = 0; + + printf (current_substring, (char *) str); + } + break; + + case long_long_arg: +#if defined (CC_HAS_LONG_LONG) && defined (PRINTF_HAS_LONG_LONG) + { + long long val = args[i]; + + printf (current_substring, val); + break; + } +#else + error (_("long long not supported in agent printf")); +#endif + case int_arg: + { + int val = args[i]; + + printf (current_substring, val); + break; + } + + case long_arg: + { + long val = args[i]; + + printf (current_substring, val); + break; + } + + case literal_piece: + /* Print a portion of the format string that has no + directives. Note that this will not include any + ordinary %-specs, but it might include "%%". That is + why we use printf_filtered and not puts_filtered here. + Also, we pass a dummy argument because some platforms + have modified GCC to include -Wformat-security by + default, which will warn here if there is no + argument. */ + printf (current_substring, 0); + break; + + default: + error (_("Format directive in '%s' not supported in agent printf"), + current_substring); + } + + /* Maybe advance to the next argument. */ + if (fpieces[fp].argclass != literal_piece) + ++i; + } + + free_format_pieces (fpieces); +} + /* The agent expression evaluator, as specified by the GDB docs. It returns 0 if everything went OK, and a nonzero error code otherwise. */ @@ -1152,6 +1270,43 @@ gdb_eval_agent_expr (struct regcache *re top = stack[sp]; break; + case gdb_agent_op_printf: + { + int nargs, slen, i; + CORE_ADDR fn = 0, chan = 0; + /* Can't have more args than the entire size of the stack. */ + ULONGEST args[STACK_MAX]; + char *format; + + nargs = aexpr->bytes[pc++]; + slen = aexpr->bytes[pc++]; + slen = (slen << 8) + aexpr->bytes[pc++]; + format = (char *) &(aexpr->bytes[pc]); + pc += slen; + /* Pop function and channel. */ + fn = top; + if (--sp >= 0) + top = stack[sp]; + chan = top; + if (--sp >= 0) + top = stack[sp]; + /* Pop arguments into a dedicated array. */ + for (i = 0; i < nargs; ++i) + { + args[i] = top; + if (--sp >= 0) + top = stack[sp]; + } + + /* A bad format string means something is very wrong; give + up immediately. */ + if (format[slen - 1] != '\0') + error (_("Unterminated format string in printf bytecode")); + + ax_printf (fn, chan, format, nargs, args); + } + 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.224 diff -u -p -r1.224 linux-low.c --- gdbserver/linux-low.c 12 Jun 2012 14:02:15 -0000 1.224 +++ gdbserver/linux-low.c 2 Jul 2012 15:25:10 -0000 @@ -2618,7 +2618,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 @@ -3499,7 +3502,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 2 Jul 2012 15:25:10 -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 2 Jul 2012 15:25:10 -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.174 diff -u -p -r1.174 server.c --- gdbserver/server.c 13 Jun 2012 02:35:01 -0000 1.174 +++ gdbserver/server.c 2 Jul 2012 15:25:10 -0000 @@ -1685,8 +1685,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+"); if (target_supports_agent ()) strcat (own_buf, ";QAgent+"); @@ -2907,6 +2908,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 != ';') @@ -2916,22 +2918,33 @@ process_point_options (CORE_ADDR point_a while (*dataptr) { - switch (*dataptr) + if (*dataptr == ';') + ++dataptr; + + if (*dataptr == 'X') { - case 'X': - /* Conditional expression. */ - if (remote_debug) - 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; @@ -2997,7 +3010,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); @@ -3008,9 +3021,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 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 2 Jul 2012 15:25:10 -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: common/format.c =================================================================== RCS file: common/format.c diff -N common/format.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ common/format.c 2 Jul 2012 15:25:10 -0000 @@ -0,0 +1,400 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifdef GDBSERVER +#include "server.h" +#else +#include "defs.h" +#endif + +#include <string.h> + +#include "format.h" + +struct format_piece * +parse_format_string (char **arg) +{ + char *s, *f, *string; + char *prev_start; + char *percent_loc; + char *sub_start, *current_substring; + struct format_piece *pieces; + int next_frag; + int max_pieces; + enum argclass this_argclass; + + s = *arg; + + /* 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 != '"' && *s != '\0') + { + int c = *s++; + switch (c) + { + case '\0': + continue; + + 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; + } + } + + /* Terminate our escape-processed copy. */ + *f++ = '\0'; + + /* Whether the format string ended with double-quote or zero, we're + done with it; it's up to callers to complain about syntax. */ + *arg = s; + + /* Need extra space for the '\0's. Doubling the size is sufficient. */ + + current_substring = xmalloc (strlen (string) * 2 + 1000); + + max_pieces = strlen (string) + 2; + + pieces = (struct format_piece *) + xmalloc (max_pieces * sizeof (struct format_piece)); + + next_frag = 0; + + /* Now scan the string for %-specs and see what kinds of args they want. + argclass classifies the %-specs so we can give printf-type functions + something of the right size. */ + + f = string; + prev_start = string; + while (*f) + if (*f++ == '%') + { + int seen_hash = 0, seen_zero = 0, lcount = 0, seen_prec = 0; + int seen_space = 0, seen_plus = 0; + int seen_big_l = 0, seen_h = 0, seen_big_h = 0; + int seen_big_d = 0, seen_double_big_d = 0; + int bad = 0; + + /* Skip over "%%", it will become part of a literal piece. */ + if (*f == '%') + { + f++; + continue; + } + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - 1 - prev_start); + current_substring += f - 1 - prev_start; + *current_substring++ = '\0'; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = literal_piece; + next_frag++; + + percent_loc = f - 1; + + /* Check the validity of the format specifier, and work + out what argument it expects. We only accept C89 + format strings, with the exception of long long (which + we autoconf for). */ + + /* The first part of a format specifier is a set of flag + characters. */ + while (strchr ("0-+ #", *f)) + { + if (*f == '#') + seen_hash = 1; + else if (*f == '0') + seen_zero = 1; + else if (*f == ' ') + seen_space = 1; + else if (*f == '+') + seen_plus = 1; + f++; + } + + /* The next part of a format specifier is a width. */ + while (strchr ("0123456789", *f)) + f++; + + /* The next part of a format specifier is a precision. */ + if (*f == '.') + { + seen_prec = 1; + f++; + while (strchr ("0123456789", *f)) + f++; + } + + /* The next part of a format specifier is a length modifier. */ + if (*f == 'h') + { + seen_h = 1; + f++; + } + else if (*f == 'l') + { + f++; + lcount++; + if (*f == 'l') + { + f++; + lcount++; + } + } + else if (*f == 'L') + { + seen_big_l = 1; + f++; + } + /* Decimal32 modifier. */ + else if (*f == 'H') + { + seen_big_h = 1; + f++; + } + /* Decimal64 and Decimal128 modifiers. */ + else if (*f == 'D') + { + f++; + + /* Check for a Decimal128. */ + if (*f == 'D') + { + f++; + seen_double_big_d = 1; + } + else + seen_big_d = 1; + } + + switch (*f) + { + case 'u': + if (seen_hash) + bad = 1; + /* FALLTHROUGH */ + + case 'o': + case 'x': + case 'X': + if (seen_space || seen_plus) + bad = 1; + /* FALLTHROUGH */ + + case 'd': + case 'i': + if (lcount == 0) + this_argclass = int_arg; + else if (lcount == 1) + this_argclass = long_arg; + else + this_argclass = long_long_arg; + + if (seen_big_l) + bad = 1; + break; + + case 'c': + this_argclass = lcount == 0 ? int_arg : wide_char_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'p': + this_argclass = ptr_arg; + if (lcount || seen_h || seen_big_l) + bad = 1; + if (seen_prec || seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 's': + this_argclass = lcount == 0 ? string_arg : wide_string_arg; + if (lcount > 1 || seen_h || seen_big_l) + bad = 1; + if (seen_zero || seen_space || seen_plus) + bad = 1; + break; + + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + if (seen_big_h || seen_big_d || seen_double_big_d) + this_argclass = decfloat_arg; + else if (seen_big_l) + this_argclass = long_double_arg; + else + this_argclass = double_arg; + + if (lcount || seen_h) + bad = 1; + break; + + case '*': + error (_("`*' not supported for precision or width in printf")); + + case 'n': + error (_("Format specifier `n' not supported in printf")); + + case '\0': + error (_("Incomplete format specifier at end of format string")); + + default: + error (_("Unrecognized format specifier '%c' in printf"), *f); + } + + if (bad) + error (_("Inappropriate modifiers to " + "format specifier '%c' in printf"), + *f); + + f++; + + sub_start = current_substring; + + if (lcount > 1 && USE_PRINTF_I64) + { + /* Windows' printf does support long long, but not the usual way. + Convert %lld to %I64d. */ + int length_before_ll = f - percent_loc - 1 - lcount; + + strncpy (current_substring, percent_loc, length_before_ll); + strcpy (current_substring + length_before_ll, "I64"); + current_substring[length_before_ll + 3] = + percent_loc[length_before_ll + lcount]; + current_substring += length_before_ll + 4; + } + else if (this_argclass == wide_string_arg + || this_argclass == wide_char_arg) + { + /* Convert %ls or %lc to %s. */ + int length_before_ls = f - percent_loc - 2; + + strncpy (current_substring, percent_loc, length_before_ls); + strcpy (current_substring + length_before_ls, "s"); + current_substring += length_before_ls + 2; + } + else + { + strncpy (current_substring, percent_loc, f - percent_loc); + current_substring += f - percent_loc; + } + + *current_substring++ = '\0'; + + prev_start = f; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = this_argclass; + next_frag++; + } + + /* Record the remainder of the string. */ + + sub_start = current_substring; + + strncpy (current_substring, prev_start, f - prev_start); + current_substring += f - prev_start; + *current_substring++ = '\0'; + + pieces[next_frag].string = sub_start; + pieces[next_frag].argclass = literal_piece; + next_frag++; + + /* Record an end-of-array marker. */ + + pieces[next_frag].string = NULL; + pieces[next_frag].argclass = literal_piece; + + return pieces; +} + +void +free_format_pieces (struct format_piece *pieces) +{ + if (!pieces) + return; + + /* We happen to know that all the string pieces are in the block + pointed to by the first string piece. */ + if (pieces[0].string) + xfree (pieces[0].string); + + xfree (pieces); +} + +void +free_format_pieces_cleanup (void *ptr) +{ + void **location = ptr; + + if (location == NULL) + return; + + if (*location != NULL) + { + free_format_pieces (*location); + *location = NULL; + } +} + Index: common/format.h =================================================================== RCS file: common/format.h diff -N common/format.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ common/format.h 2 Jul 2012 15:25:10 -0000 @@ -0,0 +1,63 @@ +/* Parse a printf-style format string. + + Copyright (C) 1986-2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#if defined(__MINGW32__) && !defined(PRINTF_HAS_LONG_LONG) +# define USE_PRINTF_I64 1 +# define PRINTF_HAS_LONG_LONG +#else +# define USE_PRINTF_I64 0 +#endif + +/* The argclass represents the general type of data that goes with a + format directive; int_arg for %d, long_arg for %l, and so forth. + Note that these primarily distinguish types by size and need for + special handling, so for instance %u and %x are (at present) also + classed as int_arg. */ + +enum argclass + { + literal_piece, + int_arg, long_arg, long_long_arg, ptr_arg, + string_arg, wide_string_arg, wide_char_arg, + double_arg, long_double_arg, decfloat_arg + }; + +/* A format piece is a section of the format string that may include a + single print directive somewhere in it, and the associated class + for the argument. */ + +struct format_piece +{ + char *string; + enum argclass argclass; +}; + +/* Return an array of printf fragments found at the given string, and + rewrite ARG with a pointer to the end of the format string. */ + +extern struct format_piece *parse_format_string (char **arg); + +/* Given a pointer to an array of format pieces, free any memory that + would have been allocated by parse_format_string. */ + +extern void free_format_pieces (struct format_piece *frags); + +/* Freeing, cast as a cleanup. */ + +extern void free_format_pieces_cleanup (void *); Index: testsuite/gdb.base/dprintf.exp =================================================================== RCS file: /cvs/src/src/gdb/testsuite/gdb.base/dprintf.exp,v retrieving revision 1.3 diff -u -p -r1.3 dprintf.exp --- testsuite/gdb.base/dprintf.exp 15 May 2012 13:36:18 -0000 1.3 +++ testsuite/gdb.base/dprintf.exp 2 Jul 2012 15:25:10 -0000 @@ -85,6 +85,30 @@ if ![target_info exists gdb,noinferiorio "2nd dprintf, fprintf" } +set target_can_dprintf 1 +set msg "Set dprintf style to agent" +gdb_test_multiple "set dprintf-style agent" $msg { + -re "warning: Target cannot run dprintf commands.*" { + set target_can_dprintf 0 + pass "$msg - cannot do" + } + -re ".*$gdb_prompt $" { + pass "$msg - can do" + } +} + +if $target_can_dprintf { + + gdb_run_cmd + + gdb_test "" "Breakpoint" + + gdb_test "continue" "Breakpoint \[0-9\]+, foo .*" "1st dprintf, agent" + + gdb_test "continue" "Breakpoint \[0-9\]+, foo .*" "2nd dprintf, agent" + +} + gdb_test "set dprintf-style foobar" "Undefined item: \"foobar\"." \ "Set dprintf style to an unrecognized type" ^ permalink raw reply [flat|nested] 13+ messages in thread
* 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-02 18:19 ` Stan Shebs @ 2012-07-18 19:18 ` Jan Kratochvil 2012-07-20 2:28 ` Yao Qi 0 siblings, 1 reply; 13+ messages in thread From: Jan Kratochvil @ 2012-07-18 19:18 UTC (permalink / raw) To: Stan Shebs; +Cc: Tom Tromey, gdb-patches On Mon, 02 Jul 2012 20:18:34 +0200, Stan Shebs wrote: > I went ahead and committed with these changes, so we can start > collecting some user experience. 2885fb01788703023acfeb2bade84c20d8723853 is the first bad commit commit 2885fb01788703023acfeb2bade84c20d8723853 Author: Stan Shebs <shebs@apple.com> Date: Mon Jul 2 15:29:29 2012 +0000 Add target-side support for dynamic printf. Running ./gdb.base/pending.exp ... FAIL: gdb.base/pending.exp: continue to resolved breakpoint 2 FAIL: gdb.base/pending.exp: continue to resolved breakpoint 1 FAIL: gdb.base/pending.exp: continue to resolved breakpoint 3 (the program exited) There is somehow missed a breakpoint, IIUC. Thanks, Jan ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-18 19:18 ` 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] Jan Kratochvil @ 2012-07-20 2:28 ` Yao Qi 2012-07-25 19:50 ` Jan Kratochvil 2012-07-27 16:36 ` Pedro Alves 0 siblings, 2 replies; 13+ messages in thread From: Yao Qi @ 2012-07-20 2:28 UTC (permalink / raw) To: gdb-patches; +Cc: Jan Kratochvil, Stan Shebs, Tom Tromey On Wednesday, July 18, 2012 09:17:41 PM Jan Kratochvil wrote: > 2885fb01788703023acfeb2bade84c20d8723853 is the first bad commit > commit 2885fb01788703023acfeb2bade84c20d8723853 > Author: Stan Shebs <shebs@apple.com> > Date: Mon Jul 2 15:29:29 2012 +0000 > Add target-side support for dynamic printf. > > Running ./gdb.base/pending.exp ... > FAIL: gdb.base/pending.exp: continue to resolved breakpoint 2 > FAIL: gdb.base/pending.exp: continue to resolved breakpoint 1 > FAIL: gdb.base/pending.exp: continue to resolved breakpoint 3 (the program > exited) > > There is somehow missed a breakpoint, IIUC. Hi, This regression is caused by the following change, On Monday, July 02, 2012 11:18:34 AM Stan Shebs wrote: > @@ -2916,22 +2918,33 @@ process_point_options (CORE_ADDR point_a > > while (*dataptr) > { > - switch (*dataptr) > + if (*dataptr == ';') > + ++dataptr; > + > + if (*dataptr == 'X') > { > - case 'X': > - /* Conditional expression. */ > - if (remote_debug) > - 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 != ';') It seems incorrect to remove "*dataptr != 'X'" out of this condition checking. With the breakpoint commands added, the Z packet becomes "Z0xxxxXxxxx,Xxxxx;cmds:xxxxx" When parsing this packet, in current (wrong) GDBserver, only the first condition ('Xxxxx') in packet is added, and the rest of conditions are skipped, which is a mistake. The fix is just to revert this change. Regression tested on x86_64/gdbserver, and these fails are fixed. -FAIL: gdb.base/pending.exp: continue to resolved breakpoint 2 -FAIL: gdb.base/pending.exp: continue to resolved breakpoint 1 +PASS: gdb.base/pending.exp: continue to resolved breakpoint 2 +PASS: gdb.base/pending.exp: continue to resolved breakpoint 1 -FAIL: gdb.base/pending.exp: continue to resolved breakpoint 3 (the program exited) +PASS: gdb.base/pending.exp: continue to resolved breakpoint 3 Regression tested on x86_64/native, no changes. -- Yao (齐尧) gdb/gdbserver: 2012-07-20 Yao Qi <yao@codesourcery.com> * server.c (process_point_options): Stop at 'X' when parsing. --- gdb/gdbserver/server.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 963d575..4e15b3c 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -2944,7 +2944,7 @@ process_point_options (CORE_ADDR point_addr, char **packet) } /* Skip tokens until we find one that we recognize. */ - while (*dataptr && *dataptr != ';') + while (*dataptr && *dataptr != 'X' && *dataptr != ';') dataptr++; } *packet = dataptr; -- 1.7.7.6 ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-20 2:28 ` Yao Qi @ 2012-07-25 19:50 ` Jan Kratochvil 2012-07-27 2:32 ` [committed]: " Yao Qi 2012-07-27 16:36 ` Pedro Alves 1 sibling, 1 reply; 13+ messages in thread From: Jan Kratochvil @ 2012-07-25 19:50 UTC (permalink / raw) To: Yao Qi; +Cc: gdb-patches, Stan Shebs, Tom Tromey On Fri, 20 Jul 2012 04:27:59 +0200, Yao Qi wrote: > It seems incorrect to remove "*dataptr != 'X'" out of this condition checking. > > With the breakpoint commands added, the Z packet becomes > > "Z0xxxxXxxxx,Xxxxx;cmds:xxxxx" > > When parsing this packet, in current (wrong) GDBserver, only the first > condition ('Xxxxx') in packet is added, and the rest of conditions are skipped, > which is a mistake. xxxx may contain only hex bytes or ',' so it should be safe. > 2012-07-20 Yao Qi <yao@codesourcery.com> > > * server.c (process_point_options): Stop at 'X' when parsing. I find this change safe for check-in so OK. Thanks for the fix, Jan ^ permalink raw reply [flat|nested] 13+ messages in thread
* [committed]: 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-25 19:50 ` Jan Kratochvil @ 2012-07-27 2:32 ` Yao Qi 0 siblings, 0 replies; 13+ messages in thread From: Yao Qi @ 2012-07-27 2:32 UTC (permalink / raw) To: gdb-patches On Wednesday, July 25, 2012 09:50:17 PM Jan Kratochvil wrote: > > 2012-07-20 Yao Qi <yao@codesourcery.com> > > > > > > > > * server.c (process_point_options): Stop at 'X' when parsing. > > I find this change safe for check-in so OK. > > > Thanks for the fix, Committed to HEAD and 7_5. http://sourceware.org/ml/gdb-cvs/2012-07/msg00228.html http://sourceware.org/ml/gdb-cvs/2012-07/msg00229.html -- Yao (齐尧) ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-20 2:28 ` Yao Qi 2012-07-25 19:50 ` Jan Kratochvil @ 2012-07-27 16:36 ` Pedro Alves 2012-07-28 10:24 ` Yao Qi 1 sibling, 1 reply; 13+ messages in thread From: Pedro Alves @ 2012-07-27 16:36 UTC (permalink / raw) To: Yao Qi; +Cc: gdb-patches On 07/20/2012 03:27 AM, Yao Qi wrote: >> /* Skip tokens until we find one that we recognize. */ >> - while (*dataptr && *dataptr != 'X' && *dataptr != ';') >> + while (*dataptr && *dataptr != ';') > > It seems incorrect to remove "*dataptr != 'X'" out of this condition checking. > > With the breakpoint commands added, the Z packet becomes > > "Z0xxxxXxxxx,Xxxxx;cmds:xxxxx" > > When parsing this packet, in current (wrong) GDBserver, only the first > condition ('Xxxxx') in packet is added, and the rest of conditions are skipped, > which is a mistake. > > The fix is just to revert this change. Regression tested on x86_64/gdbserver, > and these fails are fixed. The original intent of this (before "cmds:" was added) was to skip all the way to the next ';', in case in the future GDB sends some newer component (the things in between ';'s.) We now have "X" and "cmds:". It seems a little better to not try to always skip unknown characters and hard code X, but instead skip characters only when we find we didn't recognize one. Does this look correct to you? No regressions on x86_64 Fedora 17. 2012-07-27 Pedro Alves <palves@redhat.com> * server.c (process_point_options): Only skip tokens if we find one that is unrecognized. Don't treat 'X' specially while skipping unrecognized tokens. --- gdb/gdbserver/server.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 4e15b3c..547552f 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -2938,14 +2938,12 @@ process_point_options (CORE_ADDR point_addr, char **packet) } 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 != ';') + dataptr++; } - - /* Skip tokens until we find one that we recognize. */ - while (*dataptr && *dataptr != 'X' && *dataptr != ';') - dataptr++; } *packet = dataptr; } -- Pedro Alves ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-27 16:36 ` Pedro Alves @ 2012-07-28 10:24 ` Yao Qi 2012-07-28 11:41 ` Yao Qi 0 siblings, 1 reply; 13+ messages in thread From: Yao Qi @ 2012-07-28 10:24 UTC (permalink / raw) To: gdb-patches; +Cc: Pedro Alves On Friday, July 27, 2012 05:36:03 PM Pedro Alves wrote: > @@ -2938,14 +2938,12 @@ process_point_options (CORE_ADDR point_addr, char > **packet) } > 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 != ';') > + dataptr++; > } > - > - /* Skip tokens until we find one that we recognize. */ > - while (*dataptr && *dataptr != 'X' && *dataptr != ';') > - dataptr++; > } > *packet = dataptr; Pedro, I like it. It is better to remove 'X' out of condition checking, so that it is easier to add other new tokens in the future. I am wondering that the original code can skip some "bad" chars in 'X' and 'cmds:' to go to next ';', and I don't know this case does exist. We may add an assertion at the end of 'while (*dataptr)' loop, like this, while (*dataptr) { ... gdb_assert (*dataptr == 0 || *dataptr == ';'); } or we don't have to worry about this at all. -- Yao (齐尧) ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] 2012-07-28 10:24 ` Yao Qi @ 2012-07-28 11:41 ` Yao Qi 0 siblings, 0 replies; 13+ messages in thread From: Yao Qi @ 2012-07-28 11:41 UTC (permalink / raw) To: gdb-patches; +Cc: Pedro Alves On Saturday, July 28, 2012 06:23:41 PM Yao Qi wrote: > I am wondering that the original code can skip some "bad" chars in 'X' and > 'cmds:' to go to next ';', and I don't know this case does exist. We may > add an assertion at the end of 'while (*dataptr)' loop, like this, > > while (*dataptr) > { > ... > gdb_assert (*dataptr == 0 || *dataptr == ';'); > } Sorry, my brain didn't work just now. It is wrong, and please ignore this. -- Yao (齐尧) ^ permalink raw reply [flat|nested] 13+ messages in thread
* Build regression on 64-bit hosts [Re: [PATCH] Dynamic printf for a target agent] 2012-06-25 15:31 ` Stan Shebs 2012-06-27 20:45 ` Tom Tromey @ 2012-07-02 16:41 ` Jan Kratochvil 1 sibling, 0 replies; 13+ messages in thread From: Jan Kratochvil @ 2012-07-02 16:41 UTC (permalink / raw) To: Stan Shebs; +Cc: Yao Qi, gdb-patches 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 921)/* Return true if there are no commands to run at this location, 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 922) which likely means we want to report back to GDB. */ 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 923)int 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 924)gdb_no_commands_at_breakpoint (CORE_ADDR where) 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 925){ 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 926) struct breakpoint *bp = find_gdb_breakpoint_at (where); 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 927) 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 928) if (bp == NULL) 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 929) return 0; 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 930) 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 931) if (debug_threads) 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 932) fprintf (stderr, "at 0x%s, bp command_list is 0x%x\n", 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 933) paddress (where), (int) bp->command_list); mem-break.c:933:25: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast] 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 934) return (bp->command_list == NULL); 2885fb01 (Stan Shebs 2012-07-02 15:29:29 +0000 935)} Thanks, Jan ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2012-07-28 11:41 UTC | newest] Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-05-30 1:10 [PATCH] Dynamic printf for a target agent Stan Shebs 2012-05-31 6:02 ` Yao Qi 2012-06-25 15:31 ` Stan Shebs 2012-06-27 20:45 ` Tom Tromey 2012-07-02 18:19 ` Stan Shebs 2012-07-18 19:18 ` 7.4->7.5 Regression gdb.base/pending.exp with gdbserver [Re: [PATCH] Dynamic printf for a target agent] Jan Kratochvil 2012-07-20 2:28 ` Yao Qi 2012-07-25 19:50 ` Jan Kratochvil 2012-07-27 2:32 ` [committed]: " Yao Qi 2012-07-27 16:36 ` Pedro Alves 2012-07-28 10:24 ` Yao Qi 2012-07-28 11:41 ` Yao Qi 2012-07-02 16:41 ` Build regression on 64-bit hosts " Jan Kratochvil
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox