From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18491 invoked by alias); 29 Dec 2009 17:21:42 -0000 Received: (qmail 18477 invoked by uid 22791); 29 Dec 2009 17:21:36 -0000 X-SWARE-Spam-Status: No, hits=-1.5 required=5.0 tests=AWL,BAYES_05,SPF_PASS X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 29 Dec 2009 17:21:29 +0000 Received: (qmail 12676 invoked from network); 29 Dec 2009 17:21:27 -0000 Received: from unknown (HELO macbook-2.local) (stan@127.0.0.2) by mail.codesourcery.com with ESMTPA; 29 Dec 2009 17:21:27 -0000 Message-ID: <4B3A3A8E.2030003@codesourcery.com> Date: Tue, 29 Dec 2009 17:21:00 -0000 From: Stan Shebs User-Agent: Thunderbird 2.0.0.23 (Macintosh/20090812) MIME-Version: 1.0 To: Stan Shebs CC: gdb-patches@sourceware.org Subject: Re: [PATCH] Trace state variables References: <4B279456.1010508@codesourcery.com> In-Reply-To: <4B279456.1010508@codesourcery.com> Content-Type: multipart/mixed; boundary="------------090504010805060504000301" Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-12/txt/msg00414.txt.bz2 This is a multi-part message in MIME format. --------------090504010805060504000301 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 1648 Here's what I committed. I incorporated the feedback (thanks!), including adding BINOP_ASSIGN_MODIFY (messier than one would think, required refactoring binary operation compilation), and added a NEWS item. Now on to the next new tracing feature! :-) Stan 2009-12-28 Stan Shebs Add trace state variables. * ax.h (enum agent_op): Add getv, setv, and tracev. (ax_tsv): Declare. * ax-gdb.c: Include tracepoint.h. (gen_expr): Handle BINOP_ASSIGN, BINOP_ASSIGN_MODIFY, and OP_INTERNALVAR. (gen_expr_binop_rest): New function, split from gen_expr. * ax-general.c (ax_tsv): New function. (aop_map): Add new bytecodes. * tracepoint.h (struct trace_state_variable): New struct. (tsv_s): New typedef. (find_trace_state_variable): Declare. * tracepoint.c (tvariables): New global. (next_tsv_number): New global. (create_trace_state_variable): New function. (find_trace_state_variable): New function. (delete_trace_state_variable): New function. (trace_variable_command): New function. (delete_trace_variable_command): New function. (tvariables_info): New function. (trace_start_command): Download tsvs with initial values. (_initialize_tracepoint): Add new commands. * NEWS: Mention the addition of trace state variables. * gdb.texinfo (Trace State Variables): New section. (Tracepoint Packets): Describe trace state variable packets. * agentexpr.texi (Bytecode Descriptions): Describe trace state variable bytecodes. * gdb.trace/tsv.exp: New file. * gdb.base/completion.exp: Update ambiguous info output. --------------090504010805060504000301 Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0"; name="tsv-patch-2" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="tsv-patch-2" Content-length: 44295 Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.341 diff -p -r1.341 NEWS *** NEWS 28 Dec 2009 21:29:53 -0000 1.341 --- NEWS 28 Dec 2009 23:37:28 -0000 *************** Renesas RX rx *** 24,29 **** --- 24,42 ---- lists inferiors that are not running yet or that have exited already. See also "New commands" and "New options" below. + * Trace state variables + + GDB tracepoints now include support for trace state variables, which + are variables managed by the target agent during a tracing + experiment. They are useful for tracepoints that trigger each + other, so for instance one tracepoint can count hits in a variable, + and then a second tracepoint has a condition that is true when the + count reaches a particular value. Trace state variables share the + $-syntax of GDB convenience variables, and can appear in both + tracepoint actions and condition expressions. Use the "tvariable" + command to create, and "info tvariables" to view; see "Trace State + Variables" in the manual for more detail. + * Changed commands disassemble *************** set remotebreak [on | off] *** 75,80 **** --- 88,102 ---- show remotebreak Deprecated. Use "set/show remote interrupt-sequence" instead. + tvariable $NAME [ = EXP ] + Create or modify a trace state variable. + + info tvariables + List trace state variables and their values. + + delete tvariable $NAME ... + Delete one or more trace state variables. + * New options set follow-exec-mode new|same *************** show follow-exec-mode *** 83,88 **** --- 105,118 ---- creates a new one. This is useful to be able to restart the old executable after the inferior having done an exec call. + * New remote packets + + QTDV + Define a trace state variable. + + qTV + Get the current value of a trace state variable. + * Bug fixes Process record now works correctly with hardware watchpoints. Index: ax-gdb.c =================================================================== RCS file: /cvs/src/src/gdb/ax-gdb.c,v retrieving revision 1.56 diff -p -r1.56 ax-gdb.c *** ax-gdb.c 28 Dec 2009 16:49:14 -0000 1.56 --- ax-gdb.c 28 Dec 2009 23:37:28 -0000 *************** *** 36,41 **** --- 36,42 ---- #include "user-regs.h" #include "language.h" #include "dictionary.h" + #include "tracepoint.h" /* To make sense of this file, you should read doc/agentexpr.texi. Then look at the types and enums in ax-gdb.h. For the code itself, *************** static void gen_sizeof (struct expressio *** 139,144 **** --- 140,151 ---- struct type *size_type); static void gen_expr (struct expression *exp, union exp_element **pc, struct agent_expr *ax, struct axs_value *value); + static void gen_expr_binop_rest (struct expression *exp, + enum exp_opcode op, union exp_element **pc, + struct agent_expr *ax, + struct axs_value *value, + struct axs_value *value1, + struct axs_value *value2); static void agent_command (char *exp, int from_tty); *************** gen_expr (struct expression *exp, union *** 1441,1447 **** { /* Used to hold the descriptions of operand expressions. */ struct axs_value value1, value2; ! enum exp_opcode op = (*pc)[0].opcode; /* If we're looking at a constant expression, just push its value. */ { --- 1448,1454 ---- { /* Used to hold the descriptions of operand expressions. */ struct axs_value value1, value2; ! enum exp_opcode op = (*pc)[0].opcode, op2; /* If we're looking at a constant expression, just push its value. */ { *************** gen_expr (struct expression *exp, union *** 1478,1596 **** (*pc)++; gen_expr (exp, pc, ax, &value1); gen_usual_unary (exp, ax, &value1); ! gen_expr (exp, pc, ax, &value2); ! gen_usual_unary (exp, ax, &value2); ! gen_usual_arithmetic (exp, ax, &value1, &value2); ! switch (op) { ! case BINOP_ADD: ! if (TYPE_CODE (value1.type) == TYPE_CODE_INT ! && TYPE_CODE (value2.type) == TYPE_CODE_PTR) { ! /* Swap the values and proceed normally. */ ! ax_simple (ax, aop_swap); ! gen_ptradd (ax, value, &value2, &value1); } - else if (TYPE_CODE (value1.type) == TYPE_CODE_PTR - && TYPE_CODE (value2.type) == TYPE_CODE_INT) - gen_ptradd (ax, value, &value1, &value2); - else - gen_binop (ax, value, &value1, &value2, - aop_add, aop_add, 1, "addition"); - break; - case BINOP_SUB: - if (TYPE_CODE (value1.type) == TYPE_CODE_PTR - && TYPE_CODE (value2.type) == TYPE_CODE_INT) - gen_ptrsub (ax,value, &value1, &value2); - else if (TYPE_CODE (value1.type) == TYPE_CODE_PTR - && TYPE_CODE (value2.type) == TYPE_CODE_PTR) - /* FIXME --- result type should be ptrdiff_t */ - gen_ptrdiff (ax, value, &value1, &value2, - builtin_type (exp->gdbarch)->builtin_long); else ! gen_binop (ax, value, &value1, &value2, ! aop_sub, aop_sub, 1, "subtraction"); ! break; ! case BINOP_MUL: ! gen_binop (ax, value, &value1, &value2, ! aop_mul, aop_mul, 1, "multiplication"); ! break; ! case BINOP_DIV: ! gen_binop (ax, value, &value1, &value2, ! aop_div_signed, aop_div_unsigned, 1, "division"); ! break; ! case BINOP_REM: ! gen_binop (ax, value, &value1, &value2, ! aop_rem_signed, aop_rem_unsigned, 1, "remainder"); ! break; ! case BINOP_SUBSCRIPT: ! gen_ptradd (ax, value, &value1, &value2); ! if (TYPE_CODE (value->type) != TYPE_CODE_PTR) ! error (_("Invalid combination of types in array subscripting.")); ! gen_deref (ax, value); ! break; ! case BINOP_BITWISE_AND: ! gen_binop (ax, value, &value1, &value2, ! aop_bit_and, aop_bit_and, 0, "bitwise and"); ! break; ! ! case BINOP_BITWISE_IOR: ! gen_binop (ax, value, &value1, &value2, ! aop_bit_or, aop_bit_or, 0, "bitwise or"); ! break; ! ! case BINOP_BITWISE_XOR: ! gen_binop (ax, value, &value1, &value2, ! aop_bit_xor, aop_bit_xor, 0, "bitwise exclusive-or"); ! break; ! ! case BINOP_EQUAL: ! gen_binop (ax, value, &value1, &value2, ! aop_equal, aop_equal, 0, "equal"); ! break; ! ! case BINOP_NOTEQUAL: ! gen_binop (ax, value, &value1, &value2, ! aop_equal, aop_equal, 0, "equal"); ! gen_logical_not (ax, value, ! language_bool_type (exp->language_defn, ! exp->gdbarch)); ! break; ! ! case BINOP_LESS: ! gen_binop (ax, value, &value1, &value2, ! aop_less_signed, aop_less_unsigned, 0, "less than"); ! break; ! ! case BINOP_GTR: ! ax_simple (ax, aop_swap); ! gen_binop (ax, value, &value1, &value2, ! aop_less_signed, aop_less_unsigned, 0, "less than"); ! break; ! ! case BINOP_LEQ: ! ax_simple (ax, aop_swap); ! gen_binop (ax, value, &value1, &value2, ! aop_less_signed, aop_less_unsigned, 0, "less than"); ! gen_logical_not (ax, value, ! language_bool_type (exp->language_defn, ! exp->gdbarch)); ! break; ! ! case BINOP_GEQ: ! gen_binop (ax, value, &value1, &value2, ! aop_less_signed, aop_less_unsigned, 0, "less than"); ! gen_logical_not (ax, value, ! language_bool_type (exp->language_defn, ! exp->gdbarch)); ! break; ! default: ! /* We should only list operators in the outer case statement ! that we actually handle in the inner case statement. */ ! internal_error (__FILE__, __LINE__, ! _("gen_expr: op case sets don't match")); } break; /* Note that we need to be a little subtle about generating code --- 1485,1547 ---- (*pc)++; gen_expr (exp, pc, ax, &value1); gen_usual_unary (exp, ax, &value1); ! gen_expr_binop_rest (exp, op, pc, ax, value, &value1, &value2); ! break; ! ! case BINOP_ASSIGN: ! (*pc)++; ! if ((*pc)[0].opcode == OP_INTERNALVAR) { ! char *name = internalvar_name ((*pc)[1].internalvar); ! struct trace_state_variable *tsv; ! (*pc) += 3; ! gen_expr (exp, pc, ax, value); ! tsv = find_trace_state_variable (name); ! if (tsv) { ! ax_tsv (ax, aop_setv, tsv->number); ! if (trace_kludge) ! ax_tsv (ax, aop_tracev, tsv->number); } else ! error (_("$%s is not a trace state variable, may not assign to it"), name); ! } ! else ! error (_("May only assign to trace state variables")); ! break; ! case BINOP_ASSIGN_MODIFY: ! (*pc)++; ! op2 = (*pc)[0].opcode; ! (*pc)++; ! (*pc)++; ! if ((*pc)[0].opcode == OP_INTERNALVAR) ! { ! char *name = internalvar_name ((*pc)[1].internalvar); ! struct trace_state_variable *tsv; ! (*pc) += 3; ! tsv = find_trace_state_variable (name); ! if (tsv) ! { ! /* The tsv will be the left half of the binary operation. */ ! ax_tsv (ax, aop_getv, tsv->number); ! if (trace_kludge) ! ax_tsv (ax, aop_tracev, tsv->number); ! /* Trace state variables are always 64-bit integers. */ ! value1.kind = axs_rvalue; ! value1.type = builtin_type (exp->gdbarch)->builtin_long_long; ! /* Now do right half of expression. */ ! gen_expr_binop_rest (exp, op2, pc, ax, value, &value1, &value2); ! /* We have a result of the binary op, set the tsv. */ ! ax_tsv (ax, aop_setv, tsv->number); ! if (trace_kludge) ! ax_tsv (ax, aop_tracev, tsv->number); ! } ! else ! error (_("$%s is not a trace state variable, may not assign to it"), name); } + else + error (_("May only assign to trace state variables")); break; /* Note that we need to be a little subtle about generating code *************** gen_expr (struct expression *exp, union *** 1644,1650 **** break; case OP_INTERNALVAR: ! error (_("GDB agent expressions cannot use convenience variables.")); /* Weirdo operator: see comments for gen_repeat for details. */ case BINOP_REPEAT: --- 1595,1618 ---- break; case OP_INTERNALVAR: ! { ! const char *name = internalvar_name ((*pc)[1].internalvar); ! struct trace_state_variable *tsv; ! (*pc) += 3; ! tsv = find_trace_state_variable (name); ! if (tsv) ! { ! ax_tsv (ax, aop_getv, tsv->number); ! if (trace_kludge) ! ax_tsv (ax, aop_tracev, tsv->number); ! /* Trace state variables are always 64-bit integers. */ ! value->kind = axs_rvalue; ! value->type = builtin_type (exp->gdbarch)->builtin_long_long; ! } ! else ! error (_("$%s is not a trace state variable; GDB agent expressions cannot use convenience variables."), name); ! } ! break; /* Weirdo operator: see comments for gen_repeat for details. */ case BINOP_REPEAT: *************** gen_expr (struct expression *exp, union *** 1788,1793 **** --- 1756,1886 ---- error (_("Unsupported operator in expression.")); } } + + /* This handles the middle-to-right-side of code generation for binary + expressions, which is shared between regular binary operations and + assign-modify (+= and friends) expressions. */ + + static void + gen_expr_binop_rest (struct expression *exp, + enum exp_opcode op, union exp_element **pc, + struct agent_expr *ax, struct axs_value *value, + struct axs_value *value1, struct axs_value *value2) + { + gen_expr (exp, pc, ax, value2); + gen_usual_unary (exp, ax, value2); + gen_usual_arithmetic (exp, ax, value1, value2); + switch (op) + { + case BINOP_ADD: + if (TYPE_CODE (value1->type) == TYPE_CODE_INT + && TYPE_CODE (value2->type) == TYPE_CODE_PTR) + { + /* Swap the values and proceed normally. */ + ax_simple (ax, aop_swap); + gen_ptradd (ax, value, value2, value1); + } + else if (TYPE_CODE (value1->type) == TYPE_CODE_PTR + && TYPE_CODE (value2->type) == TYPE_CODE_INT) + gen_ptradd (ax, value, value1, value2); + else + gen_binop (ax, value, value1, value2, + aop_add, aop_add, 1, "addition"); + break; + case BINOP_SUB: + if (TYPE_CODE (value1->type) == TYPE_CODE_PTR + && TYPE_CODE (value2->type) == TYPE_CODE_INT) + gen_ptrsub (ax,value, value1, value2); + else if (TYPE_CODE (value1->type) == TYPE_CODE_PTR + && TYPE_CODE (value2->type) == TYPE_CODE_PTR) + /* FIXME --- result type should be ptrdiff_t */ + gen_ptrdiff (ax, value, value1, value2, + builtin_type (exp->gdbarch)->builtin_long); + else + gen_binop (ax, value, value1, value2, + aop_sub, aop_sub, 1, "subtraction"); + break; + case BINOP_MUL: + gen_binop (ax, value, value1, value2, + aop_mul, aop_mul, 1, "multiplication"); + break; + case BINOP_DIV: + gen_binop (ax, value, value1, value2, + aop_div_signed, aop_div_unsigned, 1, "division"); + break; + case BINOP_REM: + gen_binop (ax, value, value1, value2, + aop_rem_signed, aop_rem_unsigned, 1, "remainder"); + break; + case BINOP_SUBSCRIPT: + gen_ptradd (ax, value, value1, value2); + if (TYPE_CODE (value->type) != TYPE_CODE_PTR) + error (_("Invalid combination of types in array subscripting.")); + gen_deref (ax, value); + break; + case BINOP_BITWISE_AND: + gen_binop (ax, value, value1, value2, + aop_bit_and, aop_bit_and, 0, "bitwise and"); + break; + + case BINOP_BITWISE_IOR: + gen_binop (ax, value, value1, value2, + aop_bit_or, aop_bit_or, 0, "bitwise or"); + break; + + case BINOP_BITWISE_XOR: + gen_binop (ax, value, value1, value2, + aop_bit_xor, aop_bit_xor, 0, "bitwise exclusive-or"); + break; + + case BINOP_EQUAL: + gen_binop (ax, value, value1, value2, + aop_equal, aop_equal, 0, "equal"); + break; + + case BINOP_NOTEQUAL: + gen_binop (ax, value, value1, value2, + aop_equal, aop_equal, 0, "equal"); + gen_logical_not (ax, value, + language_bool_type (exp->language_defn, + exp->gdbarch)); + break; + + case BINOP_LESS: + gen_binop (ax, value, value1, value2, + aop_less_signed, aop_less_unsigned, 0, "less than"); + break; + + case BINOP_GTR: + ax_simple (ax, aop_swap); + gen_binop (ax, value, value1, value2, + aop_less_signed, aop_less_unsigned, 0, "less than"); + break; + + case BINOP_LEQ: + ax_simple (ax, aop_swap); + gen_binop (ax, value, value1, value2, + aop_less_signed, aop_less_unsigned, 0, "less than"); + gen_logical_not (ax, value, + language_bool_type (exp->language_defn, + exp->gdbarch)); + break; + + case BINOP_GEQ: + gen_binop (ax, value, value1, value2, + aop_less_signed, aop_less_unsigned, 0, "less than"); + gen_logical_not (ax, value, + language_bool_type (exp->language_defn, + exp->gdbarch)); + break; + + default: + /* We should only list operators in the outer case statement + that we actually handle in the inner case statement. */ + internal_error (__FILE__, __LINE__, + _("gen_expr: op case sets don't match")); + } + } /* Given a single variable and a scope, generate bytecodes to trace Index: ax-general.c =================================================================== RCS file: /cvs/src/src/gdb/ax-general.c,v retrieving revision 1.15 diff -p -r1.15 ax-general.c *** ax-general.c 3 Jan 2009 05:57:50 -0000 1.15 --- ax-general.c 28 Dec 2009 23:37:28 -0000 *************** ax_reg (struct agent_expr *x, int reg) *** 272,277 **** --- 272,293 ---- x->buf[x->len + 2] = (reg) & 0xff; x->len += 3; } + + /* Assemble code to operate on a trace state variable. */ + + void + ax_tsv (struct agent_expr *x, enum agent_op op, int num) + { + /* Make sure the tsv number is in range. */ + if (num < 0 || num > 0xffff) + internal_error (__FILE__, __LINE__, _("ax-general.c (ax_tsv): variable number is %d, out of range"), num); + + grow_expr (x, 3); + x->buf[x->len] = op; + x->buf[x->len + 1] = (num >> 8) & 0xff; + x->buf[x->len + 2] = (num) & 0xff; + x->len += 3; + } *************** struct aop_map aop_map[] = *** 324,332 **** {"pop", 0, 0, 1, 0}, /* 0x29 */ {"zero_ext", 1, 0, 1, 1}, /* 0x2a */ {"swap", 0, 0, 2, 2}, /* 0x2b */ ! {0, 0, 0, 0, 0}, /* 0x2c */ ! {0, 0, 0, 0, 0}, /* 0x2d */ ! {0, 0, 0, 0, 0}, /* 0x2e */ {0, 0, 0, 0, 0}, /* 0x2f */ {"trace16", 2, 0, 1, 1}, /* 0x30 */ }; --- 340,348 ---- {"pop", 0, 0, 1, 0}, /* 0x29 */ {"zero_ext", 1, 0, 1, 1}, /* 0x2a */ {"swap", 0, 0, 2, 2}, /* 0x2b */ ! {"getv", 2, 0, 0, 1}, /* 0x2c */ ! {"setv", 2, 0, 0, 1}, /* 0x2d */ ! {"tracev", 2, 0, 0, 1}, /* 0x2e */ {0, 0, 0, 0, 0}, /* 0x2f */ {"trace16", 2, 0, 1, 1}, /* 0x30 */ }; Index: ax.h =================================================================== RCS file: /cvs/src/src/gdb/ax.h,v retrieving revision 1.10 diff -p -r1.10 ax.h *** ax.h 3 Jan 2009 05:57:50 -0000 1.10 --- ax.h 28 Dec 2009 23:37:28 -0000 *************** enum agent_op *** 131,136 **** --- 131,139 ---- aop_pop = 0x29, aop_zero_ext = 0x2a, aop_swap = 0x2b, + aop_getv = 0x2c, + aop_setv = 0x2d, + aop_tracev = 0x2e, aop_trace16 = 0x30, aop_last }; *************** extern void ax_const_d (struct agent_exp *** 182,187 **** --- 185,193 ---- /* Assemble code to push the value of register number REG on the stack. */ extern void ax_reg (struct agent_expr *EXPR, int REG); + + /* Assemble code to operate on a trace state variable. */ + extern void ax_tsv (struct agent_expr *expr, enum agent_op op, int num); /* Functions for printing out expressions, and otherwise debugging Index: tracepoint.c =================================================================== RCS file: /cvs/src/src/gdb/tracepoint.c,v retrieving revision 1.128 diff -p -r1.128 tracepoint.c *** tracepoint.c 24 Dec 2009 00:40:49 -0000 1.128 --- tracepoint.c 28 Dec 2009 23:37:28 -0000 *************** *** 34,39 **** --- 34,40 ---- #include "tracepoint.h" #include "remote.h" extern int remote_supports_cond_tracepoints (void); + extern char *unpack_varlen_hex (char *buff, ULONGEST *result); #include "linespec.h" #include "regcache.h" #include "completer.h" *************** extern void output_command (char *, int) *** 111,116 **** --- 112,130 ---- /* ======= Important global variables: ======= */ + /* The list of all trace state variables. We don't retain pointers to + any of these for any reason - API is by name or number only - so it + works to have a vector of objects. */ + + typedef struct trace_state_variable tsv_s; + DEF_VEC_O(tsv_s); + + static VEC(tsv_s) *tvariables; + + /* The next integer to assign to a variable. */ + + static int next_tsv_number = 1; + /* Number of last traceframe collected. */ static int traceframe_number; *************** static struct symtab_and_line traceframe *** 126,131 **** --- 140,148 ---- /* Tracing command lists */ static struct cmd_list_element *tfindlist; + static char *target_buf; + static long target_buf_size; + /* ======= Important command functions: ======= */ static void trace_actions_command (char *, int); static void trace_start_command (char *, int); *************** set_traceframe_context (struct frame_inf *** 274,279 **** --- 291,495 ---- traceframe_sal.symtab->filename); } + /* Create a new trace state variable with the given name. */ + + struct trace_state_variable * + create_trace_state_variable (const char *name) + { + struct trace_state_variable tsv; + + memset (&tsv, 0, sizeof (tsv)); + tsv.name = name; + tsv.number = next_tsv_number++; + return VEC_safe_push (tsv_s, tvariables, &tsv); + } + + /* Look for a trace state variable of the given name. */ + + struct trace_state_variable * + find_trace_state_variable (const char *name) + { + struct trace_state_variable *tsv; + int ix; + + for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix) + if (strcmp (name, tsv->name) == 0) + return tsv; + + return NULL; + } + + void + delete_trace_state_variable (const char *name) + { + struct trace_state_variable *tsv; + int ix; + + for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix) + if (strcmp (name, tsv->name) == 0) + { + VEC_unordered_remove (tsv_s, tvariables, ix); + return; + } + + warning (_("No trace variable named \"$%s\", not deleting"), name); + } + + /* The 'tvariable' command collects a name and optional expression to + evaluate into an initial value. */ + + void + trace_variable_command (char *args, int from_tty) + { + struct expression *expr; + struct cleanup *old_chain; + struct internalvar *intvar = NULL; + LONGEST initval = 0; + struct trace_state_variable *tsv; + + if (!args || !*args) + error_no_arg (_("trace state variable name")); + + /* All the possible valid arguments are expressions. */ + expr = parse_expression (args); + old_chain = make_cleanup (free_current_contents, &expr); + + if (expr->nelts == 0) + error (_("No expression?")); + + /* Only allow two syntaxes; "$name" and "$name=value". */ + if (expr->elts[0].opcode == OP_INTERNALVAR) + { + intvar = expr->elts[1].internalvar; + } + else if (expr->elts[0].opcode == BINOP_ASSIGN + && expr->elts[1].opcode == OP_INTERNALVAR) + { + intvar = expr->elts[2].internalvar; + initval = value_as_long (evaluate_subexpression_type (expr, 4)); + } + else + error (_("Syntax must be $NAME [ = EXPR ]")); + + if (!intvar) + error (_("No name given")); + + if (strlen (internalvar_name (intvar)) <= 0) + error (_("Must supply a non-empty variable name")); + + /* If the variable already exists, just change its initial value. */ + tsv = find_trace_state_variable (internalvar_name (intvar)); + if (tsv) + { + tsv->initial_value = initval; + printf_filtered (_("Trace state variable $%s now has initial value %s.\n"), + tsv->name, plongest (tsv->initial_value)); + return; + } + + /* Create a new variable. */ + tsv = create_trace_state_variable (internalvar_name (intvar)); + tsv->initial_value = initval; + + printf_filtered (_("Trace state variable $%s created, with initial value %s.\n"), + tsv->name, plongest (tsv->initial_value)); + + do_cleanups (old_chain); + } + + void + delete_trace_variable_command (char *args, int from_tty) + { + int i, ix; + char **argv; + struct cleanup *back_to; + struct trace_state_variable *tsv; + + if (args == NULL) + { + if (query (_("Delete all trace state variables? "))) + VEC_free (tsv_s, tvariables); + dont_repeat (); + return; + } + + argv = gdb_buildargv (args); + back_to = make_cleanup_freeargv (argv); + + for (i = 0; argv[i] != NULL; i++) + { + if (*argv[i] == '$') + delete_trace_state_variable (argv[i] + 1); + else + warning (_("Name \"%s\" not prefixed with '$', ignoring"), argv[i]); + } + + do_cleanups (back_to); + + dont_repeat (); + } + + /* List all the trace state variables. */ + + static void + tvariables_info (char *args, int from_tty) + { + struct trace_state_variable *tsv; + int ix; + char *reply; + ULONGEST tval; + + if (target_is_remote ()) + { + char buf[20]; + + for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix) + { + /* We don't know anything about the value until we get a + valid packet. */ + tsv->value_known = 0; + sprintf (buf, "qTV:%x", tsv->number); + putpkt (buf); + reply = remote_get_noisy_reply (&target_buf, &target_buf_size); + if (reply && *reply) + { + if (*reply == 'V') + { + unpack_varlen_hex (reply + 1, &tval); + tsv->value = (LONGEST) tval; + tsv->value_known = 1; + } + /* FIXME say anything about oddball replies? */ + } + } + } + + if (VEC_length (tsv_s, tvariables) == 0) + { + printf_filtered (_("No trace state variables.\n")); + return; + } + + printf_filtered (_("Name\t\t Initial\tCurrent\n")); + + for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix) + { + printf_filtered ("$%s", tsv->name); + print_spaces_filtered (17 - strlen (tsv->name), gdb_stdout); + printf_filtered ("%s ", plongest (tsv->initial_value)); + print_spaces_filtered (11 - strlen (plongest (tsv->initial_value)), gdb_stdout); + if (tsv->value_known) + printf_filtered (" %s", plongest (tsv->value)); + else if (trace_running_p || traceframe_number >= 0) + /* The value is/was defined, but we don't have it. */ + printf_filtered (_(" ")); + else + /* It is not meaningful to ask about the value. */ + printf_filtered (_(" ")); + printf_filtered ("\n"); + } + } + /* ACTIONS functions: */ /* Prototypes for action-parsing utility commands */ *************** add_aexpr (struct collection_list *colle *** 1254,1262 **** collect->next_aexpr_elt++; } - static char *target_buf; - static long target_buf_size; - /* Set "transparent" memory ranges Allow trace mechanism to treat text-like sections --- 1470,1475 ---- *************** void download_tracepoint (struct breakpo *** 1312,1320 **** --- 1525,1535 ---- static void trace_start_command (char *args, int from_tty) { + char buf[2048]; VEC(breakpoint_p) *tp_vec = NULL; int ix; struct breakpoint *t; + struct trace_state_variable *tsv; dont_repeat (); /* Like "run", dangerous to repeat accidentally. */ *************** trace_start_command (char *args, int fro *** 1332,1337 **** --- 1547,1565 ---- } VEC_free (breakpoint_p, tp_vec); + /* Init any trace state variables that start with nonzero values. */ + + for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix) + { + if (tsv->initial_value != 0) + { + sprintf (buf, "QTDV:%x:%s", + tsv->number, phex ((ULONGEST) tsv->initial_value, 8)); + putpkt (buf); + remote_get_noisy_reply (&target_buf, &target_buf_size); + } + } + /* Tell target to treat text-like sections as transparent. */ remote_set_transparent_ranges (); /* Now insert traps and begin collecting data. */ *************** _initialize_tracepoint (void) *** 2235,2240 **** --- 2463,2485 ---- add_com ("tdump", class_trace, trace_dump_command, _("Print everything collected at the current tracepoint.")); + c = add_com ("tvariable", class_trace, trace_variable_command,_("\ + Define a trace state variable.\n\ + Argument is a $-prefixed name, optionally followed\n\ + by '=' and an expression that sets the initial value\n\ + at the start of tracing.")); + set_cmd_completer (c, expression_completer); + + add_cmd ("tvariable", class_trace, delete_trace_variable_command, _("\ + Delete one or more trace state variables.\n\ + Arguments are the names of the variables to delete.\n\ + If no arguments are supplied, delete all variables."), &deletelist); + /* FIXME add a trace variable completer */ + + add_info ("tvariables", tvariables_info, _("\ + Status of trace state variables and their values.\n\ + ")); + add_prefix_cmd ("tfind", class_trace, trace_find_command, _("\ Select a trace frame;\n\ No argument means forward by one frame; '-' means backward by one frame."), Index: tracepoint.h =================================================================== RCS file: /cvs/src/src/gdb/tracepoint.h,v retrieving revision 1.16 diff -p -r1.16 tracepoint.h *** tracepoint.h 23 Dec 2009 23:21:29 -0000 1.16 --- tracepoint.h 28 Dec 2009 23:37:28 -0000 *************** enum actionline_type *** 35,40 **** --- 35,68 ---- STEPPING = 2 }; + /* A trace state variable is a value managed by a target being + traced. A trace state variable (or tsv for short) can be accessed + and assigned to by tracepoint actions and conditionals, but is not + part of the program being traced, and it doesn't have to be + collected. Effectively the variables are scratch space for + tracepoints. */ + + struct trace_state_variable + { + /* The variable's name. The user has to prefix with a dollar sign, + but we don't store that internally. */ + const char *name; + + /* An id number assigned by GDB, and transmitted to targets. */ + int number; + + /* The initial value of a variable is a 64-bit signed integer. */ + LONGEST initial_value; + + /* 1 if the value is known, else 0. The value is known during a + trace run, or in tfind mode if the variable was collected into + the current trace frame. */ + int value_known; + + /* The value of a variable is a 64-bit signed integer. */ + LONGEST value; + }; + extern unsigned long trace_running_p; /* A hook used to notify the UI of tracepoint operations. */ *************** enum actionline_type validate_actionline *** 49,52 **** --- 77,82 ---- extern void end_actions_pseudocommand (char *args, int from_tty); extern void while_stepping_pseudocommand (char *args, int from_tty); + extern struct trace_state_variable *find_trace_state_variable (const char *name); + #endif /* TRACEPOINT_H */ Index: doc/agentexpr.texi =================================================================== RCS file: /cvs/src/src/gdb/doc/agentexpr.texi,v retrieving revision 1.9 diff -p -r1.9 agentexpr.texi *** doc/agentexpr.texi 11 Nov 2009 15:08:50 -0000 1.9 --- doc/agentexpr.texi 28 Dec 2009 23:37:28 -0000 *************** alignment within the bytecode stream; th *** 440,445 **** --- 440,463 ---- 16-bit on an unaligned address raises an exception, you should fetch the register number one byte at a time. + @item @code{getv} (0x2c) @var{n}: @result{} @var{v} + Push the value of trace state variable number @var{n}, without sign + extension. + + The variable number @var{n} is encoded as a 16-bit unsigned integer + immediately following the @code{getv} bytecode. It is always stored most + significant byte first, regardless of the target's normal endianness. + The variable number is not guaranteed to fall at any particular + alignment within the bytecode stream; thus, on machines where fetching a + 16-bit on an unaligned address raises an exception, you should fetch the + register number one byte at a time. + + @item @code{setv} (0x2d) @var{n}: @result{} @var{v} + Set trace state variable number @var{n} to the value found on the top + of the stack. The stack is unchanged, so that the value is readily + available if the assignment is part of a larger expression. The + handling of @var{n} is as described for @code{getv}. + @item @code{trace} (0x0c): @var{addr} @var{size} @result{} Record the contents of the @var{size} bytes at @var{addr} in a trace buffer, for later retrieval by GDB. *************** Identical to trace_quick, except that @v *** 457,462 **** --- 475,484 ---- unsigned integer, not a single byte. This should probably have been named @code{trace_quick16}, for consistency. + @item @code{tracev} (0x2e) @var{n}: @result{} @var{a} + Record the value of trace state variable number @var{n} in the trace + buffer. The handling of @var{n} is as described for @code{getv}. + @item @code{end} (0x27): @result{} Stop executing bytecode; the result should be the top element of the stack. If the purpose of the expression was to compute an lvalue or a Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.650 diff -p -r1.650 gdb.texinfo *** doc/gdb.texinfo 28 Dec 2009 21:29:53 -0000 1.650 --- doc/gdb.texinfo 28 Dec 2009 23:37:29 -0000 *************** conditions and actions. *** 9331,9336 **** --- 9331,9337 ---- * Enable and Disable Tracepoints:: * Tracepoint Passcounts:: * Tracepoint Conditions:: + * Trace State Variables:: * Tracepoint Actions:: * Listing Tracepoints:: * Starting and Stopping Trace Experiments:: *************** search through. *** 9497,9502 **** --- 9498,9556 ---- (@value{GDBP}) @kbd{trace normal_operation if errcode > 0} @end smallexample + @node Trace State Variables + @subsection Trace State Variables + @cindex trace state variables + + A @dfn{trace state variable} is a special type of variable that is + created and managed by target-side code. The syntax is the same as + that for GDB's convenience variables (a string prefixed with ``$''), + but they are stored on the target. They must be created explicitly, + using a @code{tvariable} command. They are always 64-bit signed + integers. + + Trace state variables are remembered by @value{GDBN}, and downloaded + to the target along with tracepoint information when the trace + experiment starts. There are no intrinsic limits on the number of + trace state variables, beyond memory limitations of the target. + + @cindex convenience variables, and trace state variables + Although trace state variables are managed by the target, you can use + them in print commands and expressions as if they were convenience + variables; @value{GDBN} will get the current value from the target + while the trace experiment is running. Trace state variables share + the same namespace as other ``$'' variables, which means that you + cannot have trace state variables with names like @code{$23} or + @code{$pc}, nor can you have a trace state variable and a convenience + variable with the same name. + + @table @code + + @item tvariable $@var{name} [ = @var{expression} ] + @kindex tvariable + The @code{tvariable} command creates a new trace state variable named + @code{$@var{name}}, and optionally gives it an initial value of + @var{expression}. @var{expression} is evaluated when this command is + entered; the result will be converted to an integer if possible, + otherwise @value{GDBN} will report an error. A subsequent + @code{tvariable} command specifying the same name does not create a + variable, but instead assigns the supplied initial value to the + existing variable of that name, overwriting any previous initial + value. The default initial value is 0. + + @item info tvariables + @kindex info tvariables + List all the trace state variables along with their initial values. + Their current values may also be displayed, if the trace experiment is + currently running. + + @item delete tvariable @r{[} $@var{name} @dots{} @r{]} + @kindex delete tvariable + Delete the given trace state variables, or all of them if no arguments + are specified. + + @end table + @node Tracepoint Actions @subsection Tracepoint Action Lists *************** use @code{output} instead. *** 9929,9935 **** Here's a simple example of using these convenience variables for stepping through all the trace snapshots and printing some of their ! data. @smallexample (@value{GDBP}) @b{tfind start} --- 9983,9990 ---- Here's a simple example of using these convenience variables for stepping through all the trace snapshots and printing some of their ! data. Note that these are not the same as trace state variables, ! which are managed by the target. @smallexample (@value{GDBP}) @b{tfind start} *************** The packet was understood and carried ou *** 29978,29983 **** --- 30033,30048 ---- The packet was not recognized. @end table + @item QTDV:@var{n}:@var{value} + @cindex define trace state variable, remote request + @cindex @samp{QTDV} packet + Create a new trace state variable, number @var{n}, with an initial + value of @var{value}, which is a 64-bit signed integer. Both @var{n} + and @var{value} are encoded as hexadecimal values. @value{GDBN} has + the option of not using this packet for initial values of zero; the + target should simply create the trace state variables as they are + mentioned in expressions. + @item QTFrame:@var{n} Select the @var{n}'th tracepoint frame from the buffer, and use the register and memory contents recorded there to answer subsequent *************** There is no trace experiment running. *** 30051,30058 **** --- 30116,30143 ---- There is a trace experiment running. @end table + @item qTV:@var{var} + @cindex trace state variable value, remote request + @cindex @samp{qTV} packet + Ask the stub for the value of the trace state variable number @var{var}. + + Replies: + @table @samp + @item V@var{value} + The value of the variable is @var{value}. This will be the current + value of the variable if the user is examining a running target, or a + saved value if the variable was collected in the trace frame that the + user is looking at. Note that multiple requests may result in + different reply values, such as when requesting values while the + program is running. + + @item U + The value of the variable is unknown. This would occur, for example, + if the user is examining a trace frame in which the requested variable + was not collected. @end table + @end table @node Host I/O Packets @section Host I/O Packets Index: testsuite/gdb.base/completion.exp =================================================================== RCS file: /cvs/src/src/gdb/testsuite/gdb.base/completion.exp,v retrieving revision 1.39 diff -p -r1.39 completion.exp *** testsuite/gdb.base/completion.exp 13 Jul 2009 19:24:18 -0000 1.39 --- testsuite/gdb.base/completion.exp 28 Dec 2009 23:37:29 -0000 *************** gdb_expect { *** 211,217 **** -re "^info t foo\\\x07$"\ { send_gdb "\n" gdb_expect { ! -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, types\\..*$gdb_prompt $"\ { pass "complete 'info t foo'"} -re ".*$gdb_prompt $" { fail "complete 'info t foo'"} timeout {fail "(timeout) complete 'info t foo'"} --- 211,217 ---- -re "^info t foo\\\x07$"\ { send_gdb "\n" gdb_expect { ! -re "Ambiguous info command \"t foo\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\..*$gdb_prompt $"\ { pass "complete 'info t foo'"} -re ".*$gdb_prompt $" { fail "complete 'info t foo'"} timeout {fail "(timeout) complete 'info t foo'"} *************** gdb_expect { *** 227,233 **** -re "^info t\\\x07$"\ { send_gdb "\n" gdb_expect { ! -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, types\\.. *$gdb_prompt $"\ { pass "complete 'info t'"} -re ".*$gdb_prompt $" { fail "complete 'info t'"} --- 227,233 ---- -re "^info t\\\x07$"\ { send_gdb "\n" gdb_expect { ! -re "Ambiguous info command \"t\": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\.. *$gdb_prompt $"\ { pass "complete 'info t'"} -re ".*$gdb_prompt $" { fail "complete 'info t'"} *************** gdb_expect { *** 245,251 **** -re "^info t \\\x07$"\ { send_gdb "\n" gdb_expect { ! -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, types\\.. *$gdb_prompt $"\ { pass "complete 'info t '"} -re ".*$gdb_prompt $" { fail "complete 'info t '"} --- 245,251 ---- -re "^info t \\\x07$"\ { send_gdb "\n" gdb_expect { ! -re "Ambiguous info command \"t \": target, tasks, terminal, threads, tp, tracepoints, tvariables, types\\.. *$gdb_prompt $"\ { pass "complete 'info t '"} -re ".*$gdb_prompt $" { fail "complete 'info t '"} Index: testsuite/gdb.trace/tsv.exp =================================================================== RCS file: testsuite/gdb.trace/tsv.exp diff -N testsuite/gdb.trace/tsv.exp *** /dev/null 1 Jan 1970 00:00:00 -0000 --- testsuite/gdb.trace/tsv.exp 28 Dec 2009 23:37:29 -0000 *************** *** 0 **** --- 1,107 ---- + # Copyright 2009 Free Software Foundation, Inc. + # + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by + # the Free Software Foundation; either version 3 of the License, or + # (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program. If not, see . + + 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" + + --------------090504010805060504000301--