Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Stan Shebs <stanshebs@earthlink.net>
To: gdb-patches@sourceware.org
Subject: [PATCH] Dynamic printf for a target agent
Date: Wed, 30 May 2012 01:10:00 -0000	[thread overview]
Message-ID: <4FC57340.6070306@earthlink.net> (raw)

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

             reply	other threads:[~2012-05-30  1:10 UTC|newest]

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

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=4FC57340.6070306@earthlink.net \
    --to=stanshebs@earthlink.net \
    --cc=gdb-patches@sourceware.org \
    /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