This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] Trace state variables
- From: Stan Shebs <stan at codesourcery dot com>
- To: Stan Shebs <stan at codesourcery dot com>
- Cc: gdb-patches at sourceware dot org
- Date: Tue, 29 Dec 2009 09:21:18 -0800
- Subject: Re: [PATCH] Trace state variables
- References: <4B279456.1010508@codesourcery.com>
Here's what I committed. I incorporated the feedback (thanks!),
including adding BINOP_ASSIGN_MODIFY (messier than one would think,
required refactoring binary operation compilation), and added a NEWS
item. Now on to the next new tracing feature! :-)
Stan
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.
* gdb.texinfo (Trace State Variables): New section.
(Tracepoint Packets): Describe trace state variable packets.
* agentexpr.texi (Bytecode Descriptions): Describe trace state
variable bytecodes.
* gdb.trace/tsv.exp: New file.
* gdb.base/completion.exp: Update ambiguous info output.
Index: NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.341
diff -p -r1.341 NEWS
*** NEWS 28 Dec 2009 21:29:53 -0000 1.341
--- NEWS 28 Dec 2009 23:37:28 -0000
*************** Renesas RX rx
*** 24,29 ****
--- 24,42 ----
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
*************** set remotebreak [on | off]
*** 75,80 ****
--- 88,102 ----
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
*************** show follow-exec-mode
*** 83,88 ****
--- 105,118 ----
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: ax-gdb.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.c,v
retrieving revision 1.56
diff -p -r1.56 ax-gdb.c
*** ax-gdb.c 28 Dec 2009 16:49:14 -0000 1.56
--- ax-gdb.c 28 Dec 2009 23:37:28 -0000
***************
*** 36,41 ****
--- 36,42 ----
#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,
*************** static void gen_sizeof (struct expressio
*** 139,144 ****
--- 140,151 ----
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);
*************** gen_expr (struct expression *exp, union
*** 1441,1447 ****
{
/* Used to hold the descriptions of operand expressions. */
struct axs_value value1, value2;
! enum exp_opcode op = (*pc)[0].opcode;
/* If we're looking at a constant expression, just push its value. */
{
--- 1448,1454 ----
{
/* Used to hold the descriptions of operand expressions. */
struct axs_value value1, value2;
! enum exp_opcode op = (*pc)[0].opcode, op2;
/* If we're looking at a constant expression, just push its value. */
{
*************** gen_expr (struct expression *exp, union
*** 1478,1596 ****
(*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)
{
! 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"));
}
break;
/* Note that we need to be a little subtle about generating code
--- 1485,1547 ----
(*pc)++;
gen_expr (exp, pc, ax, &value1);
gen_usual_unary (exp, ax, &value1);
! gen_expr_binop_rest (exp, op, pc, ax, value, &value1, &value2);
! break;
!
! case BINOP_ASSIGN:
! (*pc)++;
! if ((*pc)[0].opcode == OP_INTERNALVAR)
{
! 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)
{
! 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;
! 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
*************** gen_expr (struct expression *exp, union
*** 1644,1650 ****
break;
case OP_INTERNALVAR:
! error (_("GDB agent expressions cannot use convenience variables."));
/* Weirdo operator: see comments for gen_repeat for details. */
case BINOP_REPEAT:
--- 1595,1618 ----
break;
case OP_INTERNALVAR:
! {
! 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:
*************** gen_expr (struct expression *exp, union
*** 1788,1793 ****
--- 1756,1886 ----
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"));
+ }
+ }
/* Given a single variable and a scope, generate bytecodes to trace
Index: ax-general.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-general.c,v
retrieving revision 1.15
diff -p -r1.15 ax-general.c
*** ax-general.c 3 Jan 2009 05:57:50 -0000 1.15
--- ax-general.c 28 Dec 2009 23:37:28 -0000
*************** ax_reg (struct agent_expr *x, int reg)
*** 272,277 ****
--- 272,293 ----
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;
+ }
*************** struct aop_map aop_map[] =
*** 324,332 ****
{"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 */
{0, 0, 0, 0, 0}, /* 0x2f */
{"trace16", 2, 0, 1, 1}, /* 0x30 */
};
--- 340,348 ----
{"pop", 0, 0, 1, 0}, /* 0x29 */
{"zero_ext", 1, 0, 1, 1}, /* 0x2a */
{"swap", 0, 0, 2, 2}, /* 0x2b */
! {"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: ax.h
===================================================================
RCS file: /cvs/src/src/gdb/ax.h,v
retrieving revision 1.10
diff -p -r1.10 ax.h
*** ax.h 3 Jan 2009 05:57:50 -0000 1.10
--- ax.h 28 Dec 2009 23:37:28 -0000
*************** enum agent_op
*** 131,136 ****
--- 131,139 ----
aop_pop = 0x29,
aop_zero_ext = 0x2a,
aop_swap = 0x2b,
+ aop_getv = 0x2c,
+ aop_setv = 0x2d,
+ aop_tracev = 0x2e,
aop_trace16 = 0x30,
aop_last
};
*************** extern void ax_const_d (struct agent_exp
*** 182,187 ****
--- 185,193 ----
/* 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);
/* Functions for printing out expressions, and otherwise debugging
Index: tracepoint.c
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.c,v
retrieving revision 1.128
diff -p -r1.128 tracepoint.c
*** tracepoint.c 24 Dec 2009 00:40:49 -0000 1.128
--- tracepoint.c 28 Dec 2009 23:37:28 -0000
***************
*** 34,39 ****
--- 34,40 ----
#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"
*************** extern void output_command (char *, int)
*** 111,116 ****
--- 112,130 ----
/* ======= 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;
*************** static struct symtab_and_line traceframe
*** 126,131 ****
--- 140,148 ----
/* 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);
*************** set_traceframe_context (struct frame_inf
*** 274,279 ****
--- 291,495 ----
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 */
*************** add_aexpr (struct collection_list *colle
*** 1254,1262 ****
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
--- 1470,1475 ----
*************** void download_tracepoint (struct breakpo
*** 1312,1320 ****
--- 1525,1535 ----
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. */
*************** trace_start_command (char *args, int fro
*** 1332,1337 ****
--- 1547,1565 ----
}
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. */
*************** _initialize_tracepoint (void)
*** 2235,2240 ****
--- 2463,2485 ----
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: tracepoint.h
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.h,v
retrieving revision 1.16
diff -p -r1.16 tracepoint.h
*** tracepoint.h 23 Dec 2009 23:21:29 -0000 1.16
--- tracepoint.h 28 Dec 2009 23:37:28 -0000
*************** enum actionline_type
*** 35,40 ****
--- 35,68 ----
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. */
*************** enum actionline_type validate_actionline
*** 49,52 ****
--- 77,82 ----
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 */
Index: doc/agentexpr.texi
===================================================================
RCS file: /cvs/src/src/gdb/doc/agentexpr.texi,v
retrieving revision 1.9
diff -p -r1.9 agentexpr.texi
*** doc/agentexpr.texi 11 Nov 2009 15:08:50 -0000 1.9
--- doc/agentexpr.texi 28 Dec 2009 23:37:28 -0000
*************** alignment within the bytecode stream; th
*** 440,445 ****
--- 440,463 ----
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.
*************** Identical to trace_quick, except that @v
*** 457,462 ****
--- 475,484 ----
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: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.650
diff -p -r1.650 gdb.texinfo
*** doc/gdb.texinfo 28 Dec 2009 21:29:53 -0000 1.650
--- doc/gdb.texinfo 28 Dec 2009 23:37:29 -0000
*************** conditions and actions.
*** 9331,9336 ****
--- 9331,9337 ----
* Enable and Disable Tracepoints::
* Tracepoint Passcounts::
* Tracepoint Conditions::
+ * Trace State Variables::
* Tracepoint Actions::
* Listing Tracepoints::
* Starting and Stopping Trace Experiments::
*************** search through.
*** 9497,9502 ****
--- 9498,9556 ----
(@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
*************** use @code{output} instead.
*** 9929,9935 ****
Here's a simple example of using these convenience variables for
stepping through all the trace snapshots and printing some of their
! data.
@smallexample
(@value{GDBP}) @b{tfind start}
--- 9983,9990 ----
Here's a simple example of using these convenience variables for
stepping through all the trace snapshots and printing some of their
! data. Note that these are not the same as trace state variables,
! which are managed by the target.
@smallexample
(@value{GDBP}) @b{tfind start}
*************** The packet was understood and carried ou
*** 29978,29983 ****
--- 30033,30048 ----
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
*************** There is no trace experiment running.
*** 30051,30058 ****
--- 30116,30143 ----
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: testsuite/gdb.base/completion.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/completion.exp,v
retrieving revision 1.39
diff -p -r1.39 completion.exp
*** testsuite/gdb.base/completion.exp 13 Jul 2009 19:24:18 -0000 1.39
--- testsuite/gdb.base/completion.exp 28 Dec 2009 23:37:29 -0000
*************** gdb_expect {
*** 211,217 ****
-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 $"\
{ pass "complete 'info t foo'"}
-re ".*$gdb_prompt $" { fail "complete 'info t foo'"}
timeout {fail "(timeout) complete 'info t foo'"}
--- 211,217 ----
-re "^info t foo\\\x07$"\
{ send_gdb "\n"
gdb_expect {
! -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'"}
*************** gdb_expect {
*** 227,233 ****
-re "^info t\\\x07$"\
{ send_gdb "\n"
gdb_expect {
! -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, types\\..
*$gdb_prompt $"\
{ pass "complete 'info t'"}
-re ".*$gdb_prompt $" { fail "complete 'info t'"}
--- 227,233 ----
-re "^info t\\\x07$"\
{ send_gdb "\n"
gdb_expect {
! -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'"}
*************** gdb_expect {
*** 245,251 ****
-re "^info t \\\x07$"\
{ send_gdb "\n"
gdb_expect {
! -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, types\\..
*$gdb_prompt $"\
{ pass "complete 'info t '"}
-re ".*$gdb_prompt $" { fail "complete 'info t '"}
--- 245,251 ----
-re "^info t \\\x07$"\
{ send_gdb "\n"
gdb_expect {
! -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 '"}
Index: testsuite/gdb.trace/tsv.exp
===================================================================
RCS file: testsuite/gdb.trace/tsv.exp
diff -N testsuite/gdb.trace/tsv.exp
*** /dev/null 1 Jan 1970 00:00:00 -0000
--- testsuite/gdb.trace/tsv.exp 28 Dec 2009 23:37:29 -0000
***************
*** 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"
+
+