Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Stan Shebs <stanshebs@earthlink.net>
To: Tom Tromey <tromey@redhat.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH] Dynamic printf for a target agent
Date: Mon, 02 Jul 2012 18:19:00 -0000	[thread overview]
Message-ID: <4FF1E5FA.2010801@earthlink.net> (raw)
In-Reply-To: <87mx3o5vsk.fsf@fleche.redhat.com>

[-- 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"
 

  reply	other threads:[~2012-07-02 18:19 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-05-30  1:10 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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4FF1E5FA.2010801@earthlink.net \
    --to=stanshebs@earthlink.net \
    --cc=gdb-patches@sourceware.org \
    --cc=tromey@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox