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. ==> 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.
This commit is contained in:
parent
ae77ee9a7f
commit
f61e138d9a
13 changed files with 792 additions and 120 deletions
|
@ -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>
|
2009-12-28 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* NEWS: Document "info variables" change.
|
* NEWS: Document "info variables" change.
|
||||||
|
|
30
gdb/NEWS
30
gdb/NEWS
|
@ -24,6 +24,19 @@ Renesas RX rx
|
||||||
lists inferiors that are not running yet or that have exited
|
lists inferiors that are not running yet or that have exited
|
||||||
already. See also "New commands" and "New options" below.
|
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
|
* Changed commands
|
||||||
|
|
||||||
disassemble
|
disassemble
|
||||||
|
@ -75,6 +88,15 @@ set remotebreak [on | off]
|
||||||
show remotebreak
|
show remotebreak
|
||||||
Deprecated. Use "set/show remote interrupt-sequence" instead.
|
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
|
* New options
|
||||||
|
|
||||||
set follow-exec-mode new|same
|
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
|
creates a new one. This is useful to be able to restart the old
|
||||||
executable after the inferior having done an exec call.
|
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
|
* Bug fixes
|
||||||
|
|
||||||
Process record now works correctly with hardware watchpoints.
|
Process record now works correctly with hardware watchpoints.
|
||||||
|
|
313
gdb/ax-gdb.c
313
gdb/ax-gdb.c
|
@ -36,6 +36,7 @@
|
||||||
#include "user-regs.h"
|
#include "user-regs.h"
|
||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "dictionary.h"
|
#include "dictionary.h"
|
||||||
|
#include "tracepoint.h"
|
||||||
|
|
||||||
/* To make sense of this file, you should read doc/agentexpr.texi.
|
/* 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,
|
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);
|
struct type *size_type);
|
||||||
static void gen_expr (struct expression *exp, union exp_element **pc,
|
static void gen_expr (struct expression *exp, union exp_element **pc,
|
||||||
struct agent_expr *ax, struct axs_value *value);
|
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);
|
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. */
|
/* Used to hold the descriptions of operand expressions. */
|
||||||
struct axs_value value1, value2;
|
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. */
|
/* 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)++;
|
(*pc)++;
|
||||||
gen_expr (exp, pc, ax, &value1);
|
gen_expr (exp, pc, ax, &value1);
|
||||||
gen_usual_unary (exp, ax, &value1);
|
gen_usual_unary (exp, ax, &value1);
|
||||||
gen_expr (exp, pc, ax, &value2);
|
gen_expr_binop_rest (exp, op, pc, ax, value, &value1, &value2);
|
||||||
gen_usual_unary (exp, ax, &value2);
|
break;
|
||||||
gen_usual_arithmetic (exp, ax, &value1, &value2);
|
|
||||||
switch (op)
|
case BINOP_ASSIGN:
|
||||||
|
(*pc)++;
|
||||||
|
if ((*pc)[0].opcode == OP_INTERNALVAR)
|
||||||
{
|
{
|
||||||
case BINOP_ADD:
|
char *name = internalvar_name ((*pc)[1].internalvar);
|
||||||
if (TYPE_CODE (value1.type) == TYPE_CODE_INT
|
struct trace_state_variable *tsv;
|
||||||
&& TYPE_CODE (value2.type) == TYPE_CODE_PTR)
|
(*pc) += 3;
|
||||||
|
gen_expr (exp, pc, ax, value);
|
||||||
|
tsv = find_trace_state_variable (name);
|
||||||
|
if (tsv)
|
||||||
{
|
{
|
||||||
/* Swap the values and proceed normally. */
|
ax_tsv (ax, aop_setv, tsv->number);
|
||||||
ax_simple (ax, aop_swap);
|
if (trace_kludge)
|
||||||
gen_ptradd (ax, value, &value2, &value1);
|
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
|
else
|
||||||
gen_binop (ax, value, &value1, &value2,
|
error (_("$%s is not a trace state variable, may not assign to it"), name);
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
|
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;
|
break;
|
||||||
|
|
||||||
/* Note that we need to be a little subtle about generating code
|
/* 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;
|
break;
|
||||||
|
|
||||||
case OP_INTERNALVAR:
|
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. */
|
/* Weirdo operator: see comments for gen_repeat for details. */
|
||||||
case BINOP_REPEAT:
|
case BINOP_REPEAT:
|
||||||
|
@ -1788,6 +1756,131 @@ gen_expr (struct expression *exp, union exp_element **pc,
|
||||||
error (_("Unsupported operator in expression."));
|
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
|
/* Given a single variable and a scope, generate bytecodes to trace
|
||||||
|
|
|
@ -272,6 +272,22 @@ ax_reg (struct agent_expr *x, int reg)
|
||||||
x->buf[x->len + 2] = (reg) & 0xff;
|
x->buf[x->len + 2] = (reg) & 0xff;
|
||||||
x->len += 3;
|
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 */
|
{"pop", 0, 0, 1, 0}, /* 0x29 */
|
||||||
{"zero_ext", 1, 0, 1, 1}, /* 0x2a */
|
{"zero_ext", 1, 0, 1, 1}, /* 0x2a */
|
||||||
{"swap", 0, 0, 2, 2}, /* 0x2b */
|
{"swap", 0, 0, 2, 2}, /* 0x2b */
|
||||||
{0, 0, 0, 0, 0}, /* 0x2c */
|
{"getv", 2, 0, 0, 1}, /* 0x2c */
|
||||||
{0, 0, 0, 0, 0}, /* 0x2d */
|
{"setv", 2, 0, 0, 1}, /* 0x2d */
|
||||||
{0, 0, 0, 0, 0}, /* 0x2e */
|
{"tracev", 2, 0, 0, 1}, /* 0x2e */
|
||||||
{0, 0, 0, 0, 0}, /* 0x2f */
|
{0, 0, 0, 0, 0}, /* 0x2f */
|
||||||
{"trace16", 2, 0, 1, 1}, /* 0x30 */
|
{"trace16", 2, 0, 1, 1}, /* 0x30 */
|
||||||
};
|
};
|
||||||
|
|
6
gdb/ax.h
6
gdb/ax.h
|
@ -131,6 +131,9 @@ enum agent_op
|
||||||
aop_pop = 0x29,
|
aop_pop = 0x29,
|
||||||
aop_zero_ext = 0x2a,
|
aop_zero_ext = 0x2a,
|
||||||
aop_swap = 0x2b,
|
aop_swap = 0x2b,
|
||||||
|
aop_getv = 0x2c,
|
||||||
|
aop_setv = 0x2d,
|
||||||
|
aop_tracev = 0x2e,
|
||||||
aop_trace16 = 0x30,
|
aop_trace16 = 0x30,
|
||||||
aop_last
|
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
|
/* Assemble code to push the value of register number REG on the
|
||||||
stack. */
|
stack. */
|
||||||
extern void ax_reg (struct agent_expr *EXPR, int REG);
|
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
|
/* Functions for printing out expressions, and otherwise debugging
|
||||||
|
|
|
@ -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>
|
2009-12-28 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* gdb.texinfo (Symbols): "info variables" prints definitions, not
|
* gdb.texinfo (Symbols): "info variables" prints definitions, not
|
||||||
|
|
|
@ -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
|
16-bit on an unaligned address raises an exception, you should fetch the
|
||||||
register number one byte at a time.
|
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{}
|
@item @code{trace} (0x0c): @var{addr} @var{size} @result{}
|
||||||
Record the contents of the @var{size} bytes at @var{addr} in a trace
|
Record the contents of the @var{size} bytes at @var{addr} in a trace
|
||||||
buffer, for later retrieval by GDB.
|
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
|
unsigned integer, not a single byte. This should probably have been
|
||||||
named @code{trace_quick16}, for consistency.
|
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{}
|
@item @code{end} (0x27): @result{}
|
||||||
Stop executing bytecode; the result should be the top element of the
|
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
|
stack. If the purpose of the expression was to compute an lvalue or a
|
||||||
|
|
|
@ -9331,6 +9331,7 @@ conditions and actions.
|
||||||
* Enable and Disable Tracepoints::
|
* Enable and Disable Tracepoints::
|
||||||
* Tracepoint Passcounts::
|
* Tracepoint Passcounts::
|
||||||
* Tracepoint Conditions::
|
* Tracepoint Conditions::
|
||||||
|
* Trace State Variables::
|
||||||
* Tracepoint Actions::
|
* Tracepoint Actions::
|
||||||
* Listing Tracepoints::
|
* Listing Tracepoints::
|
||||||
* Starting and Stopping Trace Experiments::
|
* Starting and Stopping Trace Experiments::
|
||||||
|
@ -9497,6 +9498,59 @@ search through.
|
||||||
(@value{GDBP}) @kbd{trace normal_operation if errcode > 0}
|
(@value{GDBP}) @kbd{trace normal_operation if errcode > 0}
|
||||||
@end smallexample
|
@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
|
@node Tracepoint Actions
|
||||||
@subsection Tracepoint Action Lists
|
@subsection Tracepoint Action Lists
|
||||||
|
|
||||||
|
@ -9929,7 +9983,8 @@ use @code{output} instead.
|
||||||
|
|
||||||
Here's a simple example of using these convenience variables for
|
Here's a simple example of using these convenience variables for
|
||||||
stepping through all the trace snapshots and printing some of their
|
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
|
@smallexample
|
||||||
(@value{GDBP}) @b{tfind start}
|
(@value{GDBP}) @b{tfind start}
|
||||||
|
@ -29978,6 +30033,16 @@ The packet was understood and carried out.
|
||||||
The packet was not recognized.
|
The packet was not recognized.
|
||||||
@end table
|
@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}
|
@item QTFrame:@var{n}
|
||||||
Select the @var{n}'th tracepoint frame from the buffer, and use the
|
Select the @var{n}'th tracepoint frame from the buffer, and use the
|
||||||
register and memory contents recorded there to answer subsequent
|
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.
|
There is a trace experiment running.
|
||||||
@end table
|
@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
|
||||||
|
|
||||||
|
@end table
|
||||||
|
|
||||||
@node Host I/O Packets
|
@node Host I/O Packets
|
||||||
@section Host I/O Packets
|
@section Host I/O Packets
|
||||||
|
|
|
@ -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>
|
2009-12-28 Daniel Jacobowitz <dan@codesourcery.com>
|
||||||
|
|
||||||
* gdb.base/find.c (main): Reference search buffers.
|
* gdb.base/find.c (main): Reference search buffers.
|
||||||
|
|
|
@ -211,7 +211,7 @@ gdb_expect {
|
||||||
-re "^info t foo\\\x07$"\
|
-re "^info t foo\\\x07$"\
|
||||||
{ send_gdb "\n"
|
{ send_gdb "\n"
|
||||||
gdb_expect {
|
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'"}
|
{ pass "complete 'info t foo'"}
|
||||||
-re ".*$gdb_prompt $" { fail "complete 'info t foo'"}
|
-re ".*$gdb_prompt $" { fail "complete 'info t foo'"}
|
||||||
timeout {fail "(timeout) complete 'info t foo'"}
|
timeout {fail "(timeout) complete 'info t foo'"}
|
||||||
|
@ -227,7 +227,7 @@ gdb_expect {
|
||||||
-re "^info t\\\x07$"\
|
-re "^info t\\\x07$"\
|
||||||
{ send_gdb "\n"
|
{ send_gdb "\n"
|
||||||
gdb_expect {
|
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 $"\
|
*$gdb_prompt $"\
|
||||||
{ pass "complete 'info t'"}
|
{ pass "complete 'info t'"}
|
||||||
-re ".*$gdb_prompt $" { fail "complete 'info t'"}
|
-re ".*$gdb_prompt $" { fail "complete 'info t'"}
|
||||||
|
@ -245,7 +245,7 @@ gdb_expect {
|
||||||
-re "^info t \\\x07$"\
|
-re "^info t \\\x07$"\
|
||||||
{ send_gdb "\n"
|
{ send_gdb "\n"
|
||||||
gdb_expect {
|
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 $"\
|
*$gdb_prompt $"\
|
||||||
{ pass "complete 'info t '"}
|
{ pass "complete 'info t '"}
|
||||||
-re ".*$gdb_prompt $" { fail "complete 'info t '"}
|
-re ".*$gdb_prompt $" { fail "complete 'info t '"}
|
||||||
|
|
107
gdb/testsuite/gdb.trace/tsv.exp
Normal file
107
gdb/testsuite/gdb.trace/tsv.exp
Normal file
|
@ -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"
|
||||||
|
|
||||||
|
|
251
gdb/tracepoint.c
251
gdb/tracepoint.c
|
@ -34,6 +34,7 @@
|
||||||
#include "tracepoint.h"
|
#include "tracepoint.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
extern int remote_supports_cond_tracepoints (void);
|
extern int remote_supports_cond_tracepoints (void);
|
||||||
|
extern char *unpack_varlen_hex (char *buff, ULONGEST *result);
|
||||||
#include "linespec.h"
|
#include "linespec.h"
|
||||||
#include "regcache.h"
|
#include "regcache.h"
|
||||||
#include "completer.h"
|
#include "completer.h"
|
||||||
|
@ -111,6 +112,19 @@ extern void output_command (char *, int);
|
||||||
|
|
||||||
/* ======= Important global variables: ======= */
|
/* ======= 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. */
|
/* Number of last traceframe collected. */
|
||||||
static int traceframe_number;
|
static int traceframe_number;
|
||||||
|
|
||||||
|
@ -126,6 +140,9 @@ static struct symtab_and_line traceframe_sal;
|
||||||
/* Tracing command lists */
|
/* Tracing command lists */
|
||||||
static struct cmd_list_element *tfindlist;
|
static struct cmd_list_element *tfindlist;
|
||||||
|
|
||||||
|
static char *target_buf;
|
||||||
|
static long target_buf_size;
|
||||||
|
|
||||||
/* ======= Important command functions: ======= */
|
/* ======= Important command functions: ======= */
|
||||||
static void trace_actions_command (char *, int);
|
static void trace_actions_command (char *, int);
|
||||||
static void trace_start_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);
|
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: */
|
/* ACTIONS functions: */
|
||||||
|
|
||||||
/* Prototypes for action-parsing utility commands */
|
/* Prototypes for action-parsing utility commands */
|
||||||
|
@ -1254,9 +1470,6 @@ add_aexpr (struct collection_list *collect, struct agent_expr *aexpr)
|
||||||
collect->next_aexpr_elt++;
|
collect->next_aexpr_elt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *target_buf;
|
|
||||||
static long target_buf_size;
|
|
||||||
|
|
||||||
/* Set "transparent" memory ranges
|
/* Set "transparent" memory ranges
|
||||||
|
|
||||||
Allow trace mechanism to treat text-like sections
|
Allow trace mechanism to treat text-like sections
|
||||||
|
@ -1312,9 +1525,11 @@ void download_tracepoint (struct breakpoint *t);
|
||||||
static void
|
static void
|
||||||
trace_start_command (char *args, int from_tty)
|
trace_start_command (char *args, int from_tty)
|
||||||
{
|
{
|
||||||
|
char buf[2048];
|
||||||
VEC(breakpoint_p) *tp_vec = NULL;
|
VEC(breakpoint_p) *tp_vec = NULL;
|
||||||
int ix;
|
int ix;
|
||||||
struct breakpoint *t;
|
struct breakpoint *t;
|
||||||
|
struct trace_state_variable *tsv;
|
||||||
|
|
||||||
dont_repeat (); /* Like "run", dangerous to repeat accidentally. */
|
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);
|
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. */
|
/* Tell target to treat text-like sections as transparent. */
|
||||||
remote_set_transparent_ranges ();
|
remote_set_transparent_ranges ();
|
||||||
/* Now insert traps and begin collecting data. */
|
/* Now insert traps and begin collecting data. */
|
||||||
|
@ -2235,6 +2463,23 @@ _initialize_tracepoint (void)
|
||||||
add_com ("tdump", class_trace, trace_dump_command,
|
add_com ("tdump", class_trace, trace_dump_command,
|
||||||
_("Print everything collected at the current tracepoint."));
|
_("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, _("\
|
add_prefix_cmd ("tfind", class_trace, trace_find_command, _("\
|
||||||
Select a trace frame;\n\
|
Select a trace frame;\n\
|
||||||
No argument means forward by one frame; '-' means backward by one frame."),
|
No argument means forward by one frame; '-' means backward by one frame."),
|
||||||
|
|
|
@ -35,6 +35,34 @@ enum actionline_type
|
||||||
STEPPING = 2
|
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;
|
extern unsigned long trace_running_p;
|
||||||
|
|
||||||
/* A hook used to notify the UI of tracepoint operations. */
|
/* 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 end_actions_pseudocommand (char *args, int from_tty);
|
||||||
extern void while_stepping_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 */
|
#endif /* TRACEPOINT_H */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue