Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Stan Shebs <stanshebs@earthlink.net>
To: gdb-patches@sourceware.org
Subject: [PATCH] dynamic printf
Date: Wed, 29 Feb 2012 09:14:00 -0000	[thread overview]
Message-ID: <4F4DCDD5.2040807@earthlink.net> (raw)

[-- Attachment #1: Type: text/plain, Size: 5023 bytes --]

This patch implements a "dynamic printf", which is basically a 
breakpoint with a printf;continue as its command list - but with 
additional features that make it more interesting.  First, there is an 
option to send the result to the inferior's output stream rather than to 
the GDB console; second, there is an option to do the printing in a 
remote agent/stub; and third, the agent version can continue printf'ing 
even after GDB detaches(!).

(For those who pay attention to the competitive landscape, this is an 
Ericsson-requested GDBification of a Wind River feature.)

The syntax of the command is

     dprintf <location> <format>,<arg>,<arg>...

where the location is as for breakpoints, while the format and args are 
as for the printf command.  So you could have something like

     dprintf myfun "myfun args are %d,%d\n",arg1,arg2

A setshow variable dprintf-style controls whether the printing is done 
by GDB, the inferior, or by a remote agent, and another variable 
disconnected-dprintf controls persistence upon disconnection, similarly 
to disconnected tracing.

The patch itself is somewhat of a hack-n-slash through the middle of 
GDB, and there is much to critique. :-)  Partly this is because I wanted 
to push through to a genuinely useful example of target-side breakpoint 
commands, rather than doing limited-functionality "commands" in the 
abstract.  It also points up our continuing need for being able to 
define types of breakpoints by properties rather than adding enums...  I 
borrowed some of Luis' target-side conditional code, and got some 
inspiration from Hui Zhu's previous try at a printf bytecode.

Anyway, without further ado, here's the patch itself.  I've skipped the 
testsuite for now, as I expect everybody will want nontrivial 
modifications. :-)

Stan

2012-02-28  Stan Shebs <stan@codesourcery.com>

     Add dynamic printf.
     * breakpoint.h (enum bptype): New type bp_dprintf.
     (struct bp_target_info): New field commands and persist.
     (struct bp_location): New field cmd_bytecode.
     (struct breakpoint): New field extra_string.
     * breakpoint.c (dprintf_breakpoint_ops): New.
     (disconnected_dprintf): New global.
     (is_breakpoint): Add bp_dprintf.
     (parse_format_string): New function.
     (parse_cmd_to_aexpr): New function.
     (build_target_command_list): New function.
     (insert_bp_location): Call it.
     (remove_breakpoints_pid): Skip dprintf breakpoints.
     (bpstat_what): Add dprintf case.
     (bptype_string): Ditto.
     (print_one_breakpoint_location): Ditto.
     (init_bp_location): Ditto.
     (bkpt_print_mention): Ditto.
     (dprintf_style_enums): New array.
     (dprintf_style): New global.
     (glob_extra_string): New global.
     (init_breakpoint_sal): Handle extra string.
     (addr_string_to_sals): Ditto.
     (find_condition_and_thread): Add extra argument.
     (create_breakpoint): Save away additional text at end of command.
     (dprintf_command): New function.
     (_initialize_breakpoint): Add new commands.
     * common/ax.def (printf): New bytecode.
     * ax-gdb.c (gen_printf): New function.
     (agent_print_command): New function.
     (_initialize_ax_gdb): Add maint agent-printf command.
     * ax-gdb.h (gen_printf): Declare.
     * ax-general.c (ax_print): Add printf disassembly.
     * remote.c (remote_state) <cond_breakpoints>: New field.
     (PACKET_BreakpointCommandss): New enum.
     (remote_breakpoint_commands_feature): New function.
     (remote_protocol_features): Add new BreakpointCommands entry.
     (remote_can_run_breakpoint_commands): New function.
     (remote_add_target_side_commands): New function.
     (remote_insert_breakpoint): Call it.
     (remote_insert_hw_breakpoint): Ditto.
     (_initialize_remote): Add new packet configuration for
     target-side breakpoint commands.
     * target.h (struct target_ops): New field
     to_can_run_breakpoint_commands.
     (target_can_run_breakpoint_commands): New macro.
     * target.c (update_current_target): Handle
     to_can_run_breakpoint_commands.

     [gdbserver]
     * server.c (handle_query): Claim support for breakpoint commands.
     (process_point_options): Add command case.
     (process_serial_event): Leave running if there are printfs in
     effect.
     * mem-break.c (struct point_command_list): New struct.
     (struct breakpoint): New field command_list.
     (any_persistent_commands): New function.
     (add_commands_to_breakpoint): New function.
     (add_breakpoint_commands): New function.
     (gdb_no_commands_at_breakpoint): New function.
     (run_breakpoint_commands): New function.
     * mem-break.h (any_persistent_commands): Declare.
     (add_breakpoint_commands): Declare.
     (gdb_no_commands_at_breakpoint): Declare.
     (run_breakpoint_commands): Declare.
     * linux-low.c (linux_wait_1): Test for and run breakpoint commands
     locally.
     * ax.c (gdb_eval_agent_expr): Add printf opcode.

     [doc]
     * gdb.texinfo (Dynamic Printf): New subsection.


