This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Conditional tracepoints


This patch adds conditional tracepoints. Tracepoint conditions work by being compiled into agent expressions that actually evaluate on the spot in the target, rather than collecting blocks of trace data. As usual for tracepoints, GDB proper doesn't have to do much, and most of the code here revolves around checking for the "ConditionalTracepoints" feature, so as to maintain compatibility with the two(?) existing tracepoint implementations out there. As a bonus subpatch, I added bytecode generation for comparison operators, other boolean ops are coming later. I tested by exercising with CodeSourcery's target agent, in the absence of anything usable in the GDB testsuite.

Stan

2009-06-26 Stan Shebs <stan@codesourcery.com>

   Conditional tracepoints.
   * ax-gdb.h (gen_eval_for_expr): Declare.
   * ax-gdb.c (gen_expr): Generate bytecodes for BINOP_EQUAL
   and other comparisons.
   (gen_eval_for_expr): New function.
   (agent_eval_command): New maintenance command.
   (_initialize_ax_gdb): Define the command.
   * remote.c (struct remote_state): New field cond_tracepoints.
   (PACKET_ConditionalTracepoints): New packet config type.
   (remote_cond_tracepoint_feature): New function.
   (remote_protocol_features): Add ConditionalTracepoints.
   (remote_supports_cond_tracepoints): New function.
   (_initialize_remote): Add ConditionalTracepoints.
   * tracepoint.c (download_tracepoint): Add conditional.

==> doc/ChangeLog <==
2009-06-26  Stan Shebs  <stan@codesourcery.com>

   * gdb.texinfo (Tracepoint Conditions): New section.
   (General Query Packets): Describe ConditionalTracepoints.
   (Tracepoint Packets): Describe condition field.
   (Maintenance Commands): Describe maint agent-eval.

==> testsuite/ChangeLog <==
2009-06-26  Stan Shebs  <stan@codesourcery.com>

* gdb.trace/tracecmd.exp: Add basic test of tracepoint conditions.

