2009-12-28 Stan Shebs <stan@codesourcery.com>
authorStan Shebs <shebs@codesourcery.com>
Mon, 28 Dec 2009 23:39:10 +0000 (23:39 +0000)
committerStan Shebs <shebs@codesourcery.com>
Mon, 28 Dec 2009 23:39:10 +0000 (23:39 +0000)
Add trace state variables.
* ax.h (enum agent_op): Add getv, setv, and tracev.
(ax_tsv): Declare.
* ax-gdb.c: Include tracepoint.h.
(gen_expr): Handle BINOP_ASSIGN, BINOP_ASSIGN_MODIFY, and
OP_INTERNALVAR.
(gen_expr_binop_rest): New function, split from gen_expr.
* ax-general.c (ax_tsv): New function.
(aop_map): Add new bytecodes.
* tracepoint.h (struct trace_state_variable): New struct.
(tsv_s): New typedef.
(find_trace_state_variable): Declare.
* tracepoint.c (tvariables): New global.
(next_tsv_number): New global.
(create_trace_state_variable): New function.
(find_trace_state_variable): New function.
(delete_trace_state_variable): New function.
(trace_variable_command): New function.
(delete_trace_variable_command): New function.
(tvariables_info): New function.
(trace_start_command): Download tsvs with initial values.
(_initialize_tracepoint): Add new commands.
* NEWS: Mention the addition of trace state variables.

==> doc/ChangeLog <==
2009-12-28  Stan Shebs  <stan@codesourcery.com>

* gdb.texinfo (Trace State Variables): New section.
(Tracepoint Packets): Describe trace state variable packets.
* agentexpr.texi (Bytecode Descriptions): Describe trace state
variable bytecodes.

==> testsuite/ChangeLog <==
2009-12-28  Stan Shebs  <stan@codesourcery.com>

* gdb.trace/tsv.exp: New file.
* gdb.base/completion.exp: Update ambiguous info output.

13 files changed:
gdb/ChangeLog
gdb/NEWS
gdb/ax-gdb.c
gdb/ax-general.c
gdb/ax.h
gdb/doc/ChangeLog
gdb/doc/agentexpr.texi
gdb/doc/gdb.texinfo
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/completion.exp
gdb/testsuite/gdb.trace/tsv.exp [new file with mode: 0644]
gdb/tracepoint.c
gdb/tracepoint.h

index 6ccb88b..3bfe688 100644 (file)
@@ -1,3 +1,29 @@
+2009-12-28  Stan Shebs  <stan@codesourcery.com>
+
+       Add trace state variables.
+       * ax.h (enum agent_op): Add getv, setv, and tracev.
+       (ax_tsv): Declare.
+       * ax-gdb.c: Include tracepoint.h.
+       (gen_expr): Handle BINOP_ASSIGN, BINOP_ASSIGN_MODIFY, and
+       OP_INTERNALVAR.
+       (gen_expr_binop_rest): New function, split from gen_expr.
+       * ax-general.c (ax_tsv): New function.
+       (aop_map): Add new bytecodes.
+       * tracepoint.h (struct trace_state_variable): New struct.
+       (tsv_s): New typedef.
+       (find_trace_state_variable): Declare.
+       * tracepoint.c (tvariables): New global.
+       (next_tsv_number): New global.
+       (create_trace_state_variable): New function.
+       (find_trace_state_variable): New function.
+       (delete_trace_state_variable): New function.
+       (trace_variable_command): New function.
+       (delete_trace_variable_command): New function.
+       (tvariables_info): New function.
+       (trace_start_command): Download tsvs with initial values.
+       (_initialize_tracepoint): Add new commands.
+       * NEWS: Mention the addition of trace state variables.
+
 2009-12-28  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * NEWS: Document "info variables" change.
index 8c0fd92..b680d9b 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -24,6 +24,19 @@ Renesas RX                   rx
   lists inferiors that are not running yet or that have exited
   already.  See also "New commands" and "New options" below.
 
+* Trace state variables
+
+  GDB tracepoints now include support for trace state variables, which
+  are variables managed by the target agent during a tracing
+  experiment.  They are useful for tracepoints that trigger each
+  other, so for instance one tracepoint can count hits in a variable,
+  and then a second tracepoint has a condition that is true when the
+  count reaches a particular value.  Trace state variables share the
+  $-syntax of GDB convenience variables, and can appear in both
+  tracepoint actions and condition expressions.  Use the "tvariable"
+  command to create, and "info tvariables" to view; see "Trace State
+  Variables" in the manual for more detail.
+  
 * Changed commands
 
 disassemble
@@ -75,6 +88,15 @@ set remotebreak [on | off]
 show remotebreak
 Deprecated.  Use "set/show remote interrupt-sequence" instead.
 
+tvariable $NAME [ = EXP ]
+  Create or modify a trace state variable.
+
+info tvariables
+  List trace state variables and their values.
+
+delete tvariable $NAME ...
+  Delete one or more trace state variables.
+
 * New options
 
 set follow-exec-mode new|same
@@ -83,6 +105,14 @@ show follow-exec-mode
   creates a new one.  This is useful to be able to restart the old
   executable after the inferior having done an exec call.
 
+* New remote packets
+
+QTDV
+   Define a trace state variable.
+
+qTV
+   Get the current value of a trace state variable.
+
 * Bug fixes
 
 Process record now works correctly with hardware watchpoints.
index a64658d..ee1f515 100644 (file)
@@ -36,6 +36,7 @@
 #include "user-regs.h"
 #include "language.h"
 #include "dictionary.h"
+#include "tracepoint.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,
@@ -139,6 +140,12 @@ static void gen_sizeof (struct expression *exp, union exp_element **pc,
                        struct type *size_type);
 static void gen_expr (struct expression *exp, union exp_element **pc,
                      struct agent_expr *ax, struct axs_value *value);
+static void gen_expr_binop_rest (struct expression *exp,
+                                enum exp_opcode op, union exp_element **pc,
+                                struct agent_expr *ax,
+                                struct axs_value *value,
+                                struct axs_value *value1,
+                                struct axs_value *value2);
 
 static void agent_command (char *exp, int from_tty);
 \f
@@ -1441,7 +1448,7 @@ gen_expr (struct expression *exp, union exp_element **pc,
 {
   /* Used to hold the descriptions of operand expressions.  */
   struct axs_value value1, value2;
-  enum exp_opcode op = (*pc)[0].opcode;
+  enum exp_opcode op = (*pc)[0].opcode, op2;
 
   /* If we're looking at a constant expression, just push its value.  */
   {
@@ -1478,119 +1485,63 @@ gen_expr (struct expression *exp, union exp_element **pc,
       (*pc)++;
       gen_expr (exp, pc, ax, &value1);
       gen_usual_unary (exp, ax, &value1);
-      gen_expr (exp, pc, ax, &value2);
-      gen_usual_unary (exp, ax, &value2);
-      gen_usual_arithmetic (exp, ax, &value1, &value2);
-      switch (op)
+      gen_expr_binop_rest (exp, op, pc, ax, value, &value1, &value2);
+      break;
+
+    case BINOP_ASSIGN:
+      (*pc)++;
+      if ((*pc)[0].opcode == OP_INTERNALVAR)
        {
-       case BINOP_ADD:
-         if (TYPE_CODE (value1.type) == TYPE_CODE_INT
-             && TYPE_CODE (value2.type) == TYPE_CODE_PTR)
+         char *name = internalvar_name ((*pc)[1].internalvar);
+         struct trace_state_variable *tsv;
+         (*pc) += 3;
+         gen_expr (exp, pc, ax, value);
+         tsv = find_trace_state_variable (name);
+         if (tsv)
            {
-             /* Swap the values and proceed normally.  */
-             ax_simple (ax, aop_swap);
-             gen_ptradd (ax, value, &value2, &value1);
+             ax_tsv (ax, aop_setv, tsv->number);
+             if (trace_kludge)
+               ax_tsv (ax, aop_tracev, tsv->number);
            }
-         else if (TYPE_CODE (value1.type) == TYPE_CODE_PTR
-                  && TYPE_CODE (value2.type) == TYPE_CODE_INT)
-           gen_ptradd (ax, value, &value1, &value2);
-         else
-           gen_binop (ax, value, &value1, &value2,
-                      aop_add, aop_add, 1, "addition");
-         break;
-       case BINOP_SUB:
-         if (TYPE_CODE (value1.type) == TYPE_CODE_PTR
-             && TYPE_CODE (value2.type) == TYPE_CODE_INT)
-           gen_ptrsub (ax,value, &value1, &value2);
-         else if (TYPE_CODE (value1.type) == TYPE_CODE_PTR
-                  && TYPE_CODE (value2.type) == TYPE_CODE_PTR)
-           /* FIXME --- result type should be ptrdiff_t */
-           gen_ptrdiff (ax, value, &value1, &value2,
-                        builtin_type (exp->gdbarch)->builtin_long);
          else
-           gen_binop (ax, value, &value1, &value2,
-                      aop_sub, aop_sub, 1, "subtraction");
-         break;
-       case BINOP_MUL:
-         gen_binop (ax, value, &value1, &value2,
-                    aop_mul, aop_mul, 1, "multiplication");
-         break;
-       case BINOP_DIV:
-         gen_binop (ax, value, &value1, &value2,
-                    aop_div_signed, aop_div_unsigned, 1, "division");
-         break;
-       case BINOP_REM:
-         gen_binop (ax, value, &value1, &value2,
-                    aop_rem_signed, aop_rem_unsigned, 1, "remainder");
-         break;
-       case BINOP_SUBSCRIPT:
-         gen_ptradd (ax, value, &value1, &value2);
-         if (TYPE_CODE (value->type) != TYPE_CODE_PTR)
-           error (_("Invalid combination of types in array subscripting."));
-         gen_deref (ax, value);
-         break;
-       case BINOP_BITWISE_AND:
-         gen_binop (ax, value, &value1, &value2,
-                    aop_bit_and, aop_bit_and, 0, "bitwise and");
-         break;
-
-       case BINOP_BITWISE_IOR:
-         gen_binop (ax, value, &value1, &value2,
-                    aop_bit_or, aop_bit_or, 0, "bitwise or");
-         break;
-
-       case BINOP_BITWISE_XOR:
-         gen_binop (ax, value, &value1, &value2,
-                    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;
+           error (_("$%s is not a trace state variable, may not assign to it"), name);
+       }
+      else
+       error (_("May only assign to trace state variables"));
+      break;
 
-       default:
-         /* We should only list operators in the outer case statement
-            that we actually handle in the inner case statement.  */
-         internal_error (__FILE__, __LINE__,
-                         _("gen_expr: op case sets don't match"));
+    case BINOP_ASSIGN_MODIFY:
+      (*pc)++;
+      op2 = (*pc)[0].opcode;
+      (*pc)++;
+      (*pc)++;
+      if ((*pc)[0].opcode == OP_INTERNALVAR)
+       {
+         char *name = internalvar_name ((*pc)[1].internalvar);
+         struct trace_state_variable *tsv;
+         (*pc) += 3;
+         tsv = find_trace_state_variable (name);
+         if (tsv)
+           {
+             /* The tsv will be the left half of the binary operation.  */
+             ax_tsv (ax, aop_getv, tsv->number);
+             if (trace_kludge)
+               ax_tsv (ax, aop_tracev, tsv->number);
+             /* Trace state variables are always 64-bit integers.  */
+             value1.kind = axs_rvalue;
+             value1.type = builtin_type (exp->gdbarch)->builtin_long_long;
+             /* Now do right half of expression.  */
+             gen_expr_binop_rest (exp, op2, pc, ax, value, &value1, &value2);
+             /* We have a result of the binary op, set the tsv.  */
+             ax_tsv (ax, aop_setv, tsv->number);
+             if (trace_kludge)
+               ax_tsv (ax, aop_tracev, tsv->number);
+           }
+         else
+           error (_("$%s is not a trace state variable, may not assign to it"), name);
        }
+      else
+       error (_("May only assign to trace state variables"));
       break;
 
       /* Note that we need to be a little subtle about generating code
@@ -1644,7 +1595,24 @@ gen_expr (struct expression *exp, union exp_element **pc,
       break;
 
     case OP_INTERNALVAR:
-      error (_("GDB agent expressions cannot use convenience variables."));
+      {
+       const char *name = internalvar_name ((*pc)[1].internalvar);
+       struct trace_state_variable *tsv;
+       (*pc) += 3;
+       tsv = find_trace_state_variable (name);
+       if (tsv)
+         {
+           ax_tsv (ax, aop_getv, tsv->number);
+           if (trace_kludge)
+             ax_tsv (ax, aop_tracev, tsv->number);
+           /* Trace state variables are always 64-bit integers.  */
+           value->kind = axs_rvalue;
+           value->type = builtin_type (exp->gdbarch)->builtin_long_long;
+         }
+       else
+         error (_("$%s is not a trace state variable; GDB agent expressions cannot use convenience variables."), name);
+      }
+      break;
 
       /* Weirdo operator: see comments for gen_repeat for details.  */
     case BINOP_REPEAT:
@@ -1788,6 +1756,131 @@ gen_expr (struct expression *exp, union exp_element **pc,
       error (_("Unsupported operator in expression."));
     }
 }
+
+/* This handles the middle-to-right-side of code generation for binary
+   expressions, which is shared between regular binary operations and
+   assign-modify (+= and friends) expressions.  */
+
+static void
+gen_expr_binop_rest (struct expression *exp,
+                    enum exp_opcode op, union exp_element **pc,
+                    struct agent_expr *ax, struct axs_value *value,
+                    struct axs_value *value1, struct axs_value *value2)
+{
+  gen_expr (exp, pc, ax, value2);
+  gen_usual_unary (exp, ax, value2);
+  gen_usual_arithmetic (exp, ax, value1, value2);
+  switch (op)
+    {
+    case BINOP_ADD:
+      if (TYPE_CODE (value1->type) == TYPE_CODE_INT
+         && TYPE_CODE (value2->type) == TYPE_CODE_PTR)
+       {
+         /* Swap the values and proceed normally.  */
+         ax_simple (ax, aop_swap);
+         gen_ptradd (ax, value, value2, value1);
+       }
+      else if (TYPE_CODE (value1->type) == TYPE_CODE_PTR
+              && TYPE_CODE (value2->type) == TYPE_CODE_INT)
+       gen_ptradd (ax, value, value1, value2);
+      else
+       gen_binop (ax, value, value1, value2,
+                  aop_add, aop_add, 1, "addition");
+      break;
+    case BINOP_SUB:
+      if (TYPE_CODE (value1->type) == TYPE_CODE_PTR
+         && TYPE_CODE (value2->type) == TYPE_CODE_INT)
+       gen_ptrsub (ax,value, value1, value2);
+      else if (TYPE_CODE (value1->type) == TYPE_CODE_PTR
+              && TYPE_CODE (value2->type) == TYPE_CODE_PTR)
+       /* FIXME --- result type should be ptrdiff_t */
+       gen_ptrdiff (ax, value, value1, value2,
+                    builtin_type (exp->gdbarch)->builtin_long);
+      else
+       gen_binop (ax, value, value1, value2,
+                  aop_sub, aop_sub, 1, "subtraction");
+      break;
+    case BINOP_MUL:
+      gen_binop (ax, value, value1, value2,
+                aop_mul, aop_mul, 1, "multiplication");
+      break;
+    case BINOP_DIV:
+      gen_binop (ax, value, value1, value2,
+                aop_div_signed, aop_div_unsigned, 1, "division");
+      break;
+    case BINOP_REM:
+      gen_binop (ax, value, value1, value2,
+                aop_rem_signed, aop_rem_unsigned, 1, "remainder");
+      break;
+    case BINOP_SUBSCRIPT:
+      gen_ptradd (ax, value, value1, value2);
+      if (TYPE_CODE (value->type) != TYPE_CODE_PTR)
+       error (_("Invalid combination of types in array subscripting."));
+      gen_deref (ax, value);
+      break;
+    case BINOP_BITWISE_AND:
+      gen_binop (ax, value, value1, value2,
+                aop_bit_and, aop_bit_and, 0, "bitwise and");
+      break;
+
+    case BINOP_BITWISE_IOR:
+      gen_binop (ax, value, value1, value2,
+                aop_bit_or, aop_bit_or, 0, "bitwise or");
+      break;
+      
+    case BINOP_BITWISE_XOR:
+      gen_binop (ax, value, value1, value2,
+                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.  */
+      internal_error (__FILE__, __LINE__,
+                     _("gen_expr: op case sets don't match"));
+    }
+}
 \f
 
 /* Given a single variable and a scope, generate bytecodes to trace
index 65b5ba1..606e049 100644 (file)
@@ -272,6 +272,22 @@ ax_reg (struct agent_expr *x, int reg)
   x->buf[x->len + 2] = (reg) & 0xff;
   x->len += 3;
 }
+
+/* Assemble code to operate on a trace state variable.  */
+
+void
+ax_tsv (struct agent_expr *x, enum agent_op op, int num)
+{
+  /* Make sure the tsv number is in range.  */
+  if (num < 0 || num > 0xffff)
+    internal_error (__FILE__, __LINE__, _("ax-general.c (ax_tsv): variable number is %d, out of range"), num);
+
+  grow_expr (x, 3);
+  x->buf[x->len] = op;
+  x->buf[x->len + 1] = (num >> 8) & 0xff;
+  x->buf[x->len + 2] = (num) & 0xff;
+  x->len += 3;
+}
 \f
 
 
@@ -324,9 +340,9 @@ struct aop_map aop_map[] =
   {"pop", 0, 0, 1, 0},         /* 0x29 */
   {"zero_ext", 1, 0, 1, 1},    /* 0x2a */
   {"swap", 0, 0, 2, 2},                /* 0x2b */
-  {0, 0, 0, 0, 0},             /* 0x2c */
-  {0, 0, 0, 0, 0},             /* 0x2d */
-  {0, 0, 0, 0, 0},             /* 0x2e */
+  {"getv", 2, 0, 0, 1},                /* 0x2c */
+  {"setv", 2, 0, 0, 1},                /* 0x2d */
+  {"tracev", 2, 0, 0, 1},      /* 0x2e */
   {0, 0, 0, 0, 0},             /* 0x2f */
   {"trace16", 2, 0, 1, 1},     /* 0x30 */
 };
index 7fea3ce..844af83 100644 (file)
--- a/gdb/ax.h
+++ b/gdb/ax.h
@@ -131,6 +131,9 @@ enum agent_op
     aop_pop = 0x29,
     aop_zero_ext = 0x2a,
     aop_swap = 0x2b,
+    aop_getv = 0x2c,
+    aop_setv = 0x2d,
+    aop_tracev = 0x2e,
     aop_trace16 = 0x30,
     aop_last
   };
@@ -182,6 +185,9 @@ extern void ax_const_d (struct agent_expr *EXPR, LONGEST d);
 /* Assemble code to push the value of register number REG on the
    stack.  */
 extern void ax_reg (struct agent_expr *EXPR, 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);
 \f
 
 /* Functions for printing out expressions, and otherwise debugging
index bd4d5d1..d02f7ec 100644 (file)
@@ -1,3 +1,10 @@
+2009-12-28  Stan Shebs  <stan@codesourcery.com>
+
+       * gdb.texinfo (Trace State Variables): New section.
+       (Tracepoint Packets): Describe trace state variable packets.
+       * agentexpr.texi (Bytecode Descriptions): Describe trace state
+       variable bytecodes.
+
 2009-12-28  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.texinfo (Symbols): "info variables" prints definitions, not
index e3d171d..37c65f8 100644 (file)
@@ -440,6 +440,24 @@ alignment within the bytecode stream; thus, on machines where fetching a
 16-bit on an unaligned address raises an exception, you should fetch the
 register number one byte at a time.
 
+@item @code{getv} (0x2c) @var{n}: @result{} @var{v}
+Push the value of trace state variable number @var{n}, without sign
+extension.
+
+The variable number @var{n} is encoded as a 16-bit unsigned integer
+immediately following the @code{getv} bytecode.  It is always stored most
+significant byte first, regardless of the target's normal endianness.
+The variable number is not guaranteed to fall at any particular
+alignment within the bytecode stream; thus, on machines where fetching a
+16-bit on an unaligned address raises an exception, you should fetch the
+register number one byte at a time.
+
+@item @code{setv} (0x2d) @var{n}: @result{} @var{v}
+Set trace state variable number @var{n} to the value found on the top
+of the stack.  The stack is unchanged, so that the value is readily
+available if the assignment is part of a larger expression.  The
+handling of @var{n} is as described for @code{getv}.
+
 @item @code{trace} (0x0c): @var{addr} @var{size} @result{}
 Record the contents of the @var{size} bytes at @var{addr} in a trace
 buffer, for later retrieval by GDB.
@@ -457,6 +475,10 @@ Identical to trace_quick, except that @var{size} is a 16-bit big-endian
 unsigned integer, not a single byte.  This should probably have been
 named @code{trace_quick16}, for consistency.
 
+@item @code{tracev} (0x2e) @var{n}: @result{} @var{a}
+Record the value of trace state variable number @var{n} in the trace
+buffer.  The handling of @var{n} is as described for @code{getv}.
+
 @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 308834f..7d3a35c 100644 (file)
@@ -9331,6 +9331,7 @@ conditions and actions.
 * Enable and Disable Tracepoints::
 * Tracepoint Passcounts::
 * Tracepoint Conditions::
+* Trace State Variables::
 * Tracepoint Actions::
 * Listing Tracepoints::
 * Starting and Stopping Trace Experiments::
@@ -9497,6 +9498,59 @@ search through.
 (@value{GDBP}) @kbd{trace normal_operation if errcode > 0}
 @end smallexample
 
+@node Trace State Variables
+@subsection Trace State Variables
+@cindex trace state variables
+
+A @dfn{trace state variable} is a special type of variable that is
+created and managed by target-side code.  The syntax is the same as
+that for GDB's convenience variables (a string prefixed with ``$''),
+but they are stored on the target.  They must be created explicitly,
+using a @code{tvariable} command.  They are always 64-bit signed
+integers.
+
+Trace state variables are remembered by @value{GDBN}, and downloaded
+to the target along with tracepoint information when the trace
+experiment starts.  There are no intrinsic limits on the number of
+trace state variables, beyond memory limitations of the target.
+
+@cindex convenience variables, and trace state variables
+Although trace state variables are managed by the target, you can use
+them in print commands and expressions as if they were convenience
+variables; @value{GDBN} will get the current value from the target
+while the trace experiment is running.  Trace state variables share
+the same namespace as other ``$'' variables, which means that you
+cannot have trace state variables with names like @code{$23} or
+@code{$pc}, nor can you have a trace state variable and a convenience
+variable with the same name.
+
+@table @code
+
+@item tvariable $@var{name} [ = @var{expression} ]
+@kindex tvariable
+The @code{tvariable} command creates a new trace state variable named
+@code{$@var{name}}, and optionally gives it an initial value of
+@var{expression}.  @var{expression} is evaluated when this command is
+entered; the result will be converted to an integer if possible,
+otherwise @value{GDBN} will report an error. A subsequent
+@code{tvariable} command specifying the same name does not create a
+variable, but instead assigns the supplied initial value to the
+existing variable of that name, overwriting any previous initial
+value. The default initial value is 0.
+
+@item info tvariables
+@kindex info tvariables
+List all the trace state variables along with their initial values.
+Their current values may also be displayed, if the trace experiment is
+currently running.
+
+@item delete tvariable @r{[} $@var{name} @dots{} @r{]}
+@kindex delete tvariable
+Delete the given trace state variables, or all of them if no arguments
+are specified.
+
+@end table
+
 @node Tracepoint Actions
 @subsection Tracepoint Action Lists
 
@@ -9929,7 +9983,8 @@ use @code{output} instead.
 
 Here's a simple example of using these convenience variables for
 stepping through all the trace snapshots and printing some of their
-data.
+data.  Note that these are not the same as trace state variables,
+which are managed by the target.
 
 @smallexample
 (@value{GDBP}) @b{tfind start}
@@ -29978,6 +30033,16 @@ The packet was understood and carried out.
 The packet was not recognized.
 @end table
 
+@item QTDV:@var{n}:@var{value}
+@cindex define trace state variable, remote request
+@cindex @samp{QTDV} packet
+Create a new trace state variable, number @var{n}, with an initial
+value of @var{value}, which is a 64-bit signed integer.  Both @var{n}
+and @var{value} are encoded as hexadecimal values. @value{GDBN} has
+the option of not using this packet for initial values of zero; the
+target should simply create the trace state variables as they are
+mentioned in expressions.
+
 @item QTFrame:@var{n}
 Select the @var{n}'th tracepoint frame from the buffer, and use the
 register and memory contents recorded there to answer subsequent
@@ -30051,8 +30116,28 @@ There is no trace experiment running.
 There is a trace experiment running.
 @end table
 
+@item qTV:@var{var}
+@cindex trace state variable value, remote request
+@cindex @samp{qTV} packet
+Ask the stub for the value of the trace state variable number @var{var}.
+
+Replies:
+@table @samp
+@item V@var{value}
+The value of the variable is @var{value}.  This will be the current
+value of the variable if the user is examining a running target, or a
+saved value if the variable was collected in the trace frame that the
+user is looking at.  Note that multiple requests may result in
+different reply values, such as when requesting values while the
+program is running.
+
+@item U
+The value of the variable is unknown.  This would occur, for example,
+if the user is examining a trace frame in which the requested variable
+was not collected.
 @end table
 
+@end table
 
 @node Host I/O Packets
 @section Host I/O Packets
index efebac1..a170dcf 100644 (file)
@@ -1,3 +1,8 @@
+2009-12-28  Stan Shebs  <stan@codesourcery.com>
+
+       * gdb.trace/tsv.exp: New file.
+       * gdb.base/completion.exp: Update ambiguous info output.
+       
 2009-12-28  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * gdb.base/find.c (main): Reference search buffers.
index 5021b67..00f4bd1 100644 (file)
@@ -211,7 +211,7 @@ gdb_expect  {
         -re "^info t foo\\\x07$"\
             { send_gdb "\n"
               gdb_expect {
-                      -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, types\\..*$gdb_prompt $"\
+                      -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $"\
                                         { pass "complete 'info t foo'"}
                       -re ".*$gdb_prompt $" { fail "complete 'info t foo'"}
                       timeout           {fail "(timeout) complete 'info t foo'"}
@@ -227,7 +227,7 @@ gdb_expect  {
         -re "^info t\\\x07$"\
             { send_gdb "\n"
               gdb_expect {
-                      -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, types\\..
+                      -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..
 *$gdb_prompt $"\
                                         { pass "complete 'info t'"}
                       -re ".*$gdb_prompt $" { fail "complete 'info t'"}
@@ -245,7 +245,7 @@ gdb_expect  {
         -re "^info t \\\x07$"\
             { send_gdb "\n"
               gdb_expect {
-                      -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, types\\..
+                      -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..
 *$gdb_prompt $"\
                                         { pass "complete 'info t '"}
                       -re ".*$gdb_prompt $" { fail "complete 'info t '"}
diff --git a/gdb/testsuite/gdb.trace/tsv.exp b/gdb/testsuite/gdb.trace/tsv.exp
new file mode 100644 (file)
index 0000000..5bab91d
--- /dev/null
@@ -0,0 +1,107 @@
+#   Copyright 2009 Free Software Foundation, Inc.
+#
+# 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/>.
+
+load_lib "trace-support.exp";
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+gdb_exit
+gdb_start
+set testfile "actions"
+set srcfile ${testfile}.c
+set binfile $objdir/$subdir/tsv
+if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \
+         executable {debug nowarnings}] != "" } {
+    untested tracecmd.exp
+    return -1
+}
+gdb_reinitialize_dir $srcdir/$subdir
+
+# If testing on a remote host, download the source file.
+# remote_download host $srcdir/$subdir/$srcfile
+
+gdb_file_cmd $binfile
+
+gdb_test "tvariable \$tvar1" \
+  "Trace state variable \\\$tvar1 created, with initial value 0." \
+  "Create a trace state variable"
+
+gdb_test "tvariable \$tvar2 = 45" \
+  "Trace state variable \\\$tvar2 created, with initial value 45." \
+  "Create a trace state variable with initial value"
+
+gdb_test "tvariable \$tvar2 = -92" \
+  "Trace state variable \\\$tvar2 now has initial value -92." \
+  "Change initial value of a trace state variable"
+
+gdb_test "tvariable \$tvar3 = 2 + 3" \
+  "Trace state variable \\\$tvar3 created, with initial value 5." \
+  "Create a trace state variable with expression"
+
+gdb_test "tvariable \$tvar3 = 1234567000000" \
+  "Trace state variable \\\$tvar3 now has initial value 1234567000000." \
+  "Init trace state variable to a 64-bit value"
+
+gdb_test "tvariable main" \
+  "Syntax must be \\\$NAME \\\[ = EXPR \\\]" \
+  "tvariable syntax error, bad name"
+
+gdb_test "tvariable \$tvar1 - 93" \
+  "Syntax must be \\\$NAME \\\[ = EXPR \\\]" \
+  "tvariable syntax error, not an assignment"
+
+gdb_test "info tvariables" \
+    "Name\[\t \]+Initial\[\t \]+Current.*
+\\\$tvar1\[\t \]+0\[\t \]+<undefined>.*
+\\\$tvar2\[\t \]+-92\[\t \]+<undefined>.*
+\\\$tvar3\[\t \]+1234567000000\[\t \]+.*<undefined>.*" \
+  "List tvariables"
+
+gdb_test "delete tvariable \$tvar2" \
+  "" \
+  "delete trace state variable"
+
+gdb_test "info tvariables" \
+    "Name\[\t \]+Initial\[\t \]+Current.*
+\\\$tvar1\[\t \]+0\[\t \]+<undefined>.*
+\\\$tvar3\[\t \]+1234567000000\[\t \]+.*<undefined>.*" \
+  "List tvariables after deletion"
+
+send_gdb "delete tvariable\n"
+gdb_expect 30 {
+    -re "Delete all trace state variables.*y or n.*$" {
+       send_gdb "y\n"
+       gdb_expect 30 {
+           -re "$gdb_prompt $" {
+               pass "Delete all trace state variables"
+           }
+           timeout { fail "Delete all trace state variables (timeout)" }
+       }
+    }
+    -re "$gdb_prompt $" { # This happens if there were no variables
+    }
+    timeout { perror "Delete all trace state variables (timeout)" ; return }
+}
+
+gdb_test "info tvariables" \
+  "No trace state variables.*" \
+  "List tvariables after deleting all"
+
+
index 87e7882..702d348 100644 (file)
@@ -34,6 +34,7 @@
 #include "tracepoint.h"
 #include "remote.h"
 extern int remote_supports_cond_tracepoints (void);
+extern char *unpack_varlen_hex (char *buff, ULONGEST *result);
 #include "linespec.h"
 #include "regcache.h"
 #include "completer.h"
@@ -111,6 +112,19 @@ extern void output_command (char *, int);
 
 /* ======= Important global variables: ======= */
 
+/* The list of all trace state variables.  We don't retain pointers to
+   any of these for any reason - API is by name or number only - so it
+   works to have a vector of objects.  */
+
+typedef struct trace_state_variable tsv_s;
+DEF_VEC_O(tsv_s);
+
+static VEC(tsv_s) *tvariables;
+
+/* The next integer to assign to a variable.  */
+
+static int next_tsv_number = 1;
+
 /* Number of last traceframe collected.  */
 static int traceframe_number;
 
@@ -126,6 +140,9 @@ static struct symtab_and_line traceframe_sal;
 /* Tracing command lists */
 static struct cmd_list_element *tfindlist;
 
+static char *target_buf;
+static long target_buf_size;
+
 /* ======= Important command functions: ======= */
 static void trace_actions_command (char *, int);
 static void trace_start_command (char *, int);
@@ -274,6 +291,205 @@ set_traceframe_context (struct frame_info *trace_frame)
                            traceframe_sal.symtab->filename);
 }
 
+/* Create a new trace state variable with the given name.  */
+
+struct trace_state_variable *
+create_trace_state_variable (const char *name)
+{
+  struct trace_state_variable tsv;
+
+  memset (&tsv, 0, sizeof (tsv));
+  tsv.name = name;
+  tsv.number = next_tsv_number++;
+  return VEC_safe_push (tsv_s, tvariables, &tsv);
+}
+
+/* Look for a trace state variable of the given name.  */
+
+struct trace_state_variable *
+find_trace_state_variable (const char *name)
+{
+  struct trace_state_variable *tsv;
+  int ix;
+
+  for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+    if (strcmp (name, tsv->name) == 0)
+      return tsv;
+
+  return NULL;
+}
+
+void
+delete_trace_state_variable (const char *name)
+{
+  struct trace_state_variable *tsv;
+  int ix;
+
+  for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+    if (strcmp (name, tsv->name) == 0)
+      {
+       VEC_unordered_remove (tsv_s, tvariables, ix);
+       return;
+      }
+
+  warning (_("No trace variable named \"$%s\", not deleting"), name);
+}
+
+/* The 'tvariable' command collects a name and optional expression to
+   evaluate into an initial value.  */
+
+void
+trace_variable_command (char *args, int from_tty)
+{
+  struct expression *expr;
+  struct cleanup *old_chain;
+  struct internalvar *intvar = NULL;
+  LONGEST initval = 0;
+  struct trace_state_variable *tsv;
+
+  if (!args || !*args)
+    error_no_arg (_("trace state variable name"));
+
+  /* All the possible valid arguments are expressions.  */
+  expr = parse_expression (args);
+  old_chain = make_cleanup (free_current_contents, &expr);
+
+  if (expr->nelts == 0)
+    error (_("No expression?"));
+
+  /* Only allow two syntaxes; "$name" and "$name=value".  */
+  if (expr->elts[0].opcode == OP_INTERNALVAR)
+    {
+      intvar = expr->elts[1].internalvar;
+    }
+  else if (expr->elts[0].opcode == BINOP_ASSIGN
+          && expr->elts[1].opcode == OP_INTERNALVAR)
+    {
+      intvar = expr->elts[2].internalvar;
+      initval = value_as_long (evaluate_subexpression_type (expr, 4));
+    }
+  else
+    error (_("Syntax must be $NAME [ = EXPR ]"));
+
+  if (!intvar)
+    error (_("No name given"));
+
+  if (strlen (internalvar_name (intvar)) <= 0)
+    error (_("Must supply a non-empty variable name"));
+
+  /* If the variable already exists, just change its initial value.  */
+  tsv = find_trace_state_variable (internalvar_name (intvar));
+  if (tsv)
+    {
+      tsv->initial_value = initval;
+      printf_filtered (_("Trace state variable $%s now has initial value %s.\n"),
+                      tsv->name, plongest (tsv->initial_value));
+      return;
+    }
+
+  /* Create a new variable.  */
+  tsv = create_trace_state_variable (internalvar_name (intvar));
+  tsv->initial_value = initval;
+
+  printf_filtered (_("Trace state variable $%s created, with initial value %s.\n"),
+                  tsv->name, plongest (tsv->initial_value));
+
+  do_cleanups (old_chain);
+}
+
+void
+delete_trace_variable_command (char *args, int from_tty)
+{
+  int i, ix;
+  char **argv;
+  struct cleanup *back_to;
+  struct trace_state_variable *tsv;
+
+  if (args == NULL)
+    {
+      if (query (_("Delete all trace state variables? ")))
+       VEC_free (tsv_s, tvariables);
+      dont_repeat ();
+      return;
+    }
+
+  argv = gdb_buildargv (args);
+  back_to = make_cleanup_freeargv (argv);
+
+  for (i = 0; argv[i] != NULL; i++)
+    {
+      if (*argv[i] == '$')
+       delete_trace_state_variable (argv[i] + 1);
+      else
+       warning (_("Name \"%s\" not prefixed with '$', ignoring"), argv[i]);
+    }
+
+  do_cleanups (back_to);
+
+  dont_repeat ();
+}
+
+/* List all the trace state variables.  */
+
+static void
+tvariables_info (char *args, int from_tty)
+{
+  struct trace_state_variable *tsv;
+  int ix;
+  char *reply;
+  ULONGEST tval;
+
+  if (target_is_remote ())
+    {
+      char buf[20];
+
+      for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+       {
+         /* We don't know anything about the value until we get a
+            valid packet.  */
+         tsv->value_known = 0;
+         sprintf (buf, "qTV:%x", tsv->number);
+         putpkt (buf);
+         reply = remote_get_noisy_reply (&target_buf, &target_buf_size);
+         if (reply && *reply)
+           {
+             if (*reply == 'V')
+               {
+                 unpack_varlen_hex (reply + 1, &tval);
+                 tsv->value = (LONGEST) tval;
+                 tsv->value_known = 1;
+               }
+             /* FIXME say anything about oddball replies? */
+           }
+       }
+    }
+
+  if (VEC_length (tsv_s, tvariables) == 0)
+    {
+      printf_filtered (_("No trace state variables.\n"));
+      return;
+    }
+
+  printf_filtered (_("Name\t\t  Initial\tCurrent\n"));
+
+  for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+    {
+      printf_filtered ("$%s", tsv->name);
+      print_spaces_filtered (17 - strlen (tsv->name), gdb_stdout);
+      printf_filtered ("%s ", plongest (tsv->initial_value));
+      print_spaces_filtered (11 - strlen (plongest (tsv->initial_value)), gdb_stdout);
+      if (tsv->value_known)
+       printf_filtered ("  %s", plongest (tsv->value));
+      else if (trace_running_p || traceframe_number >= 0)
+       /* The value is/was defined, but we don't have it.  */
+       printf_filtered (_("  <unknown>"));
+      else
+       /* It is not meaningful to ask about the value.  */
+       printf_filtered (_("  <undefined>"));
+      printf_filtered ("\n");
+    }
+}
+
 /* ACTIONS functions: */
 
 /* Prototypes for action-parsing utility commands  */
@@ -1254,9 +1470,6 @@ add_aexpr (struct collection_list *collect, struct agent_expr *aexpr)
   collect->next_aexpr_elt++;
 }
 
-static char *target_buf;
-static long target_buf_size;
-
 /* Set "transparent" memory ranges
 
    Allow trace mechanism to treat text-like sections
@@ -1312,9 +1525,11 @@ void download_tracepoint (struct breakpoint *t);
 static void
 trace_start_command (char *args, int from_tty)
 {
+  char buf[2048];
   VEC(breakpoint_p) *tp_vec = NULL;
   int ix;
   struct breakpoint *t;
+  struct trace_state_variable *tsv;
 
   dont_repeat ();      /* Like "run", dangerous to repeat accidentally.  */
 
@@ -1332,6 +1547,19 @@ trace_start_command (char *args, int from_tty)
        }
       VEC_free (breakpoint_p, tp_vec);
 
+      /* Init any trace state variables that start with nonzero values.  */
+
+      for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+       {
+         if (tsv->initial_value != 0)
+           {
+             sprintf (buf, "QTDV:%x:%s",
+                      tsv->number, phex ((ULONGEST) tsv->initial_value, 8));
+             putpkt (buf);
+             remote_get_noisy_reply (&target_buf, &target_buf_size);
+           }
+       }
+
       /* Tell target to treat text-like sections as transparent.  */
       remote_set_transparent_ranges ();
       /* Now insert traps and begin collecting data.  */
@@ -2235,6 +2463,23 @@ _initialize_tracepoint (void)
   add_com ("tdump", class_trace, trace_dump_command,
           _("Print everything collected at the current tracepoint."));
 
+  c = add_com ("tvariable", class_trace, trace_variable_command,_("\
+Define a trace state variable.\n\
+Argument is a $-prefixed name, optionally followed\n\
+by '=' and an expression that sets the initial value\n\
+at the start of tracing."));
+  set_cmd_completer (c, expression_completer);
+
+  add_cmd ("tvariable", class_trace, delete_trace_variable_command, _("\
+Delete one or more trace state variables.\n\
+Arguments are the names of the variables to delete.\n\
+If no arguments are supplied, delete all variables."), &deletelist);
+  /* FIXME add a trace variable completer */
+
+  add_info ("tvariables", tvariables_info, _("\
+Status of trace state variables and their values.\n\
+"));
+
   add_prefix_cmd ("tfind", class_trace, trace_find_command, _("\
 Select a trace frame;\n\
 No argument means forward by one frame; '-' means backward by one frame."),
index 8bdd1f7..f23bb53 100644 (file)
@@ -35,6 +35,34 @@ enum actionline_type
     STEPPING = 2
   };
 
+/* A trace state variable is a value managed by a target being
+   traced. A trace state variable (or tsv for short) can be accessed
+   and assigned to by tracepoint actions and conditionals, but is not
+   part of the program being traced, and it doesn't have to be
+   collected. Effectively the variables are scratch space for
+   tracepoints.  */
+
+struct trace_state_variable
+  {
+    /* The variable's name.  The user has to prefix with a dollar sign,
+       but we don't store that internally.  */
+    const char *name;
+
+    /* An id number assigned by GDB, and transmitted to targets.  */
+    int number;
+
+    /* The initial value of a variable is a 64-bit signed integer.  */
+    LONGEST initial_value;
+
+    /* 1 if the value is known, else 0.  The value is known during a
+       trace run, or in tfind mode if the variable was collected into
+       the current trace frame.  */
+    int value_known;
+
+    /* The value of a variable is a 64-bit signed integer.  */
+    LONGEST value;
+  };
+
 extern unsigned long trace_running_p;
 
 /* A hook used to notify the UI of tracepoint operations.  */
@@ -49,4 +77,6 @@ enum actionline_type validate_actionline (char **, struct breakpoint *);
 extern void end_actions_pseudocommand (char *args, int from_tty);
 extern void while_stepping_pseudocommand (char *args, int from_tty);
 
+extern struct trace_state_variable *find_trace_state_variable (const char *name);
+
 #endif /* TRACEPOINT_H */