[-- Attachment #2: dprintf-patch-1 --]
[-- Type: text/plain, Size: 46628 bytes --]

Index: ax-gdb.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.c,v
retrieving revision 1.92
diff -u -p -r1.92 ax-gdb.c
--- ax-gdb.c	7 Feb 2012 04:48:18 -0000	1.92
+++ ax-gdb.c	29 Feb 2012 05:52:13 -0000
@@ -2511,6 +2511,55 @@ gen_trace_for_return_address (CORE_ADDR 
   return ax;
 }
 
+/* Given a collection of printf-style arguments, generate code to
+   evaluate the arguments and pass everything to a special
+   bytecode.  */
+
+struct agent_expr *
+gen_printf (CORE_ADDR scope, int channel, char *format,
+	    int nargs, struct expression **exprs)
+{
+  struct expression *expr;
+  struct cleanup *old_chain = 0;
+  struct agent_expr *ax = new_agent_expr (exprs[0]->gdbarch, scope);
+  union exp_element *pc;
+  struct axs_value value;
+  int tem, bot;
+
+  old_chain = make_cleanup_free_agent_expr (ax);
+
+  /* Evaluate and push the args on the stack in reverse order,
+     for simplicity of collecting them on the target side.  */
+  for (tem = nargs - 1; tem >= 0; --tem)
+    {
+      pc = exprs[tem]->elts;
+      /* We're computing values, not doing side effects.  */
+      trace_kludge = 0;
+      value.optimized_out = 0;
+      gen_expr (exprs[tem], &pc, ax, &value);
+      require_rvalue (ax, &value);
+    }
+
+  ax_simple (ax, aop_printf);
+  ax_simple (ax, channel);
+  ax_simple (ax, nargs);
+  /* FIXME define a string-adder in ax-general.c */
+  ax_simple (ax, strlen (format) + 1);
+  while (*format != '\0')
+    ax_simple (ax, *format++);
+  ax_simple (ax, '\0');
+
+  /* Oh, and terminate.  */
+  ax_simple (ax, aop_end);
+
+  /* We have successfully built the agent expr, so cancel the cleanup
+     request.  If we add more cleanups that we always want done, this
+     will have to get more complicated.  */
+  discard_cleanups (old_chain);
+
+  return ax;
+}
+
 static void
 agent_command (char *exp, int from_tty)
 {
@@ -2594,6 +2643,66 @@ agent_eval_command (char *exp, int from_
   do_cleanups (old_chain);
   dont_repeat ();
 }
+/* Parse the given expression, compile it into an agent expression
+   that does a printf, and display the resulting expression.  */
+
+extern char *parse_format_string (char **arg);
+
+static void
+agent_printf_command (char *exp, int from_tty)
+{
+  struct cleanup *old_chain = 0;
+  struct expression *expr;
+  struct expression *argvec[100];
+  struct agent_expr *agent;
+  struct frame_info *fi = get_current_frame ();	/* need current scope */
+  char *cmdrest, *string;
+  int nargs;
+
+  /* We don't deal with overlay debugging at the moment.  We need to
+     think more carefully about this.  If you copy this code into
+     another command, change the error message; the user shouldn't
+     have to know anything about agent expressions.  */
+  if (overlay_debugging)
+    error (_("GDB can't do agent expression translation with overlays."));
+
+  if (exp == 0)
+    error_no_arg (_("expression to translate"));
+
+  cmdrest = exp;
+
+  string = parse_format_string (&cmdrest);
+
+  nargs = 0;
+  while (*cmdrest != '\0')
+    {
+      char *cmd1;
+
+      cmd1 = cmdrest;
+      expr = parse_exp_1 (&cmd1, (struct block *) 0, 1);
+      argvec[nargs] = expr;
+      ++nargs;
+      cmdrest = cmd1;
+      if (*cmdrest == ',')
+	++cmdrest;
+    }
+
+#if 0
+  old_chain = make_cleanup (free_current_contents, &expr);
+#endif
+  agent = gen_printf (get_frame_pc (fi), 0, string, nargs, argvec);
+  make_cleanup_free_agent_expr (agent);
+  ax_reqs (agent);
+  ax_print (gdb_stdout, agent);
+
+  /* It would be nice to call ax_reqs here to gather some general info
+     about the expression, and then print out the result.  */
+
+#if 0
+  do_cleanups (old_chain);
+#endif
+  dont_repeat ();
+}
 \f
 
 /* Initialization code.  */
@@ -2611,4 +2720,9 @@ _initialize_ax_gdb (void)
 	   _("Translate an expression into remote "
 	     "agent bytecode for evaluation."),
 	   &maintenancelist);
+
+  add_cmd ("agent-printf", class_maintenance, agent_printf_command,
+	   _("Translate an expression into remote "
+	     "agent bytecode for evaluation."),
+	   &maintenancelist);
 }
Index: ax-gdb.h
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.h,v
retrieving revision 1.23
diff -u -p -r1.23 ax-gdb.h
--- ax-gdb.h	4 Jan 2012 08:16:59 -0000	1.23
+++ ax-gdb.h	29 Feb 2012 05:52:13 -0000
@@ -110,6 +110,9 @@ extern struct agent_expr *gen_trace_for_
 
 extern struct agent_expr *gen_eval_for_expr (CORE_ADDR, struct expression *);
 
+extern struct agent_expr *gen_printf (CORE_ADDR, int, char *,
+				      int, struct expression **);
+
 extern int trace_kludge;
 extern int trace_string_kludge;
 
Index: ax-general.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-general.c,v
retrieving revision 1.29
diff -u -p -r1.29 ax-general.c
--- ax-general.c	4 Jan 2012 08:16:59 -0000	1.29
+++ ax-general.c	29 Feb 2012 05:52:13 -0000
@@ -391,6 +391,19 @@ ax_print (struct ui_file *f, struct agen
 	  print_longest (f, 'd', 0,
 			 read_const (x, i + 1, aop_map[op].op_size));
 	}
+      /* Handle the complicated printf arguments specially.  */
+      else if (op == aop_printf)
+	{
+	  int chan, slen, nargs;
+
+	  ++i;
+	  chan = x->buf[i++];
+	  nargs = x->buf[i++];
+	  slen = x->buf[i++];
+	  fprintf_filtered (f, " %d \"%s\", %d args",
+			    chan, &(x->buf[i]), nargs);
+	  i += slen - 1;
+	}
       fprintf_filtered (f, "\n");
       i += 1 + aop_map[op].op_size;
 
Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.657
diff -u -p -r1.657 breakpoint.c
--- breakpoint.c	24 Feb 2012 16:26:36 -0000	1.657
+++ breakpoint.c	29 Feb 2012 05:52:13 -0000
@@ -290,6 +290,12 @@ static struct breakpoint_ops momentary_b
    breakpoints.  */
 struct breakpoint_ops bkpt_breakpoint_ops;
 
+static struct breakpoint_ops dprintf_breakpoint_ops;
+
+/* True if dprintf commands should continue to operate even if GDB
+   has disconnected.  */
+static int disconnected_dprintf = 1;
+
 /* A reference-counted struct command_line.  This lets multiple
    breakpoints share a single command list.  */
 struct counted_command_line
@@ -1480,7 +1486,8 @@ int
 is_breakpoint (const struct breakpoint *bpt)
 {
   return (bpt->type == bp_breakpoint
-	  || bpt->type == bp_hardware_breakpoint);
+	  || bpt->type == bp_hardware_breakpoint
+	  || bpt->type == bp_dprintf);
 }
 
 /* Return true if BPT is of any hardware watchpoint kind.  */
@@ -2062,6 +2069,247 @@ build_target_condition_list (struct bp_l
   return;
 }
 
