From f61e138d9a5c10da22c01ef377034e66e6978fe6 Mon Sep 17 00:00:00 2001 From: Stan Shebs Date: Mon, 28 Dec 2009 23:39:10 +0000 Subject: [PATCH] 2009-12-28 Stan Shebs 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 * 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 * gdb.trace/tsv.exp: New file. * gdb.base/completion.exp: Update ambiguous info output. --- gdb/ChangeLog | 26 +++ gdb/NEWS | 30 +++ gdb/ax-gdb.c | 311 +++++++++++++++++--------- gdb/ax-general.c | 22 +- gdb/ax.h | 6 + gdb/doc/ChangeLog | 7 + gdb/doc/agentexpr.texi | 22 ++ gdb/doc/gdb.texinfo | 87 ++++++- gdb/testsuite/ChangeLog | 5 + gdb/testsuite/gdb.base/completion.exp | 6 +- gdb/testsuite/gdb.trace/tsv.exp | 107 +++++++++ gdb/tracepoint.c | 251 ++++++++++++++++++++- gdb/tracepoint.h | 30 +++ 13 files changed, 791 insertions(+), 119 deletions(-) create mode 100644 gdb/testsuite/gdb.trace/tsv.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 6ccb88b8948..3bfe6882d14 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,29 @@ +2009-12-28 Stan Shebs + + 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 * NEWS: Document "info variables" change. diff --git a/gdb/NEWS b/gdb/NEWS index 8c0fd92b9ff..b680d9be6b7 100644 --- 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. diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c index a64658dd219..ee1f515ecb2 100644 --- a/gdb/ax-gdb.c +++ b/gdb/ax-gdb.c @@ -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); @@ -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")); + } +} /* Given a single variable and a scope, generate bytecodes to trace diff --git a/gdb/ax-general.c b/gdb/ax-general.c index 65b5ba151d7..606e04922d4 100644 --- a/gdb/ax-general.c +++ b/gdb/ax-general.c @@ -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; +} @@ -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 */ }; diff --git a/gdb/ax.h b/gdb/ax.h index 7fea3ce4c3a..844af8376a4 100644 --- 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); /* Functions for printing out expressions, and otherwise debugging diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index bd4d5d11d01..d02f7ec4f2e 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2009-12-28 Stan Shebs + + * 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 * gdb.texinfo (Symbols): "info variables" prints definitions, not diff --git a/gdb/doc/agentexpr.texi b/gdb/doc/agentexpr.texi index e3d171d9184..37c65f8b8cf 100644 --- a/gdb/doc/agentexpr.texi +++ b/gdb/doc/agentexpr.texi @@ -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 diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 308834f4423..7d3a35cb15f 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index efebac1b51c..a170dcf475a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2009-12-28 Stan Shebs + + * gdb.trace/tsv.exp: New file. + * gdb.base/completion.exp: Update ambiguous info output. + 2009-12-28 Daniel Jacobowitz * gdb.base/find.c (main): Reference search buffers. diff --git a/gdb/testsuite/gdb.base/completion.exp b/gdb/testsuite/gdb.base/completion.exp index 5021b679aa4..00f4bd137cb 100644 --- a/gdb/testsuite/gdb.base/completion.exp +++ b/gdb/testsuite/gdb.base/completion.exp @@ -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 index 00000000000..5bab91d419b --- /dev/null +++ b/gdb/testsuite/gdb.trace/tsv.exp @@ -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 . + +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 \]+.* +\\\$tvar2\[\t \]+-92\[\t \]+.* +\\\$tvar3\[\t \]+1234567000000\[\t \]+.*.*" \ + "List tvariables" + +gdb_test "delete tvariable \$tvar2" \ + "" \ + "delete trace state variable" + +gdb_test "info tvariables" \ + "Name\[\t \]+Initial\[\t \]+Current.* +\\\$tvar1\[\t \]+0\[\t \]+.* +\\\$tvar3\[\t \]+1234567000000\[\t \]+.*.*" \ + "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" + + diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index 87e78821269..702d348badf 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -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 (_(" ")); + else + /* It is not meaningful to ask about the value. */ + printf_filtered (_(" ")); + 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."), diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h index 8bdd1f7e7e0..f23bb53583d 100644 --- a/gdb/tracepoint.h +++ b/gdb/tracepoint.h @@ -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 */ -- 2.30.2