String collection for tracepoints.
* NEWS: Mention string collection.
* common/ax.def (tracenz): New bytecode.
* ax-gdb.h (trace_string_kludge): Declare.
* ax-gdb.c: Include valprint.h and c-lang.h.
(trace_string_kludge): New global.
(gen_traced_pop): Add string case.
(agent_command): Add string case.
* tracepoint.h (decode_agent_options): Declare.
* tracepoint.c: Include cli-utils.h.
(decode_agent_options): New function.
(validate_actionline): Call it.
(encode_actions_1): Ditto.
* target.h (struct target_ops): New method to_supports_string_tracing.
(target_supports_string_tracing): New macro.
* target.c (update_current_target): Add to_supports_string_tracing.
* remote.c (struct remote_state): New field string_tracing.
(remote_string_tracing_feature): New function.
(remote_protocol_features): New feature tracenz.
(remote_supports_string_tracing): New function.
(init_remote_ops): Set to_supports_string_tracing.
* tracepoint.c (agent_mem_read_string): New function.
(eval_agent_expr): Call it for tracenz.
* server.c (handle_query): Report support for tracenz.
* gdb.texinfo (Tracepoint Action Lists): Document collect/s.
(General Query Packets): Describe tracenz feature.
* agentexpr.texi (Bytecode Descriptions): Describe tracenz.
* gdb.trace/collection.c: Add code using strings.
* gdb.trace/collection.exp: Add tests of string collection.
+2011-11-02 Stan Shebs <stan@codesourcery.com>
+
+ String collection for tracepoints.
+ * NEWS: Mention string collection.
+ * common/ax.def (tracenz): New bytecode.
+ * ax-gdb.h (trace_string_kludge): Declare.
+ * ax-gdb.c: Include valprint.h and c-lang.h.
+ (trace_string_kludge): New global.
+ (gen_traced_pop): Add string case.
+ (agent_command): Add string case.
+ * tracepoint.h (decode_agent_options): Declare.
+ * tracepoint.c: Include cli-utils.h.
+ (decode_agent_options): New function.
+ (validate_actionline): Call it.
+ (encode_actions_1): Ditto.
+ * target.h (struct target_ops): New method to_supports_string_tracing.
+ (target_supports_string_tracing): New macro.
+ * target.c (update_current_target): Add to_supports_string_tracing.
+ * remote.c (struct remote_state): New field string_tracing.
+ (remote_string_tracing_feature): New function.
+ (remote_protocol_features): New feature tracenz.
+ (remote_supports_string_tracing): New function.
+ (init_remote_ops): Set to_supports_string_tracing.
+
2011-11-02 Pedro Alves <pedro@codesourcery.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
This command was formerly named "maintenance print section-scripts".
It is now generally useful and is no longer a maintenance-only command.
+collect[/s] EXPRESSIONS
+ The tracepoint collect command now takes an optional modifier "/s"
+ that directs it to dereference pointer-to-character types and
+ collect the bytes of memory up to a zero byte. The behavior is
+ similar to what you see when you use the regular print command on a
+ string. An optional integer following the "/s" sets a bound on the
+ number of bytes that will be collected.
+
* Tracepoints can now be enabled and disabled at any time after a trace
experiment has been started using the standard "enable" and "disable"
commands. It is now possible to start a trace experiment with no enabled
#include "cp-support.h"
#include "arch-utils.h"
+#include "valprint.h"
+#include "c-lang.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,
look at gen_expr, towards the bottom; that's the main function that
emits the trace bytecodes at the appropriate points. */
int trace_kludge;
+/* Inspired by trace_kludge, this indicates that pointers to chars
+ should get an added tracenz bytecode to record nonzero bytes, up to
+ a length that is the value of trace_string_kludge. */
+int trace_string_kludge;
+
/* Scan for all static fields in the given class, including any base
classes, and generate tracing bytecodes for each. */
gen_traced_pop (struct gdbarch *gdbarch,
struct agent_expr *ax, struct axs_value *value)
{
+ int string_trace = 0;
+ if (trace_string_kludge
+ && TYPE_CODE (value->type) == TYPE_CODE_PTR
+ && c_textual_element_type (check_typedef (TYPE_TARGET_TYPE (value->type)),
+ 's'))
+ string_trace = 1;
+
if (trace_kludge)
switch (value->kind)
{
case axs_rvalue:
- /* We don't trace rvalues, just the lvalues necessary to
- produce them. So just dispose of this value. */
- ax_simple (ax, aop_pop);
+ if (string_trace)
+ {
+ ax_const_l (ax, trace_string_kludge);
+ ax_simple (ax, aop_tracenz);
+ }
+ else
+ /* We don't trace rvalues, just the lvalues necessary to
+ produce them. So just dispose of this value. */
+ ax_simple (ax, aop_pop);
break;
case axs_lvalue_memory:
{
int length = TYPE_LENGTH (check_typedef (value->type));
+ if (string_trace)
+ ax_simple (ax, aop_dup);
+
/* There's no point in trying to use a trace_quick bytecode
here, since "trace_quick SIZE pop" is three bytes, whereas
"const8 SIZE trace" is also three bytes, does the same
work correctly for objects with large sizes. */
ax_const_l (ax, length);
ax_simple (ax, aop_trace);
+
+ if (string_trace)
+ {
+ ax_simple (ax, aop_ref32);
+ ax_const_l (ax, trace_string_kludge);
+ ax_simple (ax, aop_tracenz);
+ }
}
break;
larger than will fit in a stack, so just mark it for
collection and be done with it. */
ax_reg_mask (ax, value->u.reg);
+
+ /* But if the register points to a string, assume the value
+ will fit on the stack and push it anyway. */
+ if (string_trace)
+ {
+ ax_reg (ax, value->u.reg);
+ ax_const_l (ax, trace_string_kludge);
+ ax_simple (ax, aop_tracenz);
+ }
break;
}
else
if (exp == 0)
error_no_arg (_("expression to translate"));
+ trace_string_kludge = 0;
+ if (*exp == '/')
+ exp = decode_agent_options (exp);
+
/* Recognize the return address collection directive specially. Note
that it is not really an expression of any sort. */
if (strcmp (exp, "$_ret") == 0)
extern struct agent_expr *gen_eval_for_expr (CORE_ADDR, struct expression *);
extern int trace_kludge;
+extern int trace_string_kludge;
#endif /* AX_GDB_H */
DEFOP (getv, 2, 0, 0, 1, 0x2c)
DEFOP (setv, 2, 0, 0, 1, 0x2d)
DEFOP (tracev, 2, 0, 0, 1, 0x2e)
-/* We need something here just to make the tables come out ok. */
-DEFOP (invalid, 0, 0, 0, 0, 0x2f)
+DEFOP (tracenz, 0, 0, 2, 0, 0x2f)
DEFOP (trace16, 2, 0, 1, 1, 0x30)
/* We need something here just to make the tables come out ok. */
DEFOP (invalid2, 0, 0, 0, 0, 0x31)
+2011-11-02 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.texinfo (Tracepoint Action Lists): Document collect/s.
+ (General Query Packets): Describe tracenz feature.
+ * agentexpr.texi (Bytecode Descriptions): Describe tracenz.
+
2011-10-28 Paul Koning <paul_koning@dell.com>
* gdb.texinfo (gdb.types): Rename deepitems to deep_items.
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{tracenz} (0x2f) @var{addr} @var{size} @result{}
+Record the bytes at @var{addr} in a trace buffer, for later retrieval
+by GDB. Stop at either the first zero byte, or when @var{size} bytes
+have been recorded, whichever occurs first.
+
@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
@end smallexample
@kindex collect @r{(tracepoints)}
-@item collect @var{expr1}, @var{expr2}, @dots{}
+@item collect@r{[}/@var{mods}@r{]} @var{expr1}, @var{expr2}, @dots{}
Collect values of the given expressions when the tracepoint is hit.
This command accepts a comma-separated list of any valid expressions.
In addition to global, static, or local variables, the following
with a single argument, or one @code{collect} command with several
arguments separated by commas; the effect is the same.
+The optional @var{mods} changes the usual handling of the arguments.
+@code{s} requests that pointers to chars be handled as strings, in
+particular collecting the contents of the memory being pointed at, up
+to the first zero. The upper bound is by default the value of the
+@code{print elements} variable; if @code{s} is followed by a decimal
+number, that is the upper bound instead. So for instance
+@samp{collect/s25 mystr} collects as many as 25 characters at
+@samp{mystr}.
+
The command @code{info scope} (@pxref{Symbols, info scope}) is
particularly useful for figuring out what data to collect.
@tab @samp{-}
@tab No
+@item @samp{tracenz}
+@tab No
+@tab @samp{-}
+@tab No
+
@end multitable
These are the currently defined stub features, in more detail:
@samp{QTDisable} (@pxref{QTDisable}) packets that allow tracepoints
to be enabled and disabled while a trace experiment is running.
+@item tracenz
+@cindex string tracing, in remote protocol
+The remote stub supports the @samp{tracenz} bytecode for collecting strings.
+See @ref{Bytecode Descriptions} for details about the bytecode.
+
@end table
@item qSymbol::
+2011-11-02 Stan Shebs <stan@codesourcery.com>
+
+ * tracepoint.c (agent_mem_read_string): New function.
+ (eval_agent_expr): Call it for tracenz.
+ * server.c (handle_query): Report support for tracenz.
+
2011-11-02 Yao Qi <yao@codesourcery.com>
* tracepoint.c (cmd_qtstart): Remove unused local variables.
strcat (own_buf, ";qXfer:statictrace:read+");
strcat (own_buf, ";qXfer:traceframe-info:read+");
strcat (own_buf, ";EnableDisableTracepoints+");
+ strcat (own_buf, ";tracenz+");
}
return;
static int agent_mem_read (struct traceframe *tframe,
unsigned char *to, CORE_ADDR from, ULONGEST len);
+static int agent_mem_read_string (struct traceframe *tframe,
+ unsigned char *to, CORE_ADDR from,
+ ULONGEST len);
static int agent_tsv_read (struct traceframe *tframe, int n);
#ifndef IN_PROCESS_AGENT
agent_tsv_read (tframe, arg);
break;
+ case gdb_agent_op_tracenz:
+ agent_mem_read_string (tframe, NULL, (CORE_ADDR) stack[--sp],
+ (ULONGEST) top);
+ if (--sp >= 0)
+ top = stack[sp];
+ break;
+
/* GDB never (currently) generates any of these ops. */
case gdb_agent_op_float:
case gdb_agent_op_ref_float:
return 0;
}
+static int
+agent_mem_read_string (struct traceframe *tframe,
+ unsigned char *to, CORE_ADDR from, ULONGEST len)
+{
+ unsigned char *buf, *mspace;
+ ULONGEST remaining = len;
+ unsigned short blocklen, i;
+
+ /* To save a bit of space, block lengths are 16-bit, so break large
+ requests into multiple blocks. Bordering on overkill for strings,
+ but it could happen that someone specifies a large max length. */
+ while (remaining > 0)
+ {
+ size_t sp;
+
+ blocklen = (remaining > 65535 ? 65535 : remaining);
+ /* We want working space to accumulate nonzero bytes, since
+ traceframes must have a predecided size (otherwise it gets
+ harder to wrap correctly for the circular case, etc). */
+ buf = (unsigned char *) xmalloc (blocklen + 1);
+ for (i = 0; i < blocklen; ++i)
+ {
+ /* Read the string one byte at a time, in case the string is
+ at the end of a valid memory area - we don't want a
+ correctly-terminated string to engender segvio
+ complaints. */
+ read_inferior_memory (from + i, buf + i, 1);
+
+ if (buf[i] == '\0')
+ {
+ blocklen = i + 1;
+ /* Make sure outer loop stops now too. */
+ remaining = blocklen;
+ break;
+ }
+ }
+ sp = 1 + sizeof (from) + sizeof (blocklen) + blocklen;
+ mspace = add_traceframe_block (tframe, sp);
+ if (mspace == NULL)
+ {
+ xfree (buf);
+ return 1;
+ }
+ /* Identify block as a memory block. */
+ *mspace = 'M';
+ ++mspace;
+ /* Record address and size. */
+ memcpy ((void *) mspace, (void *) &from, sizeof (from));
+ mspace += sizeof (from);
+ memcpy ((void *) mspace, (void *) &blocklen, sizeof (blocklen));
+ mspace += sizeof (blocklen);
+ /* Copy the string contents. */
+ memcpy ((void *) mspace, (void *) buf, blocklen);
+ remaining -= blocklen;
+ from += blocklen;
+ xfree (buf);
+ }
+ return 0;
+}
+
/* Record the value of a trace state variable. */
static int
tracepoints while a trace experiment is running. */
int enable_disable_tracepoints;
+ /* True if the stub can collect strings using tracenz bytecode. */
+ int string_tracing;
+
/* Nonzero if the user has pressed Ctrl-C, but the target hasn't
responded to that. */
int ctrlc_pending_p;
rs->enable_disable_tracepoints = (support == PACKET_ENABLE);
}
+static void
+remote_string_tracing_feature (const struct protocol_feature *feature,
+ enum packet_support support,
+ const char *value)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ rs->string_tracing = (support == PACKET_ENABLE);
+}
+
static struct protocol_feature remote_protocol_features[] = {
{ "PacketSize", PACKET_DISABLE, remote_packet_size, -1 },
{ "qXfer:auxv:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_fdpic },
{ "QDisableRandomization", PACKET_DISABLE, remote_supported_packet,
PACKET_QDisableRandomization },
+ { "tracenz", PACKET_DISABLE,
+ remote_string_tracing_feature, -1 },
};
static char *remote_support_xml;
return rs->enable_disable_tracepoints;
}
+static int
+remote_supports_string_tracing (void)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return rs->string_tracing;
+}
+
static void
remote_trace_init (void)
{
remote_ops.to_supports_disable_randomization
= remote_supports_disable_randomization;
remote_ops.to_supports_enable_disable_tracepoint = remote_supports_enable_disable_tracepoint;
+ remote_ops.to_supports_string_tracing = remote_supports_string_tracing;
remote_ops.to_trace_init = remote_trace_init;
remote_ops.to_download_tracepoint = remote_download_tracepoint;
remote_ops.to_download_trace_state_variable
/* Do not inherit to_search_memory. */
INHERIT (to_supports_multi_process, t);
INHERIT (to_supports_enable_disable_tracepoint, t);
+ INHERIT (to_supports_string_tracing, t);
INHERIT (to_trace_init, t);
INHERIT (to_download_tracepoint, t);
INHERIT (to_download_trace_state_variable, t);
de_fault (to_supports_enable_disable_tracepoint,
(int (*) (void))
return_zero);
+ de_fault (to_supports_string_tracing,
+ (int (*) (void))
+ return_zero);
de_fault (to_trace_init,
(void (*) (void))
tcomplain);
/* Does this target support disabling address space randomization? */
int (*to_supports_disable_randomization) (void);
+ /* Does this target support the tracenz bytecode for string collection? */
+ int (*to_supports_string_tracing) (void);
+
/* Determine current architecture of thread PTID.
The target is supposed to determine the architecture of the code where
#define target_supports_enable_disable_tracepoint() \
(*current_target.to_supports_enable_disable_tracepoint) ()
+#define target_supports_string_tracing() \
+ (*current_target.to_supports_string_tracing) ()
+
/* Invalidate all target dcaches. */
extern void target_dcache_invalidate (void);
+2011-11-02 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.trace/collection.c: Add code using strings.
+ * gdb.trace/collection.exp: Add tests of string collection.
+
2011-11-02 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.base/skip-solib.exp (executable_main): New variable.
return i; /* Set_Tracepoint_Here */
}
+int strings_test_func ()
+{
+ int i = 0;
+ char *locstr, *longloc;
+
+ locstr = "abcdef";
+ longloc = malloc(500);
+ strcpy(longloc, "how now brown cow spam spam spam wonderful wonderful spam");
+
+ i += strlen (locstr);
+ i += strlen (longloc);
+
+ return i; /* Set_Tracepoint_Here */
+}
+
int
main (argc, argv, envp)
int argc;
i += reglocal_test_func ();
i += statlocal_test_func ();
i += globals_test_func ();
+ i += strings_test_func ();
/* Values of globals at end of test should be different from
values that they had when trace data was captured. */
"collect \$_ret: cease trace debugging"
}
+proc gdb_collect_strings_test { func mystr myrslt mylim msg } {
+ global hex
+ global cr
+ global gdb_prompt
+
+ prepare_for_trace_test
+
+ # Find the comment-identified line for setting this tracepoint.
+ set testline 0
+ gdb_test_multiple "list $func, +30" "collect $msg: find tracepoint line" {
+ -re "\[\r\n\](\[0-9\]+)\[^\r\n\]+ Set_Tracepoint_Here .*$gdb_prompt" {
+ set testline $expect_out(1,string)
+ pass "collect $msg: find tracepoint line"
+ }
+ -re ".*$gdb_prompt " {
+ fail "collect $msg: find tracepoint line (skipping strings test)"
+ return
+ }
+ timeout {
+ fail "collect $msg: find tracepoint line (skipping strings test)"
+ return
+ }
+ }
+
+ gdb_test "trace $testline" \
+ "Tracepoint \[0-9\]+ at .*" \
+ "collect $msg: set tracepoint"
+ gdb_trace_setactions "collect $msg: define actions" \
+ "" \
+ "collect/s$mylim $mystr" "^$"
+
+ # Begin the test.
+ run_trace_experiment $msg $func
+
+ gdb_test "print $mystr" \
+ "\\$\[0-9\]+ = $hex \"$myrslt\".*$cr" \
+ "collect $msg: collected local string"
+
+ gdb_test "tfind none" \
+ "#0 end .*" \
+ "collect $msg: cease trace debugging"
+}
+
proc gdb_trace_collection_test {} {
global fpreg
global spreg
"globalarr\[\(l6, l7\)\]" "7" "a\[\(b, c\)\]"
gdb_collect_return_test
+
+ gdb_collect_strings_test strings_test_func "locstr" "abcdef" "" \
+ "local string"
+
+ gdb_collect_strings_test strings_test_func "longloc" "how now brown c" 15 \
+ "long local string"
+
}
clean_restart $executable
#include "ax-gdb.h"
#include "memrange.h"
#include "exceptions.h"
+#include "cli/cli-utils.h"
/* readline include files */
#include "readline/readline.h"
error (_("This command can only be used in a tracepoint actions list."));
}
+/* Parse any collection options, such as /s for strings. */
+
+char *
+decode_agent_options (char *exp)
+{
+ struct value_print_options opts;
+
+ if (*exp != '/')
+ return exp;
+
+ /* Call this to borrow the print elements default for collection
+ size. */
+ get_user_print_options (&opts);
+
+ exp++;
+ if (*exp == 's')
+ {
+ if (target_supports_string_tracing ())
+ {
+ /* Allow an optional decimal number giving an explicit maximum
+ string length, defaulting it to the "print elements" value;
+ so "collect/s80 mystr" gets at most 80 bytes of string. */
+ trace_string_kludge = opts.print_max;
+ exp++;
+ if (*exp >= '0' && *exp <= '9')
+ trace_string_kludge = atoi (exp);
+ while (*exp >= '0' && *exp <= '9')
+ exp++;
+ }
+ else
+ error (_("Target does not support \"/s\" option for string tracing."));
+ }
+ else
+ error (_("Undefined collection format \"%c\"."), *exp);
+
+ exp = skip_spaces (exp);
+
+ return exp;
+}
+
/* Enter a list of actions for a tracepoint. */
static void
trace_actions_command (char *args, int from_tty)
if (cmd_cfunc_eq (c, collect_pseudocommand))
{
+ trace_string_kludge = 0;
+ if (*p == '/')
+ p = decode_agent_options (p);
+
do
{ /* Repeat over a comma-separated list. */
QUIT; /* Allow user to bail out with ^C. */
if (cmd_cfunc_eq (cmd, collect_pseudocommand))
{
+ trace_string_kludge = 0;
+ if (*action_exp == '/')
+ action_exp = decode_agent_options (action_exp);
+
do
{ /* Repeat over a comma-separated list. */
QUIT; /* Allow user to bail out with ^C. */
STEPPING_ACTIONS should be equal. */
if (stepping_frame == stepping_actions)
{
+ if (*action_exp == '/')
+ action_exp = decode_agent_options (action_exp);
+
do
{ /* Repeat over a comma-separated list. */
QUIT; /* Allow user to bail out with ^C. */
struct cleanup *make_cleanup_restore_traceframe_number (void);
void free_actions (struct breakpoint *);
+
+extern char *decode_agent_options (char *exp);
+
extern void validate_actionline (char **, struct breakpoint *);
extern void end_actions_pseudocommand (char *args, int from_tty);