+char *
+parse_format_string (char **arg)
+{
+  char *s, *f, *string;
+
+  s = *arg;
+
+  s = skip_spaces (s);
+
+  /* A format string should follow, enveloped in double quotes.  */
+  if (*s++ != '"')
+    error (_("Bad format string, missing '\"'."));
+
+  /* Parse the format-control string and copy it into the string STRING,
+     processing some kinds of escape sequence.  */
+
+  f = string = (char *) alloca (strlen (s) + 1);
+
+  while (*s != '"')
+    {
+      int c = *s++;
+      switch (c)
+	{
+	case '\0':
+	  error (_("Bad format string, non-terminated '\"'."));
+
+	case '\\':
+	  switch (c = *s++)
+	    {
+	    case '\\':
+	      *f++ = '\\';
+	      break;
+	    case 'a':
+	      *f++ = '\a';
+	      break;
+	    case 'b':
+	      *f++ = '\b';
+	      break;
+	    case 'f':
+	      *f++ = '\f';
+	      break;
+	    case 'n':
+	      *f++ = '\n';
+	      break;
+	    case 'r':
+	      *f++ = '\r';
+	      break;
+	    case 't':
+	      *f++ = '\t';
+	      break;
+	    case 'v':
+	      *f++ = '\v';
+	      break;
+	    case '"':
+	      *f++ = '"';
+	      break;
+	    default:
+	      /* ??? TODO: handle other escape sequences.  */
+	      error (_("Unrecognized escape character \\%c in format string."),
+		     c);
+	    }
+	  break;
+
+	default:
+	  *f++ = c;
+	}
+    }
+
+  /* Skip over " and following space and comma.  */
+  s++;
+  *f++ = '\0';
+  s = skip_spaces (s);
+
+  if (*s != ',' && *s != 0)
+    error (_("Invalid argument syntax"));
+
+  if (*s == ',')
+    s++;
+  s = skip_spaces (s);
+ 
+  *arg = s;
+
+  return xstrdup (string);
+}
+
+/* Parses a command described by string CMD into an agent expression
+   bytecode suitable for evaluation by the bytecode interpreter.
+   Return NULL if there was any error during parsing.  */
+
+struct agent_expr *
+parse_cmd_to_aexpr (CORE_ADDR scope, char *cmd)
+{
+  struct expression *expr;
+  struct expression *argvec[100];
+  struct agent_expr *aexpr = NULL;
+  struct cleanup *old_chain = NULL;
+  volatile struct gdb_exception ex;
+  char *cmdrest, *string;
+  int nargs;
+
+  if (!cmd)
+    return NULL;
+
+  /* For each argument, make an expression.  */
+
+  cmdrest = cmd;
+
+  string = parse_format_string (&cmdrest);
+
+  /* (should get comma separator here) */
+
+  nargs = 0;
+  while (*cmdrest != '\0')
+    {
+      char *cmd1;
+
+      cmd1 = cmdrest;
+      expr = parse_exp_1 (&cmd1, (struct block *) 0, 1);
+      argvec[nargs++] = expr;
+      cmdrest = cmd1;
+      if (*cmdrest == ',')
+	++cmdrest;
+    }
+
+  /* We don't want to stop processing, so catch any errors
+     that may show up.  */
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      aexpr = gen_printf (scope, 0, string, nargs, argvec);
+    }
+
+  if (ex.reason < 0)
+    {
+      /* If we got here, it means the condition could not be parsed to a valid
+	 bytecode expression and thus can't be evaluated on the target's side.
+	 It's no use iterating through the conditions.  */
+      return NULL;
+    }
+
+  /* We have a valid agent expression.  */
+  return aexpr;
+}
+
+/* Based on location BL, create a list of breakpoint commands to be
+   passed on to the target.  If we have duplicated locations with
+   different commands, we will add any such to the list.  */
+
+static void
+build_target_command_list (struct bp_location *bl)
+{
+  struct bp_location **locp = NULL, **loc2p;
+  int null_command_or_parse_error = 0;
+  int modified = bl->needs_update;
+  struct bp_location *loc;
+
+  /* For now, limit to dprintf breakpoints.  */
+  if (bl->owner->type != bp_dprintf)
+    return;
+
+  if (!target_can_run_breakpoint_commands ())
+    return;
+
+  /* Do a first pass to check for locations with no assigned
+     conditions or conditions that fail to parse to a valid agent expression
+     bytecode.  If any of these happen, then it's no use to send conditions
+     to the target since this location will always trigger and generate a
+     response back to GDB.  */
+  ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
+    {
+      loc = (*loc2p);
+      if (is_breakpoint (loc->owner) && loc->pspace->num == bl->pspace->num)
+	{
+	  if (modified)
+	    {
+	      struct agent_expr *aexpr;
+
+	      /* Re-parse the conditions since something changed.  In that
+		 case we already freed the condition bytecodes (see
+		 force_breakpoint_reinsertion).  We just
+		 need to parse the condition to bytecodes again.  */
+	      aexpr = parse_cmd_to_aexpr (bl->address,
+					  loc->owner->extra_string);
+	      loc->cmd_bytecode = aexpr;
+
+	      if (!aexpr)
+		continue;
+	    }
+
+	  /* If we have a NULL bytecode expression, it means something
+	     went wrong or we have a null condition expression.  */
+	  if (!loc->cmd_bytecode)
+	    {
+	      null_command_or_parse_error = 1;
+	      break;
+	    }
+	}
+    }
+
+  /* If anything failed, then we're not doing target-side commands,
+     and so clean up.  */
+  if (null_command_or_parse_error)
+    {
+      ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
+	{
+	  loc = (*loc2p);
+	  if (is_breakpoint (loc->owner)
+	      && loc->pspace->num == bl->pspace->num)
+	    {
+	      /* Only go as far as the first NULL bytecode is
+		 located.  */
+	      if (!loc->cond_bytecode)
+		return;
+
+	      free_agent_expr (loc->cond_bytecode);
+	      loc->cond_bytecode = NULL;
+	    }
+	}
+    }
+
+  /* No NULL commands or failed bytecode generation.  Build a condition list
+     for this location's address.  */
+  ALL_BP_LOCATIONS_AT_ADDR (loc2p, locp, bl->address)
+    {
+      loc = (*loc2p);
+      if (loc->owner->extra_string
+	  && is_breakpoint (loc->owner)
+	  && loc->pspace->num == bl->pspace->num
+	  && loc->owner->enable_state == bp_enabled
+	  && loc->enabled)
+	/* Add the command to the vector.  This will be used later
+	   to send the conditions to the target.  */
+	VEC_safe_push (agent_expr_p, bl->target_info.commands,
+		       loc->cmd_bytecode);
+    }
+
+  bl->target_info.persist = 0;
+  /* Maybe flag this location as persistent.  */
+  if (bl->owner->type == bp_dprintf && disconnected_dprintf)
+    bl->target_info.persist = 1;
+}
+
 /* Insert a low-level "breakpoint" of some type.  BL is the breakpoint
    location.  Any error messages are printed to TMP_ERROR_STREAM; and
    DISABLED_BREAKS, and HW_BREAKPOINT_ERROR are used to report problems.
@@ -2095,7 +2343,8 @@ insert_bp_location (struct bp_location *
   if (is_breakpoint (bl->owner))
     {
       build_target_condition_list (bl);
-      /* Reset the condition modification marker.  */
+      build_target_command_list (bl);
+      /* Reset the modification marker.  */
       bl->needs_update = 0;
     }
 
@@ -2601,6 +2850,9 @@ remove_breakpoints_pid (int pid)
     if (bl->pspace != inf->pspace)
       continue;
 
