Add target-side support for dynamic printf.
authorStan Shebs <shebs@codesourcery.com>
Mon, 2 Jul 2012 15:29:39 +0000 (15:29 +0000)
committerStan Shebs <shebs@codesourcery.com>
Mon, 2 Jul 2012 15:29:39 +0000 (15:29 +0000)
* 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.

28 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/ax-gdb.c
gdb/ax-gdb.h
gdb/ax-general.c
gdb/ax.h
gdb/breakpoint.c
gdb/breakpoint.h
gdb/common/ax.def
gdb/common/format.c [new file with mode: 0644]
gdb/common/format.h [new file with mode: 0644]
gdb/doc/ChangeLog
gdb/doc/agentexpr.texi
gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/Makefile.in
gdb/gdbserver/ax.c
gdb/gdbserver/linux-low.c
gdb/gdbserver/mem-break.c
gdb/gdbserver/mem-break.h
gdb/gdbserver/server.c
gdb/printcmd.c
gdb/remote.c
gdb/target.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/dprintf.exp

index 2a910ba..281099b 100644 (file)
@@ -1,3 +1,52 @@
+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.
+
 2012-07-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        Execute -ix and -iex only after system and user gdbinit files.
index bf6b0da..a41cff9 100644 (file)
@@ -745,7 +745,8 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
        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.h gnulib/import/extra/snippet/c++defs.h
 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) $(YYOBJ) \
        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 5cfb48a..a1e3dc8 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -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 1bf03df..031ccfc 100644 (file)
 #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 scope, struct gdbarch *gdbarch)
   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_tty)
   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 09f6889..c357fb6 100644 (file)
@@ -115,6 +115,12 @@ extern void gen_expr (struct expression *exp, union exp_element **pc,
 
 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 4f1ea20..6ea6f14 100644 (file)
@@ -330,6 +330,30 @@ ax_tsv (struct agent_expr *x, enum agent_op op, int num)
   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 agent_expr *x)
          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 d4069f7..368f727 100644 (file)
--- a/gdb/ax.h
+++ b/gdb/ax.h
@@ -219,6 +219,9 @@ extern void ax_reg_mask (struct agent_expr *ax, int reg);
 
 /* 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 06e57b3..555694e 100644 (file)
@@ -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_breakpoint_ops;
 /* 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_location *bl)
   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 *bl,
   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 breakpoint *b,
        }
     }
   
+  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 *loc)
   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 breakpoint *b)
   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 breakpoint *b)
                                  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 breakpoint *b)
     {
       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 printf"), NULL,
                          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 68a9688..4e4f875 100644 (file)
@@ -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 fdc043f..b08ede3 100644 (file)
@@ -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)
diff --git a/gdb/common/format.c b/gdb/common/format.c
new file mode 100644 (file)
index 0000000..9c22b6d
--- /dev/null
@@ -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;
+    }
+}
+
diff --git a/gdb/common/format.h b/gdb/common/format.h
new file mode 100644 (file)
index 0000000..4c6c1b9
--- /dev/null
@@ -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 1b75b4e..c6ff8f9 100644 (file)
@@ -1,3 +1,13 @@
+2012-07-02  Stan Shebs  <stan@codesourcery.com>
+
+       * 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.
+
 2012-07-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * gdb.texinfo (File Options): Change -ix and -iex commands that apply
index d0f6f15..1b0a397 100644 (file)
@@ -493,6 +493,23 @@ Record the bytes at @var{addr} in a trace buffer, for later retrieval
 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 83ce123..d324a93 100644 (file)
@@ -4649,6 +4649,14 @@ program's @code{printf} function.  This has the advantage that the
 characters go to the program's output device, so they can recorded in
 redirects to files and so forth.
 
+If you are doing remote debugging with a stub or agent, you can also
+ask to have the printf handled by the remote agent.  In addition to
+ensuring that the output goes to the remote program's device along
+with any other output the program might produce, you can also ask that
+the dprintf remain active even after disconnecting from the remote
+target.  Using the stub/agent is also more efficient, as it can do
+everything without needing to communicate with @value{GDBN}.
+
 @table @code
 @kindex dprintf
 @item dprintf @var{location},@var{template},@var{expression}[,@var{expression}@dots{}]
@@ -4672,6 +4680,12 @@ Handle the output using the @value{GDBN} @code{printf} command.
 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 the dynamic printf commands
 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 @code{globb}, while discarding
 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 packets, the operations should
 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 bytecode form.
 
 @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 features and their properties:
 @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 experiment is running.
 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 683aade..02a2398 100644 (file)
@@ -1,3 +1,31 @@
+2012-07-02  Stan Shebs  <stan@codesourcery.com>
+
+       * 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.
+
 2012-06-13  Yao Qi  <yao@codesourcery.com>
 
        * server.c (start_inferior): Remove duplicated writes to fields
index 3c5c041..446ea04 100644 (file)
@@ -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} ${ALLPARAM} ${POSSLIBS}
 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_CFLAGS) $(UST_CFLAGS) \
 
 # 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 $(regdef_h)
        $(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 4075c26..70e9322 100644 (file)
@@ -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 *aexpr)
 
 #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 *regcache,
            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 3d116fd..48134c3 100644 (file)
@@ -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_entry *entry, void *dummy)
         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 6b6b25c..aec09ba 100644 (file)
@@ -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_ADDR where)
   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 95b7f9d..bb2aa03 100644 (file)
@@ -52,11 +52,19 @@ void clear_gdb_breakpoint_conditions (CORE_ADDR addr);
 
 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 69b54d9..963d575 100644 (file)
@@ -1685,8 +1685,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
          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_addr, char **packet)
 
   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 030a4f2..2a0a886 100644 (file)
 #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 *name, struct symbol *var,
 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 *stream)
   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;
-
-       default:
-         *f++ = c;
-       }
-    }
+  make_cleanup (free_format_pieces_cleanup, &fpieces);
 
-  /* 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 *stream)
     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 *stream)
       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 *stream)
 
              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 01e3f30..1c9367d 100644 (file)
@@ -242,6 +242,8 @@ static void remote_console_output (char *msg);
 
 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 struct protocol_feature *feature,
 }
 
 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_protocol_features[] = {
     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 gdbarch *gdbarch,
   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 *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 gdbarch *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);
 
@@ -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 connected to\n\
   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 bits) in a memory packet."), NULL,
                         "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 fe1f9a1..bb8eae8 100644 (file)
@@ -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 e5bdf4d..54c58d6 100644 (file)
@@ -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_randomization (void);
 #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 44673d4..01cc53b 100644 (file)
@@ -1,3 +1,7 @@
+2012-07-02  Stan Shebs  <stan@codesourcery.com>
+
+       * gdb.base/dprintf.exp: Add agent style tests.
+
 2012-07-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * gdb.base/stale-infcall.c (infcall): New label test-next.
index 6d1ce6c..1e9d017 100644 (file)
@@ -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"