Index: ax-gdb.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.c,v
retrieving revision 1.50
diff -p -r1.50 ax-gdb.c
*** ax-gdb.c	17 Jun 2009 18:40:53 -0000	1.50
--- ax-gdb.c	27 Jun 2009 05:17:09 -0000
*************** gen_expr (struct expression *exp, union 
*** 1469,1474 ****
--- 1469,1480 ----
      case BINOP_BITWISE_AND:
      case BINOP_BITWISE_IOR:
      case BINOP_BITWISE_XOR:
+     case BINOP_EQUAL:
+     case BINOP_NOTEQUAL:
+     case BINOP_LESS:
+     case BINOP_GTR:
+     case BINOP_LEQ:
+     case BINOP_GEQ:
        (*pc)++;
        gen_expr (exp, pc, ax, &value1);
        gen_usual_unary (exp, ax, &value1);
*************** gen_expr (struct expression *exp, union 
*** 1538,1543 ****
--- 1544,1590 ----
  		     aop_bit_xor, aop_bit_xor, 0, "bitwise exclusive-or");
  	  break;
  
+ 	case BINOP_EQUAL:
+ 	  gen_binop (ax, value, &value1, &value2,
+ 		     aop_equal, aop_equal, 0, "equal");
+ 	  break;
+ 
+ 	case BINOP_NOTEQUAL:
+ 	  gen_binop (ax, value, &value1, &value2,
+ 		     aop_equal, aop_equal, 0, "equal");
+ 	  gen_logical_not (ax, value,
+ 			   language_bool_type (exp->language_defn,
+ 					       exp->gdbarch));
+ 	  break;
+ 
+ 	case BINOP_LESS:
+ 	  gen_binop (ax, value, &value1, &value2,
+ 		     aop_less_signed, aop_less_unsigned, 0, "less than");
+ 	  break;
+ 
+ 	case BINOP_GTR:
+ 	  ax_simple (ax, aop_swap);
+ 	  gen_binop (ax, value, &value1, &value2,
+ 		     aop_less_signed, aop_less_unsigned, 0, "less than");
+ 	  break;
+ 
+ 	case BINOP_LEQ:
+ 	  ax_simple (ax, aop_swap);
+ 	  gen_binop (ax, value, &value1, &value2,
+ 		     aop_less_signed, aop_less_unsigned, 0, "less than");
+ 	  gen_logical_not (ax, value,
+ 			   language_bool_type (exp->language_defn,
+ 					       exp->gdbarch));
+ 	  break;
+ 
+ 	case BINOP_GEQ:
+ 	  gen_binop (ax, value, &value1, &value2,
+ 		     aop_less_signed, aop_less_unsigned, 0, "less than");
+ 	  gen_logical_not (ax, value,
+ 			   language_bool_type (exp->language_defn,
+ 					       exp->gdbarch));
+ 	  break;
+ 
  	default:
  	  /* We should only list operators in the outer case statement
  	     that we actually handle in the inner case statement.  */
*************** gen_trace_for_expr (CORE_ADDR scope, str
*** 1756,1761 ****
--- 1803,1833 ----
    return ax;
  }
  
+ 
+ struct agent_expr *
+ gen_eval_for_expr (CORE_ADDR scope, struct expression *expr)
+ {
+   struct cleanup *old_chain = 0;
+   struct agent_expr *ax = new_agent_expr (scope);
+   union exp_element *pc;
+   struct axs_value value;
+ 
+   old_chain = make_cleanup_free_agent_expr (ax);
+ 
+   pc = expr->elts;
+   trace_kludge = 0;
+   gen_expr (expr, &pc, ax, &value);
+ 
+   /* 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)
  {
*************** agent_command (char *exp, int from_tty)
*** 1786,1791 ****
--- 1858,1898 ----
    do_cleanups (old_chain);
    dont_repeat ();
  }
+ 
+ /* Parse the given expression, compile it into an agent expression
+    that does direct evaluation, and display the resulting
+    expression.  */
+ 
+ static void
+ agent_eval_command (char *exp, int from_tty)
+ {
+   struct cleanup *old_chain = 0;
+   struct expression *expr;
+   struct agent_expr *agent;
+   struct frame_info *fi = get_current_frame ();	/* need current scope */
+ 
+   /* 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"));
+ 
+   expr = parse_expression (exp);
+   old_chain = make_cleanup (free_current_contents, &expr);
+   agent = gen_eval_for_expr (get_frame_pc (fi), expr);
+   make_cleanup_free_agent_expr (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 ();
+ }
  
  
  /* Initialization code.  */
*************** void
*** 1795,1800 ****
  _initialize_ax_gdb (void)
  {
    add_cmd ("agent", class_maintenance, agent_command,
! 	   _("Translate an expression into remote agent bytecode."),
  	   &maintenancelist);
  }
--- 1902,1911 ----
  _initialize_ax_gdb (void)
  {
    add_cmd ("agent", class_maintenance, agent_command,
! 	   _("Translate an expression into remote agent bytecode for tracing."),
! 	   &maintenancelist);
! 
!   add_cmd ("agent-eval", class_maintenance, agent_eval_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.11
diff -p -r1.11 ax-gdb.h
*** ax-gdb.h	3 Jan 2009 05:57:50 -0000	1.11
--- ax-gdb.h	27 Jun 2009 05:17:09 -0000
*************** struct axs_value
*** 99,102 ****
--- 99,104 ----
     function to discover which registers the expression uses.  */
  extern struct agent_expr *gen_trace_for_expr (CORE_ADDR, struct expression *);
  
+ extern struct agent_expr *gen_eval_for_expr (CORE_ADDR, struct expression *);
+ 
  #endif /* AX_GDB_H */
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.360
diff -p -r1.360 remote.c
*** remote.c	22 Jun 2009 16:03:33 -0000	1.360
--- remote.c	27 Jun 2009 05:17:11 -0000
*************** struct remote_state
*** 294,299 ****
--- 294,302 ----
  
    /* True if the stub reports support for vCont;t.  */
    int support_vCont_t;
+ 
+   /* True if the stub reports support for conditional tracepoints.  */
+   int cond_tracepoints;
  };
  
  /* Returns true if the multi-process extensions are in effect.  */
*************** enum {
*** 993,998 ****
--- 996,1002 ----
    PACKET_qXfer_siginfo_read,
    PACKET_qXfer_siginfo_write,
    PACKET_qAttached,
+   PACKET_ConditionalTracepoints,
    PACKET_MAX
  };
  
*************** remote_non_stop_feature (const struct pr
*** 3014,3019 ****
--- 3018,3032 ----
    rs->non_stop_aware = (support == PACKET_ENABLE);
  }
  
+ static void
+ remote_cond_tracepoint_feature (const struct protocol_feature *feature,
+ 				       enum packet_support support,
+ 				       const char *value)
+ {
+   struct remote_state *rs = get_remote_state ();
+   rs->cond_tracepoints = (support == PACKET_ENABLE);
+ }
+ 
  static struct protocol_feature remote_protocol_features[] = {
    { "PacketSize", PACKET_DISABLE, remote_packet_size, -1 },
    { "qXfer:auxv:read", PACKET_DISABLE, remote_supported_packet,
*************** static struct protocol_feature remote_pr
*** 3040,3045 ****
--- 3053,3060 ----
      PACKET_qXfer_siginfo_read },
    { "qXfer:siginfo:write", PACKET_DISABLE, remote_supported_packet,
      PACKET_qXfer_siginfo_write },
+   { "ConditionalTracepoints", PACKET_DISABLE, remote_cond_tracepoint_feature,
+     PACKET_ConditionalTracepoints },
  };
  
  static void
*************** remote_supports_multi_process (void)
*** 8730,8735 ****
--- 8745,8757 ----
    return remote_multi_process_p (rs);
  }
  
+ int
+ remote_supports_cond_tracepoints (void)
+ {
+   struct remote_state *rs = get_remote_state ();
+   return rs->cond_tracepoints;
+ }
+ 
  static void
  init_remote_ops (void)
  {
*************** Show the maximum size of the address (in
*** 9173,9178 ****
--- 9195,9203 ----
    add_packet_config_cmd (&remote_protocol_packets[PACKET_qAttached],
  			 "qAttached", "query-attached", 0);
  
+   add_packet_config_cmd (&remote_protocol_packets[PACKET_ConditionalTracepoints],
+ 			 "ConditionalTracepoints", "conditional-tracepoints", 0);
+ 
    /* Keep the old ``set remote Z-packet ...'' working.  Each individual
       Z sub-packet has its own set and show commands, but users may
       have sets to this variable in their .gdbinit files (or in their
Index: tracepoint.c
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.c,v
retrieving revision 1.122
diff -p -r1.122 tracepoint.c
*** tracepoint.c	4 Jun 2009 12:28:39 -0000	1.122
--- tracepoint.c	27 Jun 2009 05:17:11 -0000
***************
*** 32,37 ****
--- 32,38 ----
  #include "breakpoint.h"
  #include "tracepoint.h"
  #include "remote.h"
+ extern int remote_supports_cond_tracepoints (void);
  #include "linespec.h"
  #include "regcache.h"
  #include "completer.h"
*************** download_tracepoint (struct breakpoint *
*** 1335,1346 ****
--- 1336,1366 ----
    char **stepping_actions;
    int ndx;
    struct cleanup *old_chain = NULL;
+   struct agent_expr *aexpr;
+   struct cleanup *aexpr_chain = NULL;
  
    sprintf_vma (tmp, (t->loc ? t->loc->address : 0));
    sprintf (buf, "QTDP:%x:%s:%c:%lx:%x", t->number, 
  	   tmp, /* address */
  	   (t->enable_state == bp_enabled ? 'E' : 'D'),
  	   t->step_count, t->pass_count);
+   /* If the tracepoint has a conditional, make it into an agent
+      expression and append to the definition.  */
+   if (t->loc->cond)
+     {
+       /* Only test support at download time, we may not know target
+ 	 capabilities at definition time.  */
+       if (remote_supports_cond_tracepoints ())
+ 	{
+ 	  aexpr = gen_eval_for_expr (t->loc->address, t->loc->cond);
+ 	  aexpr_chain = make_cleanup_free_agent_expr (aexpr);
+ 	  sprintf (buf + strlen (buf), ":X%x,", aexpr->len);
+ 	  mem2hex (aexpr->buf, buf + strlen (buf), aexpr->len);
+ 	  do_cleanups (aexpr_chain);
+ 	}
+       else
+ 	warning (_("Target does not support conditional tracepoints, ignoring tp %d cond"), t->number);
+     }
  
    if (t->actions)
      strcat (buf, "-");
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.600
diff -p -r1.600 gdb.texinfo
*** doc/gdb.texinfo	15 Jun 2009 12:11:36 -0000	1.600
--- doc/gdb.texinfo	27 Jun 2009 05:17:17 -0000
*************** conditions and actions.
*** 8845,8850 ****
--- 8845,8851 ----
  * Create and Delete Tracepoints::
  * Enable and Disable Tracepoints::
  * Tracepoint Passcounts::
+ * Tracepoint Conditions::
  * Tracepoint Actions::
  * Listing Tracepoints::
  * Starting and Stopping Trace Experiments::
*************** Here are some examples of using the @cod
*** 8884,8889 ****
--- 8885,8897 ----
  @noindent
  You can abbreviate @code{trace} as @code{tr}.
  
+ @item trace @var{location} if @var{cond}
+ Set a tracepoint with condition @var{cond}; evaluate the expression
+ @var{cond} each time the tracepoint is reached, and collect data only
+ if the value is nonzero---that is, if @var{cond} evaluates as true.
+ @xref{Tracepoint Conditions, ,Tracepoint Conditions}, for more
+ information on tracepoint conditions.
+ 
  @vindex $tpnum
  @cindex last tracepoint number
  @cindex recent tracepoint number
*************** Examples:
*** 8966,8971 ****
--- 8974,9017 ----
  @end smallexample
  @end table
  
+ @node Tracepoint Conditions
+ @subsection Tracepoint Conditions
+ @cindex conditional tracepoints
+ @cindex tracepoint conditions
+ 
+ The simplest sort of tracepoint collects data every time your program
+ reaches a specified place.  You can also specify a @dfn{condition} for
+ a tracepoint.  A condition is just a Boolean expression in your
+ programming language (@pxref{Expressions, ,Expressions}).  A
+ tracepoint with a condition evaluates the expression each time your
+ program reaches it, and data collection happens only if the condition
+ is @emph{true}.
+ 
+ Tracepoint conditions can be specified when a tracepoint is set, by
+ using @samp{if} in the arguments to the @code{trace} command.
+ @xref{Create and Delete Tracepoints, ,Setting Tracepoints}.  They can
+ also be set or changed at any time with the @code{condition} command,
+ just as with breakpoints.
+ 
+ In contrast to breakpoint conditions, @value{GDBN} does not actually
+ evaluate @var{expression} at the time the @code{condition} command (or
+ a command that sets a tracepoint with a condition, like @code{trace if
+ @dots{}}) is given, however.  @xref{Expressions, ,Expressions}.
+ Instead, @value{GDBN} encodes the expression into a form suitable for
+ execution on the target, independently of @value{GDBN}. Global
+ variables become raw memory locations, locals become stack accesses,
+ and so forth.
+ 
+ For instance, the following tracepoint command might be useful in
+ collecting data about uses of a function that should not be called when
+ an error has occurred and is propagating through the program; an
+ unconditional tracepoint could end up collecting thousands of useless
+ frames that you would have to search through.
+ 
+ @smallexample
+ (@value{GDBP}) @b{trace normal_operation if errcode > 0}
+ @end smallexample
+ 
  @node Tracepoint Actions
  @subsection Tracepoint Action Lists
  
*************** messages, see @ref{Debugging Output}.)
*** 26393,26402 ****
  
  @table @code
  @kindex maint agent
  @item maint agent @var{expression}
  Translate the given @var{expression} into remote agent bytecodes.
  This command is useful for debugging the Agent Expression mechanism
! (@pxref{Agent Expressions}).
  
  @kindex maint info breakpoints
  @item @anchor{maint info breakpoints}maint info breakpoints
--- 26439,26453 ----
  
  @table @code
  @kindex maint agent
+ @kindex maint agent-eval
  @item maint agent @var{expression}
+ @itemx maint agent-eval @var{expression}
  Translate the given @var{expression} into remote agent bytecodes.
  This command is useful for debugging the Agent Expression mechanism
! (@pxref{Agent Expressions}).  The @samp{agent} version produces an
! expression useful for data collection, such as by tracepoints, while
! @samp{maint agent-eval} produces an expression that evaluates directly
! to a result.
  
  @kindex maint info breakpoints
  @item @anchor{maint info breakpoints}maint info breakpoints
*************** These are the currently defined stub fea
*** 28274,28279 ****
--- 28325,28335 ----
  @tab @samp{-}
  @tab No
  
+ @item @samp{ConditionalTracepoints}
+ @tab No
+ @tab @samp{-}
+ @tab No
+ 
  @end multitable
  
  These are the currently defined stub features, in more detail:
*************** indicated it supports them in its @samp{
*** 28351,28356 ****
--- 28407,28416 ----
  The remote stub understands the @samp{qXfer:osdata:read} packet
  ((@pxref{qXfer osdata read}).
  
+ @item ConditionalTracepoints
+ The remote stub accepts and tests conditional expressions defined for
+ tracepoints (@pxref{Tracepoint Conditions}).
+ 
  @end table
  
  @item qSymbol::
*************** tracepoints (@pxref{Tracepoints}).
*** 28663,28673 ****
  
  @table @samp
  
! @item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}@r{[}-@r{]}
  Create a new tracepoint, number @var{n}, at @var{addr}.  If @var{ena}
  is @samp{E}, then the tracepoint is enabled; if it is @samp{D}, then
  the tracepoint is disabled.  @var{step} is the tracepoint's step
! count, and @var{pass} is its pass count.  If the trailing @samp{-} is
  present, further @samp{QTDP} packets will follow to specify this
  tracepoint's actions.
  
--- 28723,28736 ----
  
  @table @samp
  
! @item QTDP:@var{n}:@var{addr}:@var{ena}:@var{step}:@var{pass}[:X@var{len},@var{bytes}]@r{[}-@r{]}
  Create a new tracepoint, number @var{n}, at @var{addr}.  If @var{ena}
  is @samp{E}, then the tracepoint is enabled; if it is @samp{D}, then
  the tracepoint is disabled.  @var{step} is the tracepoint's step
! count, and @var{pass} is its pass count.  If an @samp{X} is present,
! it introduces a tracepoint condition, which consists of a hexadecimal
! length, followed by a comma and hex-encoded bytes, in a manner similar
! to action encodings as described below.  If the trailing @samp{-} is
  present, further @samp{QTDP} packets will follow to specify this
  tracepoint's actions.
  
Index: testsuite/gdb.trace/tracecmd.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.trace/tracecmd.exp,v
retrieving revision 1.12
diff -p -r1.12 tracecmd.exp
*** testsuite/gdb.trace/tracecmd.exp	9 Jun 2009 17:12:43 -0000	1.12
--- testsuite/gdb.trace/tracecmd.exp	27 Jun 2009 05:17:20 -0000
*************** gdb_test "trace" "No default breakpoint 
*** 153,159 ****
  # deferred to limits test module
  
  # 1.11 tracepoint conditions
! # conditions on tracepoints not implemented
  
  # 1.12 set tracepoint in prologue
  # [see tfind.exp]
--- 153,164 ----
  # deferred to limits test module
  
  # 1.11 tracepoint conditions
! gdb_delete_tracepoints
! gdb_test "trace gdb_recursion_test if q1 > 0" \
! 	"Tracepoint $decimal at $hex: file.*$srcfile, line $testline1." \
! 	"1.11a: conditional tracepoint"
! gdb_test "info trace" "in gdb_recursion_test.*$srcfile:$testline1.*trace only if q1 > 0" \
! 	"1.11b: verify conditional tracepoint"
  
  # 1.12 set tracepoint in prologue
  # [see tfind.exp]

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]