+    if (bl->owner->type == bp_dprintf)
+      continue;
+
     if (bl->inserted)
       {
 	val = remove_breakpoint (bl, mark_uninserted);
@@ -5054,6 +5306,11 @@ bpstat_what (bpstat bs_head)
 	     PC of the former breakpoint.  */
 	  this_action = BPSTAT_WHAT_KEEP_CHECKING;
 	  break;
+
+	case bp_dprintf:
+	  this_action = BPSTAT_WHAT_STOP_SILENT;
+	  break;
+
 	default:
 	  internal_error (__FILE__, __LINE__,
 			  _("bpstat_what: unhandled bptype %d"), (int) bptype);
@@ -5318,6 +5575,7 @@ bptype_string (enum bptype type)
     {bp_tracepoint, "tracepoint"},
     {bp_fast_tracepoint, "fast tracepoint"},
     {bp_static_tracepoint, "static tracepoint"},
+    {bp_dprintf, "dprintf"},
     {bp_jit_event, "jit events"},
     {bp_gnu_ifunc_resolver, "STT_GNU_IFUNC resolver"},
     {bp_gnu_ifunc_resolver_return, "STT_GNU_IFUNC resolver return"},
@@ -5458,6 +5716,7 @@ print_one_breakpoint_location (struct br
       case bp_tracepoint:
       case bp_fast_tracepoint:
       case bp_static_tracepoint:
+      case bp_dprintf:
       case bp_jit_event:
       case bp_gnu_ifunc_resolver:
       case bp_gnu_ifunc_resolver_return:
@@ -5635,6 +5894,15 @@ print_one_breakpoint_location (struct br
 	}
     }
   
+  if (!part_of_multiple && b->extra_string
+      && b->type == bp_dprintf && !b->commands)
+    {
+      annotate_field (7);
+      ui_out_text (uiout, "\t(agent printf) ");
+      ui_out_field_string (uiout, "printf", b->extra_string);
+      ui_out_text (uiout, "\n");
+    }
+
   l = b->commands ? b->commands->commands : NULL;
   if (!part_of_multiple && l)
     {
@@ -6320,6 +6588,7 @@ init_bp_location (struct bp_location *lo
     case bp_exception_master:
     case bp_gnu_ifunc_resolver:
     case bp_gnu_ifunc_resolver_return:
+    case bp_dprintf:
       loc->loc_type = bp_loc_software_breakpoint;
       mark_breakpoint_location_modified (loc);
       break;
@@ -8192,7 +8461,19 @@ bp_loc_is_permanent (struct bp_location 
   return retval;
 }
 
+static const char dprintf_style_gdb[] = "gdb";
+static const char dprintf_style_call[] = "call";
+static const char dprintf_style_agent[] = "agent";
+static const char *const dprintf_style_enums[] = {
+  dprintf_style_gdb,
+  dprintf_style_call,
+  dprintf_style_agent,
+  NULL
+};
+static const char *dprintf_style = dprintf_style_gdb;
 
+/* Temporary hack to smuggle remainder of command line through.  */
+char *glob_extra_string = NULL;
 
 /* Create a breakpoint with SAL as location.  Use ADDR_STRING
    as textual description of the location, and COND_STRING
@@ -8247,6 +8528,7 @@ init_breakpoint_sal (struct breakpoint *
 	  b->task = task;
   
 	  b->cond_string = cond_string;
+	  b->extra_string = glob_extra_string;
 	  b->ignore_count = ignore_count;
 	  b->enable_state = enabled ? bp_enabled : bp_disabled;
 	  b->disposition = disposition;
@@ -8306,6 +8588,40 @@ init_breakpoint_sal (struct breakpoint *
 	  if (*arg)
               error (_("Garbage %s follows condition"), arg);
 	}
+
+      if (b->extra_string)
+	{
+	  char *printf_line = NULL;
+
+	  if (strcmp (dprintf_style, "gdb") == 0)
+	    printf_line = xstrprintf ("printf %s", b->extra_string);
+	  else if (strcmp (dprintf_style, "call") == 0)
+	    printf_line = xstrprintf ("call printf(%s)", b->extra_string);
+	  else if (strcmp (dprintf_style, "agent") == 0)
+	    /* Nothing to do here.  */ ;
+	  else
+	    internal_error (__FILE__, __LINE__,
+			    _("Invalid dprintf style."));
+
+	  if (printf_line)
+	    {
+	      struct command_line *printf_cmd_line, *cont_cmd_line;
+
+	      cont_cmd_line = xmalloc (sizeof (struct command_line));
+	      cont_cmd_line->body_count = 0;
+	      cont_cmd_line->control_type = simple_control;
+	      cont_cmd_line->next = NULL;
+	      cont_cmd_line->line = "continue";
+
+	      printf_cmd_line = xmalloc (sizeof (struct command_line));
+	      printf_cmd_line->body_count = 0;
+	      printf_cmd_line->control_type = simple_control;
+	      printf_cmd_line->next = cont_cmd_line;
+	      printf_cmd_line->line = printf_line;
+
+	      breakpoint_set_commands (b, printf_cmd_line);
+	    }
+	}
     }   
 
   b->display_canonical = display_canonical;
@@ -8536,7 +8852,8 @@ check_fast_tracepoint_sals (struct gdbar
    If no thread is found, *THREAD is set to -1.  */
 static void 
 find_condition_and_thread (char *tok, CORE_ADDR pc, 
-			   char **cond_string, int *thread, int *task)
+			   char **cond_string, int *thread, int *task,
+			   char **rest)
 {
   *cond_string = NULL;
   *thread = -1;
@@ -8549,6 +8866,12 @@ find_condition_and_thread (char *tok, CO
 
       tok = skip_spaces (tok);
       
+      if (*tok == '"' && rest)
+	{
+	  *rest = savestring (tok, strlen (tok));
+	  return;
+	}
+
       end_tok = skip_to_space (tok);
       
       toklen = end_tok - tok;
@@ -8588,6 +8911,11 @@ find_condition_and_thread (char *tok, CO
 	  if (!valid_task_id (*task))
 	    error (_("Unknown task %d."), *task);
 	}
+      else if (rest)
+	{
+	  *rest = savestring (tok, toklen);
+	  tok += toklen;
+	}
       else
 	error (_("Junk at end of arguments."));
     }
@@ -8673,6 +9001,7 @@ create_breakpoint (struct gdbarch *gdbar
   int pending = 0;
   int task = 0;
   int prev_bkpt_count = breakpoint_count;
+  char *extra_string = NULL;
 
   gdb_assert (ops != NULL);
 
@@ -8776,16 +9105,22 @@ create_breakpoint (struct gdbarch *gdbar
 
       if (parse_condition_and_thread)
         {
+	  char *rest;
             /* Here we only parse 'arg' to separate condition
                from thread number, so parsing in context of first
                sal is OK.  When setting the breakpoint we'll 
                re-parse it in context of each sal.  */
             cond_string = NULL;
             thread = -1;
+	    rest = NULL;
             find_condition_and_thread (arg, lsal->sals.sals[0].pc, &cond_string,
-                                       &thread, &task);
+                                       &thread, &task, &rest);
             if (cond_string)
                 make_cleanup (xfree, cond_string);
+	    if (rest)
+	      make_cleanup (xfree, rest);
+	    if (rest)
+	      extra_string = rest;
         }
       else
         {
@@ -8797,6 +9132,7 @@ create_breakpoint (struct gdbarch *gdbar
             }
         }
 
+      glob_extra_string = extra_string;
       ops->create_breakpoints_sal (gdbarch, &canonical, lsal,
 				   cond_string, type_wanted,
 				   tempflag ? disp_del : disp_donttouch,
@@ -8944,6 +9280,21 @@ break_command (char *arg, int from_tty)
 }
 
 void
+dprintf_command (char *arg, int from_tty)
+{
+  create_breakpoint (get_current_arch (),
+		     arg,
+		     NULL, 0, 1 /* parse arg */,
+		     0, bp_dprintf,
+		     0 /* Ignore count */,
+		     pending_break_support,
+		     &dprintf_breakpoint_ops,
+		     from_tty,
+		     1 /* enabled */,
+		     0 /* internal */);
+}
+
+void
 tbreak_command (char *arg, int from_tty)
 {
   break_command_1 (arg, BP_TEMPFLAG, from_tty);
@@ -12132,6 +12483,9 @@ bkpt_print_mention (struct breakpoint *b
     case bp_hardware_breakpoint:
       printf_filtered (_("Hardware assisted breakpoint %d"), b->number);
       break;
+    case bp_dprintf:
+      printf_filtered (_("Dprintf %d"), b->number);
+      break;
     }
 
   say_where (b);
@@ -13151,13 +13505,17 @@ addr_string_to_sals (struct breakpoint *
 	  char *cond_string = 0;
 	  int thread = -1;
 	  int task = 0;
+	  char *extra_string = NULL;
 
 	  find_condition_and_thread (s, sals.sals[0].pc,
-				     &cond_string, &thread, &task);
+				     &cond_string, &thread, &task,
+				     &extra_string);
 	  if (cond_string)
 	    b->cond_string = cond_string;
 	  b->thread = thread;
 	  b->task = task;
+	  if (extra_string)
+	    b->extra_string = extra_string;
 	  b->condition_not_parsed = 0;
 	}
 
@@ -14946,6 +15304,14 @@ initialize_breakpoint_ops (void)
   ops->print_one = print_one_catch_solib;
   ops->print_mention = print_mention_catch_solib;
   ops->print_recreate = print_recreate_catch_solib;
+
+  ops = &dprintf_breakpoint_ops;
+  *ops = bkpt_base_breakpoint_ops;
+  ops->re_set = bkpt_re_set;
+  ops->resources_needed = bkpt_resources_needed;
+  ops->print_it = bkpt_print_it;
+  ops->print_mention = bkpt_print_mention;
+  ops->print_recreate = bkpt_print_recreate;
 }
 
 void
@@ -15524,6 +15890,31 @@ The breakpoint will stop execution of th
 an instruction at any address within the [START-LOCATION, END-LOCATION]\n\
 range (including START-LOCATION and END-LOCATION)."));
 
+  c = add_com ("dprintf", class_breakpoint, dprintf_command, _("\
+Set a dynamic printf at specified line or function.\n"
+BREAK_ARGS_HELP ("dprintf")));
+  set_cmd_completer (c, location_completer);
+
+  add_setshow_enum_cmd ("dprintf-style", class_support,
+			dprintf_style_enums, &dprintf_style, _("\
+Set the style of usage for dynamic printf."), _("\
+Show the style of usage for dynamic printf."), _("\
+Choose the style of usage for dynamic printf.\n\
+foo"),
+			NULL,
+			NULL,
+			&setlist, &showlist);
+
+  add_setshow_boolean_cmd ("disconnected-dprintf", no_class,
+			   &disconnected_dprintf, _("\
+Set whether dprintf continues after GDB disconnects."), _("\
+Show whether dprintf continues after GDB disconnects."), _("\
+Use this to let dprintf commands continue to hit and produce output\n\
+even if GDB disconnects or detaches from the target."),
+			   NULL,
+			   NULL,
+			   &setlist, &showlist);
+
   automatic_hardware_breakpoints = 1;
 
   observer_attach_about_to_proceed (breakpoint_about_to_proceed);
Index: breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.175
diff -u -p -r1.175 breakpoint.h
--- breakpoint.h	24 Feb 2012 15:10:59 -0000	1.175
+++ breakpoint.h	29 Feb 2012 05:52:13 -0000
@@ -154,6 +154,8 @@ enum bptype
     bp_fast_tracepoint,
     bp_static_tracepoint,
 
+    bp_dprintf,
+
     /* Event for JIT compiled code generation or deletion.  */
     bp_jit_event,
 
@@ -264,6 +266,14 @@ struct bp_target_info
   /* Vector of conditions the target should evaluate if it supports target-side
      breakpoint conditions.  */
   VEC(agent_expr_p) *conditions;
+
+  /* Vector of commands the target should evaluate if it supports
+     target-side breakpoint commands.  */
+  VEC(agent_expr_p) *commands;
+
+  /* Flag that is true if the breakpoint should be left in place even
+     when GDB is not connected.  */
+  int persist;
 };
 
 /* GDB maintains two types of information about each breakpoint (or
@@ -350,8 +360,11 @@ struct bp_location
 
   enum condition_status condition_changed;
 
-  /* Signals that breakpoint conditions need to be re-synched with the
-     target.  This has no use other than target-side breakpoints.  */
+  struct agent_expr *cmd_bytecode;
+
+  /* Signals that breakpoint conditions and/or commands need to be
+     re-synched with the target.  This has no use other than
+     target-side breakpoints.  */
   char needs_update;
 
   /* This location's address is in an unloaded solib, and so this
@@ -673,8 +686,9 @@ struct breakpoint
     /* String form of the breakpoint condition (malloc'd), or NULL if
        there is no condition.  */
     char *cond_string;
-    /* String form of exp to use for displaying to the user
-       (malloc'd), or NULL if none.  */
+
+    /* String form of extra parameters, or NULL if there are none.  */
+    char *extra_string;
 
     /* Holds the address of the related watchpoint_scope breakpoint
        when using watchpoints on local variables (might the concept of
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.486
diff -u -p -r1.486 remote.c
--- remote.c	24 Feb 2012 23:48:37 -0000	1.486
+++ remote.c	29 Feb 2012 05:52:14 -0000
@@ -244,6 +244,8 @@ static void remote_console_output (char 
 
 static int remote_supports_cond_breakpoints (void);
 
+static int remote_can_run_breakpoint_commands (void);
+
 /* The non-stop remote protocol provisions for one pending stop reply.
    This is where we keep it until it is acknowledged.  */
 
@@ -321,6 +323,10 @@ struct remote_state
      conditions.  */
   int cond_breakpoints;
 
+  /* True if the stub reports support for target-side breakpoint
+     commands.  */
+  int breakpoint_commands;
+
   /* True if the stub reports support for fast tracepoints.  */
   int fast_tracepoints;
 
@@ -1270,6 +1276,7 @@ enum {
   PACKET_qAttached,
   PACKET_ConditionalTracepoints,
   PACKET_ConditionalBreakpoints,
+  PACKET_BreakpointCommands,
   PACKET_FastTracepoints,
   PACKET_StaticTracepoints,
   PACKET_InstallInTrace,
@@ -3734,6 +3741,16 @@ remote_cond_breakpoint_feature (const st
 }
 
 static void
+remote_breakpoint_commands_feature (const struct protocol_feature *feature,
+				    enum packet_support support,
+				    const char *value)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  rs->breakpoint_commands = (support == PACKET_ENABLE);
+}
+
+static void
 remote_fast_tracepoint_feature (const struct protocol_feature *feature,
 				enum packet_support support,
 				const char *value)
@@ -3829,6 +3846,8 @@ static struct protocol_feature remote_pr
     PACKET_ConditionalTracepoints },
   { "ConditionalBreakpoints", PACKET_DISABLE, remote_cond_breakpoint_feature,
     PACKET_ConditionalBreakpoints },
+  { "BreakpointCommands", PACKET_DISABLE, remote_breakpoint_commands_feature,
+    PACKET_BreakpointCommands },
   { "FastTracepoints", PACKET_DISABLE, remote_fast_tracepoint_feature,
     PACKET_FastTracepoints },
   { "StaticTracepoints", PACKET_DISABLE, remote_static_tracepoint_feature,
@@ -7768,6 +7787,37 @@ remote_add_target_side_condition (struct
   return 0;
 }
 
+static void
+remote_add_target_side_commands (struct gdbarch *gdbarch,
+				 struct bp_target_info *bp_tgt, char *buf)
+{
+  struct agent_expr *aexpr = NULL;
+  int i, ix;
+
+  if (VEC_empty (agent_expr_p, bp_tgt->commands))
+    return;
+
+  buf += strlen (buf);
+
+  sprintf (buf, ";cmds:%x,", bp_tgt->persist);
+  buf += strlen (buf);
+
+  /* Concatenate all the agent expressions that are commands into the
+     cmds parameter.  */
+  for (ix = 0;
+       VEC_iterate (agent_expr_p, bp_tgt->commands, ix, aexpr);
+       ix++)
+    {
+      sprintf (buf, "X%x,", aexpr->len);
+      buf += strlen (buf);
+      for (i = 0; i < aexpr->len; ++i)
+	buf = pack_hex_byte (buf, aexpr->buf[i]);
+      *buf = '\0';
+    }
+
+  VEC_free (agent_expr_p, bp_tgt->commands);
+}
+
 /* Insert a breakpoint.  On targets that have software breakpoint
    support, we ask the remote target to do the work; on targets
    which don't, we insert a traditional memory breakpoint.  */
@@ -7804,6 +7854,9 @@ remote_insert_breakpoint (struct gdbarch
       if (remote_supports_cond_breakpoints ())
 	remote_add_target_side_condition (gdbarch, bp_tgt, p);
 
+      if (remote_can_run_breakpoint_commands ())
+	remote_add_target_side_commands (gdbarch, bp_tgt, p);
+
       putpkt (rs->buf);
       getpkt (&rs->buf, &rs->buf_size, 0);
 
@@ -8041,6 +8094,9 @@ remote_insert_hw_breakpoint (struct gdba
   if (remote_supports_cond_breakpoints ())
     remote_add_target_side_condition (gdbarch, bp_tgt, p);
 
+  if (remote_can_run_breakpoint_commands ())
+    remote_add_target_side_commands (gdbarch, bp_tgt, p);
+
   putpkt (rs->buf);
   getpkt (&rs->buf, &rs->buf_size, 0);
 
@@ -9973,6 +10029,14 @@ remote_supports_string_tracing (void)
   return rs->string_tracing;
 }
 
+static int
+remote_can_run_breakpoint_commands (void)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  return rs->breakpoint_commands;
+}
+
 static void
 remote_trace_init (void)
 {
@@ -10839,6 +10903,7 @@ Specify the serial device it is connecte
   remote_ops.to_supports_enable_disable_tracepoint = remote_supports_enable_disable_tracepoint;
   remote_ops.to_supports_string_tracing = remote_supports_string_tracing;
   remote_ops.to_supports_evaluation_of_breakpoint_conditions = remote_supports_cond_breakpoints;
+  remote_ops.to_can_run_breakpoint_commands = remote_can_run_breakpoint_commands;
   remote_ops.to_trace_init = remote_trace_init;
   remote_ops.to_download_tracepoint = remote_download_tracepoint;
   remote_ops.to_can_download_tracepoint = remote_can_download_tracepoint;
@@ -11361,6 +11426,10 @@ Show the maximum size of the address (in
 			 "ConditionalBreakpoints",
 			 "conditional-breakpoints", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_BreakpointCommands],
+			 "BreakpointCommands",
+			 "breakpoint-commands", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_FastTracepoints],
 			 "FastTracepoints", "fast-tracepoints", 0);
 
Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.300
diff -u -p -r1.300 target.c
--- target.c	24 Feb 2012 15:10:59 -0000	1.300
+++ target.c	29 Feb 2012 05:52:14 -0000
@@ -698,6 +698,7 @@ update_current_target (void)
       INHERIT (to_static_tracepoint_marker_at, t);
       INHERIT (to_static_tracepoint_markers_by_strid, t);
       INHERIT (to_traceframe_info, t);
+      INHERIT (to_can_run_breakpoint_commands, t);
       INHERIT (to_magic, t);
       INHERIT (to_supports_evaluation_of_breakpoint_conditions, t);
       /* Do not inherit to_memory_map.  */
@@ -929,6 +930,9 @@ update_current_target (void)
   de_fault (to_supports_evaluation_of_breakpoint_conditions,
 	    (int (*) (void))
 	    return_zero);
+  de_fault (to_can_run_breakpoint_commands,
+	    (int (*) (void))
+	    return_zero);
   de_fault (to_execution_direction, default_execution_direction);
 
 #undef de_fault
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.228
diff -u -p -r1.228 target.h
--- target.h	24 Feb 2012 23:46:48 -0000	1.228
+++ target.h	29 Feb 2012 05:52:14 -0000
@@ -666,6 +666,10 @@ struct target_ops
        end?  */
     int (*to_supports_evaluation_of_breakpoint_conditions) (void);
 
+    /* Does this target support evaluation of breakpoint commands on its
+       end?  */
+    int (*to_can_run_breakpoint_commands) (void);
+
     /* Determine current architecture of thread PTID.
 
        The target is supposed to determine the architecture of the code where
@@ -978,6 +982,12 @@ int target_supports_disable_randomizatio
 #define target_supports_evaluation_of_breakpoint_conditions() \
   (*current_target.to_supports_evaluation_of_breakpoint_conditions) ()
 
+/* Returns true if this target can handle breakpoint commands
+   on its end.  */
+
+#define target_can_run_breakpoint_commands() \
+  (*current_target.to_can_run_breakpoint_commands) ()
+
 /* Invalidate all target dcaches.  */
 extern void target_dcache_invalidate (void);
 
Index: common/ax.def
===================================================================
RCS file: /cvs/src/src/gdb/common/ax.def,v
retrieving revision 1.6
diff -u -p -r1.6 ax.def
--- common/ax.def	4 Jan 2012 08:17:17 -0000	1.6
+++ common/ax.def	29 Feb 2012 05:52:14 -0000
@@ -93,3 +93,5 @@ DEFOP (invalid2, 0, 0, 0, 0, 0x31)
    express the right thing.  */
 DEFOP (pick, 1, 0, 0, 1, 0x32)
 DEFOP (rot, 0, 0, 3, 3, 0x33)
+/* Both the argument and consumed numbers are dynamic for this one.  */
+DEFOP (printf, 0, 0, 0, 0, 0x34)
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.926
diff -u -p -r1.926 gdb.texinfo
--- doc/gdb.texinfo	25 Feb 2012 13:54:25 -0000	1.926
+++ doc/gdb.texinfo	29 Feb 2012 05:52:15 -0000
@@ -3301,6 +3301,7 @@ all breakpoints in that range are operat
 * Disabling::                   Disabling breakpoints
 * Conditions::                  Break conditions
 * Break Commands::              Breakpoint command lists
+* Dynamic Printf::              Dynamic printf
 * Save Breakpoints::            How to save breakpoints in a file
 * Error in Breakpoints::        ``Cannot insert breakpoints''
 * Breakpoint-related Warnings:: ``Breakpoint address adjusted...''
@@ -4587,6 +4588,68 @@ cont
 end
 @end smallexample
 
+@node Dynamic Printf
+@subsection Dynamic Printf
+
+@cindex dynamic printf
+The dynamic printf command @code{dprintf} is a convenient way to
+combine breakpoints with formatted printing of your program's data.
+
+In its most basic form, the output comes back to the GDB console and
+the @code{dprintf} only lasts as long as the debugging session.
+However, you can set the variable @code{dprintf-style} for additional
+approaches.  For instance, you can ask to format the output by calling
+your program's @code{printf} function.  This has the advantage that
+the characters go to the program's output device, so they can recorded
+in redirects to files and so forth.
+
+If you are doing remote debugging with a stub or agent, you can also
+ask to have the printf handled by the remote agent.  In addition to
+ensuring that the output goes to the remote program's device along
+with any other output the program might produce, you can also ask that
+the dprintf remain active even after disconnecting from the remote
+target.  Using the stub/agent is also more efficient, as it can do
+everything without needing to communicate with @value{GDBN}.
+
+@table @code
+@kindex dprintf
+@item dprintf @var{location} @var{template},@var{expression}[,@var{expression}@dots{}]
+Whenever execution reaches @var{location}, print the values of one or
+more @var{expressions} under the control of the string @var{template}.
+To print several values, separate them with commas.
+
+@item set dprintf-style @var{style}
+Set the dprintf output to be handled in one of several different
+styles enumerated below:
+
+@item gdb
+@kindex dprintf-style gdb
+Handle the output using the @value{GDBN} @code{printf} command.
+
+@item call
+@kindex dprintf-style call
+Handle the output by calling your program's @code{printf} function.
+(This expects @code{printf} to have been linked into the program.)
+
+@item agent
+@kindex dprintf-style agent
+Have the remote debugging agent (such as @code{gdbserver}) handle
+the output itself.  This style is only available for agents that
+support it.
+
+@item set disconnected-dprintf on
+@itemx set disconnected-dprintf off
+@kindex set disconnected-dprintf
+Choose whether @code{dprintf} commands should continue to run if
+@value{GDBN} has disconnected from the target.
+
+@item show disconnected-dprintf off
+@kindex show disconnected-dprintf
+Show the current choice for disconnected @code{dprintf}.
+
+@end table
+
+@cindex breakpoint commands
 @node Save Breakpoints
 @subsection How to save breakpoints to a file
 
Index: gdbserver/ax.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/ax.c,v
retrieving revision 1.1
diff -u -p -r1.1 ax.c
--- gdbserver/ax.c	24 Feb 2012 15:14:47 -0000	1.1
+++ gdbserver/ax.c	29 Feb 2012 05:52:15 -0000
@@ -1152,6 +1152,30 @@ gdb_eval_agent_expr (struct regcache *re
 	    top = stack[sp];
 	  break;
 
+	case gdb_agent_op_printf:
+	  {
+	    int chan, nargs, slen, i;
+	    int args[100];
+	    char *format;
+
+	    chan = aexpr->bytes[pc++];
+	    nargs = aexpr->bytes[pc++];
+	    slen = aexpr->bytes[pc++];
+	    format = (char *) &(aexpr->bytes[pc]);
+	    pc += slen;
+	    for (i = 0; i < nargs; ++i)
+	      {
+		args[i] = top;
+		if (--sp >= 0)
+		  top = stack[sp];
+	      }
+	    /* (should re-check format before calling?) */
+	    printf (format,
+		    args[0], args[1], args[2], args[3], args[4],
+		    args[5], args[6], args[7], args[8], args[9]);
+	  }
+	  break;
+
 	  /* GDB never (currently) generates any of these ops.  */
 	case gdb_agent_op_float:
 	case gdb_agent_op_ref_float:
Index: gdbserver/linux-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v
retrieving revision 1.195
diff -u -p -r1.195 linux-low.c
--- gdbserver/linux-low.c	27 Feb 2012 16:22:16 -0000	1.195
+++ gdbserver/linux-low.c	29 Feb 2012 05:52:15 -0000
@@ -2426,7 +2426,10 @@ Check if we're already there.\n",
 		   || (!step_over_finished
 		       && !bp_explains_trap && !trace_event)
 		   || (gdb_breakpoint_here (event_child->stop_pc)
-		   && gdb_condition_true_at_breakpoint (event_child->stop_pc)));
+		       && gdb_condition_true_at_breakpoint (event_child->stop_pc)
+		       && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+
+  run_breakpoint_commands (event_child->stop_pc);
 
   /* We found no reason GDB would want us to stop.  We either hit one
      of our own breakpoints, or finished an internal step GDB
@@ -3292,7 +3295,8 @@ need_step_over_p (struct inferior_list_e
 	 though.  If the condition is being evaluated on the target's side
 	 and it evaluate to false, step over this breakpoint as well.  */
       if (gdb_breakpoint_here (pc)
-	  && gdb_condition_true_at_breakpoint (pc))
+	  && gdb_condition_true_at_breakpoint (pc)
+	  && gdb_no_commands_at_breakpoint (pc))
 	{
 	  if (debug_threads)
 	    fprintf (stderr,
Index: gdbserver/mem-break.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/mem-break.c,v
retrieving revision 1.33
diff -u -p -r1.33 mem-break.c
--- gdbserver/mem-break.c	25 Feb 2012 19:54:50 -0000	1.33
+++ gdbserver/mem-break.c	29 Feb 2012 05:52:15 -0000
@@ -97,6 +97,20 @@ struct point_cond_list
   struct point_cond_list *next;
 };
 
+struct point_command_list
+{
+  /* Pointer to the agent expression that is the breakpoint's
+     commands.  */
+  struct agent_expr *cmd;
+
+  /* Flag that is true if this command should run even while GDB is
+     disconnected.  */
+  int persistence;
+
+  /* Pointer to the next command.  */
+  struct point_command_list *next;
+};
+
 /* A high level (in gdbserver's perspective) breakpoint.  */
 struct breakpoint
 {
@@ -111,6 +125,9 @@ struct breakpoint
      target's side.  */
   struct point_cond_list *cond_list;
 
+  /* Point to the list of commands to run when this is hit.  */
+  struct point_command_list *command_list;
+
   /* Link to this breakpoint's raw breakpoint.  This is always
      non-NULL.  */
   struct raw_breakpoint *raw;
@@ -121,6 +138,23 @@ struct breakpoint
   int (*handler) (CORE_ADDR);
 };
 
+int
+any_persistent_commands ()
+{
+  struct process_info *proc = current_process ();
+  struct breakpoint *bp;
+  struct point_command_list *cl;
+
+  for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+    {
+      for (cl = bp->command_list; cl != NULL; cl = cl->next)
+	if (cl->persistence)
+	  return 1;
+    }
+
+  return 0;
+}
+
 static struct raw_breakpoint *
 find_raw_breakpoint_at (CORE_ADDR where)
 {
@@ -835,6 +869,97 @@ gdb_condition_true_at_breakpoint (CORE_A
   return (value != 0);
 }
 
+/* Add commands COMMANDS to GDBserver's breakpoint BP.  */
+
+void
+add_commands_to_breakpoint (struct breakpoint *bp,
+			    struct agent_expr *commands, int persist)
+{
+  struct point_command_list *new_cmd;
+
+  /* Create new command.  */
+  new_cmd = xcalloc (1, sizeof (*new_cmd));
+  new_cmd->cmd = commands;
+  new_cmd->persistence = persist;
+
+  /* Add commands to the list.  */
+  new_cmd->next = bp->command_list;
+  bp->command_list = new_cmd;
+}
+
+/* Add a target-side command COMMAND to the breakpoint at ADDR.  */
+
+int
+add_breakpoint_commands (CORE_ADDR addr, char **command, int persist)
+{
+  struct breakpoint *bp = find_gdb_breakpoint_at (addr);
+  char *actparm = *command;
+  struct agent_expr *cmd;
+
+  if (bp == NULL)
+    return 1;
+
+  if (command == NULL)
+    return 1;
+
+  cmd = gdb_parse_agent_expr (&actparm);
+
+  if (cmd == NULL)
+    {
+      fprintf (stderr, "Command evaluation failed. "
+	       "Disabling.\n");
+      return 0;
+    }
+
+  add_commands_to_breakpoint (bp, cmd, persist);
+
+  *command = actparm;
+
+  return 0;
+}
+
+/* Return true if there are no commands to run at this location,
+   which likely means we want to report back to GDB.  */
+int
+gdb_no_commands_at_breakpoint (CORE_ADDR where)
+{
+  struct breakpoint *bp = find_gdb_breakpoint_at (where);
+
+  if (bp == NULL)
+    return 0;
+
+  if (debug_threads)
+    fprintf (stderr, "at 0x%s, bp command_list is 0x%x\n",
+	     paddress (where), (int) bp->command_list);
+  return (bp->command_list == NULL);
+}
+
+void
+run_breakpoint_commands (CORE_ADDR where)
+{
+  /* Fetch registers for the current inferior.  */
+  struct breakpoint *bp = find_gdb_breakpoint_at (where);
+  ULONGEST value = 0;
+  struct point_command_list *cl;
+  int err = 0;
+
+  struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+
+  if (bp == NULL)
+    return;
+
+  for (cl = bp->command_list;
+       cl && !value && !err; cl = cl->next)
+    {
+      /* Run the command.  */
+      err = gdb_eval_agent_expr (regcache, NULL, cl->cmd, &value);
+
+      /* If one command has a problem, stop digging the hole deeper.  */
+      if (err)
+	break;
+    }
+}
+
 /* Return 1 if there is a breakpoint inserted in address WHERE
    and if its condition, if it exists, is true.  */
 
Index: gdbserver/mem-break.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/mem-break.h,v
retrieving revision 1.25
diff -u -p -r1.25 mem-break.h
--- gdbserver/mem-break.h	24 Feb 2012 15:15:56 -0000	1.25
+++ gdbserver/mem-break.h	29 Feb 2012 05:52:15 -0000
@@ -52,11 +52,19 @@ void clear_gdb_breakpoint_conditions (CO
 
 int add_breakpoint_condition (CORE_ADDR addr, char **condition);
 
+int add_breakpoint_commands (CORE_ADDR addr, char **commands, int persist);
+
+int any_persistent_commands (void);
+
 /* Evaluation condition (if any) at breakpoint BP.  Return 1 if
    true and 0 otherwise.  */
 
 int gdb_condition_true_at_breakpoint (CORE_ADDR where);
 
+int gdb_no_commands_at_breakpoint (CORE_ADDR where);
+
+void run_breakpoint_commands (CORE_ADDR where);
+
 /* Returns TRUE if there's a GDB breakpoint set at ADDR.  */
 
 int gdb_breakpoint_here (CORE_ADDR where);
Index: gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.162
diff -u -p -r1.162 server.c
--- gdbserver/server.c	24 Feb 2012 15:15:56 -0000	1.162
+++ gdbserver/server.c	29 Feb 2012 05:52:15 -0000
@@ -1621,8 +1621,9 @@ handle_query (char *own_buf, int packet_
 	  strcat (own_buf, ";tracenz+");
 	}
 
-      /* Support target-side breakpoint conditions.  */
+      /* Support target-side breakpoint conditions and commands.  */
       strcat (own_buf, ";ConditionalBreakpoints+");
+      strcat (own_buf, ";BreakpointCommands+");
 
       return;
     }
@@ -2836,6 +2837,7 @@ static void
 process_point_options (CORE_ADDR point_addr, char **packet)
 {
   char *dataptr = *packet;
+  int persist;
 
   /* Check if data has the correct format.  */
   if (*dataptr != ';')
@@ -2845,21 +2847,33 @@ process_point_options (CORE_ADDR point_a
 
   while (*dataptr)
     {
-      switch (*dataptr)
+      if (*dataptr == ';')
+	++dataptr;
+
+      if (*dataptr == 'X')
 	{
-	  case 'X':
-	    /* Conditional expression.  */
-	    fprintf (stderr, "Found breakpoint condition.\n");
-	    add_breakpoint_condition (point_addr, &dataptr);
-	    break;
-	  default:
-	    /* Unrecognized token, just skip it.  */
-	    fprintf (stderr, "Unknown token %c, ignoring.\n",
-		     *dataptr);
+	  /* Conditional expression.  */
+	  fprintf (stderr, "Found breakpoint condition.\n");
+	  add_breakpoint_condition (point_addr, &dataptr);
+	}
+      else if (strncmp (dataptr, "cmds:", strlen ("cmds:")) == 0)
+	{
+	  dataptr += strlen ("cmds:");
+	  if (debug_threads)
+	    fprintf (stderr, "Found breakpoint commands %s.\n", dataptr);
+	  persist = (*dataptr == '1');
+	  dataptr += 2;
+	  add_breakpoint_commands (point_addr, &dataptr, persist);
+	}
+      else
+	{
+	  /* Unrecognized token, just skip it.  */
+	  fprintf (stderr, "Unknown token %c, ignoring.\n",
+		   *dataptr);
 	}
 
       /* Skip tokens until we find one that we recognize.  */
-      while (*dataptr && *dataptr != 'X' && *dataptr != ';')
+      while (*dataptr && *dataptr != ';')
 	dataptr++;
     }
   *packet = dataptr;
@@ -2925,7 +2939,7 @@ process_serial_event (void)
 	pid =
 	  ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id);
 
-      if (tracing && disconnected_tracing)
+      if ((tracing && disconnected_tracing) || any_persistent_commands ())
 	{
 	  struct thread_resume resume_info;
 	  struct process_info *process = find_process_pid (pid);
@@ -2936,9 +2950,15 @@ process_serial_event (void)
 	      break;
 	    }
 
-	  fprintf (stderr,
-		   "Disconnected tracing in effect, "
-		   "leaving gdbserver attached to the process\n");
+	  if (tracing && disconnected_tracing)
+	    fprintf (stderr,
+		     "Disconnected tracing in effect, "
+		     "leaving gdbserver attached to the process\n");
+
+	  if (any_persistent_commands ())
+	    fprintf (stderr,
+		     "Persistent commands are present, "
+		     "leaving gdbserver attached to the process\n");
 
 	  /* Make sure we're in non-stop/async mode, so we we can both
 	     wait for an async socket accept, and handle async target

             reply	other threads:[~2012-02-29  7:04 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-29  9:14 Stan Shebs [this message]
2012-02-29 10:28 ` Andreas Schwab
2012-03-13 23:09   ` Stan Shebs
2012-03-14 14:47     ` Marc Khouzam
2012-03-14 15:28     ` Tom Tromey
2012-02-29 16:16 ` Joel Brobecker
2012-03-13 23:15   ` Stan Shebs
2012-03-13 23:40     ` Joel Brobecker
2012-02-29 18:21 ` Eli Zaretskii
2012-03-13 23:20   ` Stan Shebs
2012-03-01 14:04 ` Hui Zhu
2012-03-04  6:31   ` Hui Zhu
2012-03-08 21:08 ` Tom Tromey
2012-03-13 23:51   ` Stan Shebs
2012-03-14 15:24     ` Tom Tromey

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=4F4DCDD5.2040807@earthlink.net \
    --to=stanshebs@earthlink.net \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

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

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