/* Tracing functionality for remote targets in custom GDB protocol
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+ 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
This file is part of GDB.
#include "objfiles.h"
#include "filenames.h"
#include "gdbthread.h"
-
+#include "stack.h"
+#include "gdbcore.h"
+#include "remote.h"
+#include "source.h"
#include "ax.h"
#include "ax-gdb.h"
"tstop",
"tfull",
"tdisconnected",
- "tpasscount"
+ "tpasscount",
+ "terror"
};
struct trace_status *
{
CORE_ADDR trace_pc;
- if (trace_frame == NULL) /* Cease debugging any trace buffers. */
+ if (trace_frame == NULL) /* Cease debugging any trace buffers. */
{
traceframe_fun = 0;
traceframe_sal.pc = traceframe_sal.line = 0;
if (tsv)
{
tsv->initial_value = initval;
- printf_filtered (_("Trace state variable $%s now has initial value %s.\n"),
+ printf_filtered (_("Trace state variable $%s "
+ "now has initial value %s.\n"),
tsv->name, plongest (tsv->initial_value));
+ do_cleanups (old_chain);
return;
}
tsv = create_trace_state_variable (internalvar_name (intvar));
tsv->initial_value = initval;
- printf_filtered (_("Trace state variable $%s created, with initial value %s.\n"),
+ 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;
+ int ix;
char **argv;
struct cleanup *back_to;
- struct trace_state_variable *tsv;
if (args == NULL)
{
argv = gdb_buildargv (args);
back_to = make_cleanup_freeargv (argv);
- for (i = 0; argv[i] != NULL; i++)
+ for (ix = 0; argv[ix] != NULL; ix++)
{
- if (*argv[i] == '$')
- delete_trace_state_variable (argv[i] + 1);
+ if (*argv[ix] == '$')
+ delete_trace_state_variable (argv[ix] + 1);
else
- warning (_("Name \"%s\" not prefixed with '$', ignoring"), argv[i]);
+ warning (_("Name \"%s\" not prefixed with '$', ignoring"), argv[ix]);
}
do_cleanups (back_to);
}
/* Try to acquire values from the target. */
- for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+ for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix, ++count)
tsv->value_known = target_get_trace_state_variable_value (tsv->number,
&(tsv->value));
back_to2 = make_cleanup_ui_out_tuple_begin_end (uiout, "variable");
- name = concat ("$", tsv->name, NULL);
+ name = concat ("$", tsv->name, (char *) NULL);
make_cleanup (xfree, name);
ui_out_field_string (uiout, "name", name);
ui_out_field_string (uiout, "initial", plongest (tsv->initial_value));
tvariables_info_1 ();
}
+/* Stash definitions of tsvs into the given file. */
+
+void
+save_trace_state_variables (struct ui_file *fp)
+{
+ struct trace_state_variable *tsv;
+ int ix;
+
+ for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+ {
+ fprintf_unfiltered (fp, "tvariable $%s", tsv->name);
+ if (tsv->initial_value)
+ fprintf_unfiltered (fp, " = %s", plongest (tsv->initial_value));
+ fprintf_unfiltered (fp, "\n");
+ }
+}
+
/* ACTIONS functions: */
/* The three functions:
t->number);
struct cleanup *cleanups = make_cleanup (xfree, tmpbuf);
- l = read_command_lines (tmpbuf, from_tty, 1, check_tracepoint_command, t);
+ l = read_command_lines (tmpbuf, from_tty, 1,
+ check_tracepoint_command, t);
do_cleanups (cleanups);
breakpoint_set_commands (t, l);
}
/* else just return */
}
+/* Report the results of checking the agent expression, as errors or
+ internal errors. */
+
+static void
+report_agent_reqs_errors (struct agent_expr *aexpr)
+{
+ /* All of the "flaws" are serious bytecode generation issues that
+ should never occur. */
+ if (aexpr->flaw != agent_flaw_none)
+ internal_error (__FILE__, __LINE__, _("expression is malformed"));
+
+ /* If analysis shows a stack underflow, GDB must have done something
+ badly wrong in its bytecode generation. */
+ if (aexpr->min_height < 0)
+ internal_error (__FILE__, __LINE__,
+ _("expression has min height < 0"));
+
+ /* Issue this error if the stack is predicted to get too deep. The
+ limit is rather arbitrary; a better scheme might be for the
+ target to report how much stack it will have available. The
+ depth roughly corresponds to parenthesization, so a limit of 20
+ amounts to 20 levels of expression nesting, which is actually
+ a pretty big hairy expression. */
+ if (aexpr->max_height > 20)
+ error (_("Expression is too complicated."));
+}
+
/* worker function */
-enum actionline_type
+void
validate_actionline (char **line, struct breakpoint *t)
{
struct cmd_list_element *c;
struct cleanup *old_chain = NULL;
char *p, *tmp_p;
struct bp_location *loc;
+ struct agent_expr *aexpr;
/* if EOF is typed, *line is NULL */
if (*line == NULL)
- return END;
+ return;
for (p = *line; isspace ((int) *p);)
p++;
/* Symbol lookup etc. */
if (*p == '\0') /* empty line: just prompt for another line. */
- return BADLINE;
+ return;
if (*p == '#') /* comment line */
- return GENERIC;
+ return;
c = lookup_cmd (&p, cmdlist, "", -1, 1);
if (c == 0)
- {
- warning (_("'%s' is not an action that I know, or is ambiguous."),
- p);
- return BADLINE;
- }
+ error (_("`%s' is not a tracepoint action, or is ambiguous."), p);
if (cmd_cfunc_eq (c, collect_pseudocommand))
{
- struct agent_expr *aexpr;
- struct agent_reqs areqs;
-
do
{ /* repeat over a comma-separated list */
QUIT; /* allow user to bail out with ^C */
if (*p == '$') /* look for special pseudo-symbols */
{
- if ((0 == strncasecmp ("reg", p + 1, 3)) ||
- (0 == strncasecmp ("arg", p + 1, 3)) ||
- (0 == strncasecmp ("loc", p + 1, 3)))
+ if (0 == strncasecmp ("reg", p + 1, 3)
+ || 0 == strncasecmp ("arg", p + 1, 3)
+ || 0 == strncasecmp ("loc", p + 1, 3)
+ || 0 == strncasecmp ("_sdata", p + 1, 6))
{
p = strchr (p, ',');
continue;
{
if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST)
{
- warning (_("constant %s (value %ld) will not be collected."),
- SYMBOL_PRINT_NAME (exp->elts[2].symbol),
- SYMBOL_VALUE (exp->elts[2].symbol));
- return BADLINE;
+ error (_("constant `%s' (value %ld) "
+ "will not be collected."),
+ SYMBOL_PRINT_NAME (exp->elts[2].symbol),
+ SYMBOL_VALUE (exp->elts[2].symbol));
}
- else if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_OPTIMIZED_OUT)
+ else if (SYMBOL_CLASS (exp->elts[2].symbol)
+ == LOC_OPTIMIZED_OUT)
{
- warning (_("%s is optimized away and cannot be collected."),
- SYMBOL_PRINT_NAME (exp->elts[2].symbol));
- return BADLINE;
+ error (_("`%s' is optimized away "
+ "and cannot be collected."),
+ SYMBOL_PRINT_NAME (exp->elts[2].symbol));
}
}
make_cleanup_free_agent_expr (aexpr);
if (aexpr->len > MAX_AGENT_EXPR_LEN)
- error (_("expression too complicated, try simplifying"));
-
- ax_reqs (aexpr, &areqs);
- (void) make_cleanup (xfree, areqs.reg_mask);
-
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
+ error (_("Expression is too complicated."));
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
+ ax_reqs (aexpr);
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ report_agent_reqs_errors (aexpr);
do_cleanups (old_chain);
}
}
while (p && *p++ == ',');
- return GENERIC;
}
+
else if (cmd_cfunc_eq (c, teval_pseudocommand))
{
- struct agent_expr *aexpr;
-
do
{ /* repeat over a comma-separated list */
QUIT; /* allow user to bail out with ^C */
make_cleanup_free_agent_expr (aexpr);
if (aexpr->len > MAX_AGENT_EXPR_LEN)
- error (_("expression too complicated, try simplifying"));
+ error (_("Expression is too complicated."));
+
+ ax_reqs (aexpr);
+ report_agent_reqs_errors (aexpr);
do_cleanups (old_chain);
}
}
while (p && *p++ == ',');
- return GENERIC;
}
+
else if (cmd_cfunc_eq (c, while_stepping_pseudocommand))
{
char *steparg; /* in case warning is necessary */
p++;
steparg = p;
- if (*p == '\0' ||
- (t->step_count = strtol (p, &p, 0)) == 0)
- {
- error (_("'%s': bad step-count."), *line);
- }
- return STEPPING;
+ if (*p == '\0' || (t->step_count = strtol (p, &p, 0)) == 0)
+ error (_("while-stepping step count `%s' is malformed."), *line);
}
+
else if (cmd_cfunc_eq (c, end_actions_pseudocommand))
- return END;
+ ;
+
else
- {
- error (_("'%s' is not a supported tracepoint action."), *line);
- }
+ error (_("`%s' is not a supported tracepoint action."), *line);
}
enum {
long next_aexpr_elt;
struct agent_expr **aexpr_list;
+ /* True is the user requested a collection of "$_sdata", "static
+ tracepoint data". */
+ int strace_data;
}
tracepoint_list, stepping_list;
memranges->listsize);
}
- if (type != memrange_absolute) /* Better collect the base register! */
+ if (type != memrange_absolute) /* Better collect the base register! */
add_register (memranges, type);
}
}
add_memrange (collect, reg, offset, len);
break;
+
case LOC_UNRESOLVED:
- printf_filtered ("Don't know LOC_UNRESOLVED %s\n",
- SYMBOL_PRINT_NAME (sym));
+ treat_as_expr = 1;
break;
+
case LOC_OPTIMIZED_OUT:
printf_filtered ("%s has been optimized out of existence.\n",
SYMBOL_PRINT_NAME (sym));
{
struct agent_expr *aexpr;
struct cleanup *old_chain1 = NULL;
- struct agent_reqs areqs;
aexpr = gen_trace_for_var (scope, gdbarch, sym);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
- ax_reqs (aexpr, &areqs);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
-
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ ax_reqs (aexpr);
+
+ report_agent_reqs_errors (aexpr);
discard_cleanups (old_chain1);
add_aexpr (collect, aexpr);
/* take care of the registers */
- if (areqs.reg_mask_len > 0)
+ if (aexpr->reg_mask_len > 0)
{
int ndx1, ndx2;
- for (ndx1 = 0; ndx1 < areqs.reg_mask_len; ndx1++)
+ for (ndx1 = 0; ndx1 < aexpr->reg_mask_len; ndx1++)
{
QUIT; /* allow user to bail out with ^C */
- if (areqs.reg_mask[ndx1] != 0)
+ if (aexpr->reg_mask[ndx1] != 0)
{
/* assume chars have 8 bits */
for (ndx2 = 0; ndx2 < 8; ndx2++)
- if (areqs.reg_mask[ndx1] & (1 << ndx2))
+ if (aexpr->reg_mask[ndx1] & (1 << ndx2))
/* it's used -- record it */
add_register (collect, ndx1 * 8 + ndx2);
}
}
}
+/* Data to be passed around in the calls to the locals and args
+ iterators. */
+
+struct add_local_symbols_data
+{
+ struct collection_list *collect;
+ struct gdbarch *gdbarch;
+ CORE_ADDR pc;
+ long frame_regno;
+ long frame_offset;
+ int count;
+};
+
+/* The callback for the locals and args iterators */
+
+static void
+do_collect_symbol (const char *print_name,
+ struct symbol *sym,
+ void *cb_data)
+{
+ struct add_local_symbols_data *p = cb_data;
+
+ collect_symbol (p->collect, sym, p->gdbarch, p->frame_regno,
+ p->frame_offset, p->pc);
+ p->count++;
+}
+
/* Add all locals (or args) symbols to collection list */
static void
add_local_symbols (struct collection_list *collect,
struct gdbarch *gdbarch, CORE_ADDR pc,
long frame_regno, long frame_offset, int type)
{
- struct symbol *sym;
struct block *block;
- struct dict_iterator iter;
- int count = 0;
+ struct add_local_symbols_data cb_data;
- block = block_for_pc (pc);
- while (block != 0)
+ cb_data.collect = collect;
+ cb_data.gdbarch = gdbarch;
+ cb_data.pc = pc;
+ cb_data.frame_regno = frame_regno;
+ cb_data.frame_offset = frame_offset;
+ cb_data.count = 0;
+
+ if (type == 'L')
{
- QUIT; /* allow user to bail out with ^C */
- ALL_BLOCK_SYMBOLS (block, iter, sym)
+ block = block_for_pc (pc);
+ if (block == NULL)
{
- if (SYMBOL_IS_ARGUMENT (sym)
- ? type == 'A' /* collecting Arguments */
- : type == 'L') /* collecting Locals */
- {
- count++;
- collect_symbol (collect, sym, gdbarch,
- frame_regno, frame_offset, pc);
- }
+ warning (_("Can't collect locals; "
+ "no symbol table info available.\n"));
+ return;
}
- if (BLOCK_FUNCTION (block))
- break;
- else
- block = BLOCK_SUPERBLOCK (block);
+
+ iterate_over_block_local_vars (block, do_collect_symbol, &cb_data);
+ if (cb_data.count == 0)
+ warning (_("No locals found in scope."));
+ }
+ else
+ {
+ pc = get_pc_function_start (pc);
+ block = block_for_pc (pc);
+ if (block == NULL)
+ {
+ warning (_("Can't collect args; no symbol table info available.\n"));
+ return;
+ }
+
+ iterate_over_block_arg_vars (block, do_collect_symbol, &cb_data);
+ if (cb_data.count == 0)
+ warning (_("No args found in scope."));
}
- if (count == 0)
- warning (_("No %s found in scope."),
- type == 'L' ? "locals" : "args");
+}
+
+static void
+add_static_trace_data (struct collection_list *collection)
+{
+ if (info_verbose)
+ printf_filtered ("collect static trace data\n");
+ collection->strace_data = 1;
}
/* worker function */
}
list->next_aexpr_elt = 0;
memset (list->regs_mask, 0, sizeof (list->regs_mask));
+ list->strace_data = 0;
}
/* reduce a collection list to string form (for gdb protocol) */
char *end;
long i;
- count = 1 + list->next_memrange + list->next_aexpr_elt + 1;
+ count = 1 + 1 + list->next_memrange + list->next_aexpr_elt + 1;
str_list = (char *(*)[]) xmalloc (count * sizeof (char *));
+ if (list->strace_data)
+ {
+ if (info_verbose)
+ printf_filtered ("\nCollecting static trace data\n");
+ end = temp_buf;
+ *end++ = 'L';
+ (*str_list)[ndx] = savestring (temp_buf, end - temp_buf);
+ ndx++;
+ }
+
for (i = sizeof (list->regs_mask) - 1; i > 0; i--)
if (list->regs_mask[i] != 0) /* skip leading zeroes in regs_mask */
break;
{
char *action_exp;
struct expression *exp = NULL;
- struct command_line *actions;
int i;
struct value *tempval;
struct cmd_list_element *cmd;
'L');
action_exp = strchr (action_exp, ','); /* more? */
}
+ else if (0 == strncasecmp ("$_sdata", action_exp, 7))
+ {
+ add_static_trace_data (collect);
+ action_exp = strchr (action_exp, ','); /* more? */
+ }
else
{
unsigned long addr, len;
struct cleanup *old_chain = NULL;
struct cleanup *old_chain1 = NULL;
- struct agent_reqs areqs;
exp = parse_exp_1 (&action_exp,
block_for_pc (tloc->address), 1);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
- ax_reqs (aexpr, &areqs);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
+ ax_reqs (aexpr);
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ report_agent_reqs_errors (aexpr);
discard_cleanups (old_chain1);
add_aexpr (collect, aexpr);
/* take care of the registers */
- if (areqs.reg_mask_len > 0)
+ if (aexpr->reg_mask_len > 0)
{
int ndx1;
int ndx2;
- for (ndx1 = 0; ndx1 < areqs.reg_mask_len; ndx1++)
+ for (ndx1 = 0; ndx1 < aexpr->reg_mask_len; ndx1++)
{
QUIT; /* allow user to bail out with ^C */
- if (areqs.reg_mask[ndx1] != 0)
+ if (aexpr->reg_mask[ndx1] != 0)
{
/* assume chars have 8 bits */
for (ndx2 = 0; ndx2 < 8; ndx2++)
- if (areqs.reg_mask[ndx1] & (1 << ndx2))
+ if (aexpr->reg_mask[ndx1] & (1 << ndx2))
/* it's used -- record it */
add_register (collect,
ndx1 * 8 + ndx2);
action_exp++;
{
- unsigned long addr, len;
struct cleanup *old_chain = NULL;
struct cleanup *old_chain1 = NULL;
- struct agent_reqs areqs;
exp = parse_exp_1 (&action_exp,
block_for_pc (tloc->address), 1);
aexpr = gen_eval_for_expr (tloc->address, exp);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
- ax_reqs (aexpr, &areqs);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
-
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ ax_reqs (aexpr);
+ report_agent_reqs_errors (aexpr);
discard_cleanups (old_chain1);
/* Even though we're not officially collecting, add
here. */
gdb_assert (stepping_list);
- encode_actions_1 (action->body_list[0], t, tloc, frame_reg, frame_offset,
- stepping_list, NULL);
+ encode_actions_1 (action->body_list[0], t, tloc, frame_reg,
+ frame_offset, stepping_list, NULL);
}
else
error (_("Invalid tracepoint command '%s'"), action->line);
gdbarch_virtual_frame_pointer (t->gdbarch,
t->loc->address, &frame_reg, &frame_offset);
- actions = t->commands;
+ actions = breakpoint_commands (t);
/* If there are default expressions to collect, make up a collect
action and prepend to the action list to encode. Note that since
if (*default_collect)
{
char *line;
- enum actionline_type linetype;
default_collect_line = xstrprintf ("collect %s", default_collect);
make_cleanup (xfree, default_collect_line);
line = default_collect_line;
- linetype = validate_actionline (&line, t);
- if (linetype != BADLINE)
- {
- default_collect_action = xmalloc (sizeof (struct command_line));
- make_cleanup (xfree, default_collect_action);
- default_collect_action->next = t->commands;
- default_collect_action->line = line;
- actions = default_collect_action;
- }
+ validate_actionline (&line, t);
+
+ default_collect_action = xmalloc (sizeof (struct command_line));
+ make_cleanup (xfree, default_collect_action);
+ default_collect_action->next = actions;
+ default_collect_action->line = line;
+ actions = default_collect_action;
}
encode_actions_1 (actions, t, tloc, frame_reg, frame_offset,
&tracepoint_list, &stepping_list);
{
collect->aexpr_list =
xrealloc (collect->aexpr_list,
- 2 * collect->aexpr_listsize * sizeof (struct agent_expr *));
+ 2 * collect->aexpr_listsize * sizeof (struct agent_expr *));
collect->aexpr_listsize *= 2;
}
collect->aexpr_list[collect->next_aexpr_elt] = aexpr;
void
start_tracing (void)
{
- char buf[2048];
VEC(breakpoint_p) *tp_vec = NULL;
int ix;
struct breakpoint *t;
struct trace_state_variable *tsv;
- int any_downloaded = 0;
-
- target_trace_init ();
+ int any_enabled = 0, num_to_download = 0;
tp_vec = all_tracepoints ();
+
+ /* No point in tracing without any tracepoints... */
+ if (VEC_length (breakpoint_p, tp_vec) == 0)
+ {
+ VEC_free (breakpoint_p, tp_vec);
+ error (_("No tracepoints defined, not starting trace"));
+ }
+
for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
{
+ if (t->enable_state == bp_enabled)
+ any_enabled = 1;
+
+ if ((t->type == bp_fast_tracepoint
+ ? may_insert_fast_tracepoints
+ : may_insert_tracepoints))
+ ++num_to_download;
+ else
+ warning (_("May not insert %stracepoints, skipping tracepoint %d"),
+ (t->type == bp_fast_tracepoint ? "fast " : ""), t->number);
+ }
+
+ /* No point in tracing with only disabled tracepoints. */
+ if (!any_enabled)
+ {
+ VEC_free (breakpoint_p, tp_vec);
+ error (_("No tracepoints enabled, not starting trace"));
+ }
+
+ if (num_to_download <= 0)
+ {
+ VEC_free (breakpoint_p, tp_vec);
+ error (_("No tracepoints that may be downloaded, not starting trace"));
+ }
+
+ target_trace_init ();
+
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+ {
+ if ((t->type == bp_fast_tracepoint
+ ? !may_insert_fast_tracepoints
+ : !may_insert_tracepoints))
+ continue;
+
t->number_on_target = 0;
target_download_tracepoint (t);
t->number_on_target = t->number;
- any_downloaded = 1;
}
VEC_free (breakpoint_p, tp_vec);
-
- /* No point in tracing without any tracepoints... */
- if (!any_downloaded)
- error ("No tracepoints downloaded, not starting trace");
-
+
/* Send down all the trace state variables too. */
for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
{
{
dont_repeat (); /* Like "run", dangerous to repeat accidentally. */
+ if (current_trace_status ()->running)
+ {
+ if (from_tty
+ && !query (_("A trace is running already. Start a new run? ")))
+ error (_("New trace run not started."));
+ }
+
start_tracing ();
}
static void
trace_stop_command (char *args, int from_tty)
{
+ if (!current_trace_status ()->running)
+ error (_("Trace is not running."));
+
stop_tracing ();
}
else if (ts->running)
{
printf_filtered (_("Trace is running on the target.\n"));
- if (disconnected_tracing)
- printf_filtered (_("Trace will continue if GDB disconnects.\n"));
- else
- printf_filtered (_("Trace will stop if GDB disconnects.\n"));
}
else
{
printf_filtered (_("Trace stopped by tracepoint %d.\n"),
ts->stopping_tracepoint);
break;
+ case tracepoint_error:
+ if (ts->stopping_tracepoint)
+ printf_filtered (_("Trace stopped by an "
+ "error (%s, tracepoint %d).\n"),
+ ts->error_desc, ts->stopping_tracepoint);
+ else
+ printf_filtered (_("Trace stopped by an error (%s).\n"),
+ ts->error_desc);
+ break;
case trace_stop_reason_unknown:
printf_filtered (_("Trace stopped for an unknown reason.\n"));
break;
if (ts->traceframes_created >= 0
&& ts->traceframe_count != ts->traceframes_created)
{
- printf_filtered (_("Buffer contains %d trace frames (of %d created total).\n"),
+ printf_filtered (_("Buffer contains %d trace "
+ "frames (of %d created total).\n"),
ts->traceframe_count, ts->traceframes_created);
}
else if (ts->traceframe_count >= 0)
ts->buffer_free);
}
+ if (ts->disconnected_tracing)
+ printf_filtered (_("Trace will continue if GDB disconnects.\n"));
+ else
+ printf_filtered (_("Trace will stop if GDB disconnects.\n"));
+
+ if (ts->circular_buffer)
+ printf_filtered (_("Trace buffer is circular.\n"));
+
/* Now report on what we're doing with tfind. */
if (traceframe_number >= 0)
printf_filtered (_("Looking at trace frame %d, tracepoint %d.\n"),
{
struct trace_status *ts = current_trace_status ();
int status;
- char *string_status;
status = target_get_trace_status (ts);
stop_reason = "passcount";
stopping_tracepoint = ts->stopping_tracepoint;
break;
+ case tracepoint_error:
+ stop_reason = "error";
+ stopping_tracepoint = ts->stopping_tracepoint;
+ break;
}
if (stop_reason)
if (stopping_tracepoint != -1)
ui_out_field_int (uiout, "stopping-tracepoint",
stopping_tracepoint);
+ if (ts->stop_reason == tracepoint_error)
+ ui_out_field_string (uiout, "error-description",
+ ts->error_desc);
}
}
}
-
- if ((int) ts->traceframe_count != -1)
+ if (ts->traceframe_count != -1)
ui_out_field_int (uiout, "frames", ts->traceframe_count);
- if ((int) ts->buffer_size != -1)
- ui_out_field_int (uiout, "buffer-size", (int) ts->buffer_size);
- if ((int) ts->buffer_free != -1)
- ui_out_field_int (uiout, "buffer-free", (int) ts->buffer_free);
+ if (ts->traceframes_created != -1)
+ ui_out_field_int (uiout, "frames-created", ts->traceframes_created);
+ if (ts->buffer_size != -1)
+ ui_out_field_int (uiout, "buffer-size", ts->buffer_size);
+ if (ts->buffer_free != -1)
+ ui_out_field_int (uiout, "buffer-free", ts->buffer_free);
+
+ ui_out_field_int (uiout, "disconnected", ts->disconnected_tracing);
+ ui_out_field_int (uiout, "circular", ts->circular_buffer);
}
-
+/* This function handles the details of what to do about an ongoing
+ tracing run if the user has asked to detach or otherwise disconnect
+ from the target. */
void
-disconnect_or_stop_tracing (int from_tty)
+disconnect_tracing (int from_tty)
{
/* It can happen that the target that was tracing went away on its
own, and we didn't notice. Get a status update, and if the
if (target_get_trace_status (current_trace_status ()) < 0)
current_trace_status ()->running = 0;
+ /* If running interactively, give the user the option to cancel and
+ then decide what to do differently with the run. Scripts are
+ just going to disconnect and let the target deal with it,
+ according to how it's been instructed previously via
+ disconnected-tracing. */
if (current_trace_status ()->running && from_tty)
{
- int cont = query (_("Trace is running. Continue tracing after detach? "));
- /* Note that we send the query result without affecting the
- user's setting of disconnected_tracing, so that the answer is
- a one-time-only. */
- send_disconnected_tracing_value (cont);
-
- /* Also ensure that we do the equivalent of a tstop command if
- tracing is not to continue after the detach. */
- if (!cont)
- stop_tracing ();
+ if (current_trace_status ()->disconnected_tracing)
+ {
+ if (!query (_("Trace is running and will "
+ "continue after detach; detach anyway? ")))
+ error (_("Not confirmed."));
+ }
+ else
+ {
+ if (!query (_("Trace is running but will "
+ "stop on detach; detach anyway? ")))
+ error (_("Not confirmed."));
+ }
}
+
+ /* Also we want to be out of tfind mode, otherwise things can get
+ confusing upon reconnection. Just use these calls instead of
+ full tfind_1 behavior because we're in the middle of detaching,
+ and there's no point to updating current stack frame etc. */
+ set_traceframe_number (-1);
+ set_traceframe_context (NULL);
}
/* Worker function for the various flavors of the tfind command. */
int from_tty)
{
int target_frameno = -1, target_tracept = -1;
- struct frame_id old_frame_id;
- char *reply;
+ struct frame_id old_frame_id = null_frame_id;
struct breakpoint *tp;
- old_frame_id = get_frame_id (get_current_frame ());
+ /* Only try to get the current stack frame if we have a chance of
+ succeeding. In particular, if we're trying to get a first trace
+ frame while all threads are running, it's not going to succeed,
+ so leave it with a default value and let the frame comparison
+ below (correctly) decide to print out the source location of the
+ trace frame. */
+ if (!(type == tfind_number && num == -1)
+ && (has_stack_frames () || traceframe_number >= 0))
+ old_frame_id = get_frame_id (get_current_frame ());
target_frameno = target_trace_find (type, num, addr1, addr2,
&target_tracept);
}
else if (target_frameno == -1)
{
- /* A request for a non-existant trace frame has failed.
+ /* A request for a non-existent trace frame has failed.
Our response will be different, depending on FROM_TTY:
If FROM_TTY is true, meaning that this command was
{
if (ui_out_is_mi_like_p (uiout))
ui_out_field_string (uiout, "found", "0");
- else
- printf_unfiltered (_("No trace frame found"));
+ else if (type == tfind_number && num == -1)
+ printf_unfiltered (_("No longer looking at any trace frame\n"));
+ else /* this case may never occur, check */
+ printf_unfiltered (_("No trace frame found\n"));
}
/* If we're in nonstop mode and getting out of looking at trace
{
enum print_what print_what;
- /* NOTE: in immitation of the step command, try to determine
+ /* NOTE: in imitation of the step command, try to determine
whether we have made a transition from one function to
another. If so, we'll print the "stack frame" (ie. the new
function and it's arguments) -- otherwise we'll just show the
trace_find_pc_command (char *args, int from_tty)
{
CORE_ADDR pc;
- char tmp[40];
if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
struct symtabs_and_lines sals;
struct symtab_and_line sal;
struct cleanup *old_chain;
- char startpc_str[40], endpc_str[40];
if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
sals.sals[0] = sal;
}
else
- {
+ {
sals = decode_line_spec (args, 1);
sal = sals.sals[0];
}
old_chain = make_cleanup (xfree, sals.sals);
if (sal.symtab == 0)
- {
- printf_filtered ("TFIND: No line number information available");
- if (sal.pc != 0)
- {
- /* This is useful for "info line *0x7f34". If we can't
- tell the user about a source line, at least let them
- have the symbolic address. */
- printf_filtered (" for address ");
- wrap_here (" ");
- print_address (get_current_arch (), sal.pc, gdb_stdout);
- printf_filtered (";\n -- will attempt to find by PC. \n");
- }
- else
- {
- printf_filtered (".\n");
- return; /* No line, no PC; what can we do? */
- }
- }
- else if (sal.line > 0
- && find_line_pc_range (sal, &start_pc, &end_pc))
+ error (_("No line number information available."));
+
+ if (sal.line > 0 && find_line_pc_range (sal, &start_pc, &end_pc))
{
if (start_pc == end_pc)
{
trace_find_range_command (char *args, int from_tty)
{
static CORE_ADDR start, stop;
- char start_str[40], stop_str[40];
char *tmp;
if (current_trace_status ()->running && !current_trace_status ()->from_file)
trace_find_outside_command (char *args, int from_tty)
{
CORE_ADDR start, stop;
- char start_str[40], stop_str[40];
char *tmp;
if (current_trace_status ()->running && !current_trace_status ()->from_file)
int regno;
if (args == 0 || *args == 0)
- error (_("requires an argument (function, line or *addr) to define a scope"));
+ error (_("requires an argument (function, "
+ "line or *addr) to define a scope"));
sals = decode_line_1 (&args, 1, NULL, 0, &canonical, NULL);
if (sals.nelts == 0)
We assume the objfile architecture will contain all the
standard registers that occur in debug info in that
objfile. */
- regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym, gdbarch);
+ regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym,
+ gdbarch);
if (SYMBOL_IS_ARGUMENT (sym))
printf_filtered ("an argument in register $%s",
break;
case LOC_REGPARM_ADDR:
/* Note comment at LOC_REGISTER. */
- regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym, gdbarch);
+ regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym,
+ gdbarch);
printf_filtered ("the address of an argument, in register $%s",
gdbarch_register_name (gdbarch, regno));
break;
printf_filtered ("optimized out.\n");
continue;
case LOC_COMPUTED:
- SYMBOL_COMPUTED_OPS (sym)->describe_location (sym, gdb_stdout);
+ SYMBOL_COMPUTED_OPS (sym)->describe_location (sym,
+ BLOCK_START (block),
+ gdb_stdout);
break;
}
if (SYMBOL_TYPE (sym))
*comma = ',';
}
-/* tdump command */
+
+/* Helper for trace_dump_command. Dump the action list starting at
+ ACTION. STEPPING_ACTIONS is true if we're iterating over the
+ actions of the body of a while-stepping action. STEPPING_FRAME is
+ set if the current traceframe was determined to be a while-stepping
+ traceframe. */
+
static void
-trace_dump_command (char *args, int from_tty)
+trace_dump_actions (struct command_line *action,
+ int stepping_actions, int stepping_frame,
+ int from_tty)
{
- struct regcache *regcache;
- struct gdbarch *gdbarch;
- struct breakpoint *t;
- struct command_line *action;
char *action_exp, *next_comma;
- struct cleanup *old_cleanups;
- int stepping_actions = 0;
- int stepping_frame = 0;
- struct bp_location *loc;
- if (tracepoint_number == -1)
- {
- warning (_("No current trace frame."));
- return;
- }
-
- t = get_tracepoint (tracepoint_number);
-
- if (t == NULL)
- error (_("No known tracepoint matches 'current' tracepoint #%d."),
- tracepoint_number);
-
- old_cleanups = make_cleanup (null_cleanup, NULL);
-
- printf_filtered ("Data collected at tracepoint %d, trace frame %d:\n",
- tracepoint_number, traceframe_number);
-
- /* The current frame is a trap frame if the frame PC is equal
- to the tracepoint PC. If not, then the current frame was
- collected during single-stepping. */
-
- regcache = get_current_regcache ();
- gdbarch = get_regcache_arch (regcache);
-
- /* If the traceframe's address matches any of the tracepoint's
- locations, assume it is a direct hit rather than a while-stepping
- frame. (FIXME this is not reliable, should record each frame's
- type.) */
- stepping_frame = 1;
- for (loc = t->loc; loc; loc = loc->next)
- if (loc->address == regcache_read_pc (regcache))
- stepping_frame = 0;
-
- for (action = t->commands; action; action = action->next)
+ for (; action != NULL; action = action->next)
{
struct cmd_list_element *cmd;
error (_("Bad action list item: %s"), action_exp);
if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand))
- stepping_actions = 1;
- else if (cmd_cfunc_eq (cmd, end_actions_pseudocommand))
- stepping_actions = 0;
+ {
+ int i;
+
+ for (i = 0; i < action->body_count; ++i)
+ trace_dump_actions (action->body_list[i],
+ 1, stepping_frame, from_tty);
+ }
else if (cmd_cfunc_eq (cmd, collect_pseudocommand))
{
/* Display the collected data.
}
}
}
- discard_cleanups (old_cleanups);
+}
+
+/* The tdump command. */
+
+static void
+trace_dump_command (char *args, int from_tty)
+{
+ struct regcache *regcache;
+ struct breakpoint *t;
+ int stepping_frame = 0;
+ struct bp_location *loc;
+ char *line, *default_collect_line = NULL;
+ struct command_line *actions, *default_collect_action = NULL;
+ struct cleanup *old_chain = NULL;
+
+ if (tracepoint_number == -1)
+ {
+ warning (_("No current trace frame."));
+ return;
+ }
+
+ t = get_tracepoint (tracepoint_number);
+
+ if (t == NULL)
+ error (_("No known tracepoint matches 'current' tracepoint #%d."),
+ tracepoint_number);
+
+ printf_filtered ("Data collected at tracepoint %d, trace frame %d:\n",
+ tracepoint_number, traceframe_number);
+
+ /* The current frame is a trap frame if the frame PC is equal
+ to the tracepoint PC. If not, then the current frame was
+ collected during single-stepping. */
+
+ regcache = get_current_regcache ();
+
+ /* If the traceframe's address matches any of the tracepoint's
+ locations, assume it is a direct hit rather than a while-stepping
+ frame. (FIXME this is not reliable, should record each frame's
+ type.) */
+ stepping_frame = 1;
+ for (loc = t->loc; loc; loc = loc->next)
+ if (loc->address == regcache_read_pc (regcache))
+ stepping_frame = 0;
+
+ actions = breakpoint_commands (t);
+
+ /* If there is a default-collect list, make up a collect command,
+ prepend to the tracepoint's commands, and pass the whole mess to
+ the trace dump scanner. We need to validate because
+ default-collect might have been junked since the trace run. */
+ if (*default_collect)
+ {
+ default_collect_line = xstrprintf ("collect %s", default_collect);
+ old_chain = make_cleanup (xfree, default_collect_line);
+ line = default_collect_line;
+ validate_actionline (&line, t);
+ default_collect_action = xmalloc (sizeof (struct command_line));
+ make_cleanup (xfree, default_collect_action);
+ default_collect_action->next = actions;
+ default_collect_action->line = line;
+ actions = default_collect_action;
+ }
+
+ trace_dump_actions (actions, 0, stepping_frame, from_tty);
+
+ if (*default_collect)
+ do_cleanups (old_chain);
+}
+
+/* Encode a piece of a tracepoint's source-level definition in a form
+ that is suitable for both protocol and saving in files. */
+/* This version does not do multiple encodes for long strings; it should
+ return an offset to the next piece to encode. FIXME */
+
+extern int
+encode_source_string (int tpnum, ULONGEST addr,
+ char *srctype, char *src, char *buf, int buf_size)
+{
+ if (80 + strlen (srctype) > buf_size)
+ error (_("Buffer too small for source encoding"));
+ sprintf (buf, "%x:%s:%s:%x:%x:",
+ tpnum, phex_nz (addr, sizeof (addr)),
+ srctype, 0, (int) strlen (src));
+ if (strlen (buf) + strlen (src) * 2 >= buf_size)
+ error (_("Source string too long for buffer"));
+ bin2hex (src, buf + strlen (buf), 0);
+ return -1;
}
extern int trace_regblock_size;
struct uploaded_tp *uploaded_tps = NULL, *utp;
struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
int a;
+ char *act;
LONGEST gotten = 0;
ULONGEST offset = 0;
#define MAX_TRACE_UPLOAD 2000
pathname = tilde_expand (filename);
cleanup = make_cleanup (xfree, pathname);
- fp = fopen (pathname, "w");
+ fp = fopen (pathname, "wb");
if (!fp)
error (_("Unable to open file '%s' for saving trace data (%s)"),
filename, safe_strerror (errno));
binary file, plus a hint as what this file is, and a version
number in case of future needs. */
written = fwrite ("\x7fTRACE0\n", 8, 1, fp);
- if (written < 8)
+ if (written < 1)
perror_with_name (pathname);
/* Write descriptive info. */
fprintf (fp, "R %x\n", trace_regblock_size);
/* Write out status of the tracing run (aka "tstatus" info). */
- fprintf (fp, "status %c;%s:%x",
- (ts->running ? '1' : '0'),
- stop_reason_names[ts->stop_reason], ts->stopping_tracepoint);
+ fprintf (fp, "status %c;%s",
+ (ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
+ if (ts->stop_reason == tracepoint_error)
+ {
+ char *buf = (char *) alloca (strlen (ts->error_desc) * 2 + 1);
+
+ bin2hex ((gdb_byte *) ts->error_desc, buf, 0);
+ fprintf (fp, ":%s", buf);
+ }
+ fprintf (fp, ":%x", ts->stopping_tracepoint);
if (ts->traceframe_count >= 0)
fprintf (fp, ";tframes:%x", ts->traceframe_count);
if (ts->traceframes_created >= 0)
fprintf (fp, ";tfree:%x", ts->buffer_free);
if (ts->buffer_size >= 0)
fprintf (fp, ";tsize:%x", ts->buffer_size);
+ if (ts->disconnected_tracing)
+ fprintf (fp, ";disconn:%x", ts->disconnected_tracing);
+ if (ts->circular_buffer)
+ fprintf (fp, ";circular:%x", ts->circular_buffer);
fprintf (fp, "\n");
/* Note that we want to upload tracepoints and save those, rather
fprintf (fp, ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
utp->cond);
fprintf (fp, "\n");
- for (a = 0; a < utp->numactions; ++a)
+ for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
fprintf (fp, "tp A%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->actions[a]);
- for (a = 0; a < utp->num_step_actions; ++a)
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
fprintf (fp, "tp S%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->step_actions[a]);
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ if (utp->at_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
+ fprintf (fp, "tp Z%s\n", buf);
+ }
+ if (utp->cond_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "cond", utp->cond_string,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (fp, "tp Z%s\n", buf);
+ }
+ for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
+ {
+ encode_source_string (utp->number, utp->addr, "cmd", act,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (fp, "tp Z%s\n", buf);
+ }
}
free_uploaded_tps (&uploaded_tps);
if (gotten == 0)
break;
written = fwrite (buf, gotten, 1, fp);
- if (written < gotten)
+ if (written < 1)
perror_with_name (pathname);
offset += gotten;
}
- /* Mark the end of trace data. */
+ /* Mark the end of trace data. (We know that gotten is 0 at this point.) */
written = fwrite (&gotten, 4, 1, fp);
- if (written < 4)
+ if (written < 1)
perror_with_name (pathname);
do_cleanups (cleanup);
memset (utp, 0, sizeof (struct uploaded_tp));
utp->number = num;
utp->addr = addr;
+ utp->actions = NULL;
+ utp->step_actions = NULL;
+ utp->cmd_strings = NULL;
utp->next = *utpp;
*utpp = utp;
return utp;
{
t = find_matching_tracepoint (utp);
if (t)
- printf_filtered (_("Assuming tracepoint %d is same as target's tracepoint %d at %s.\n"),
- t->number, utp->number, paddress (get_current_arch (), utp->addr));
+ printf_filtered (_("Assuming tracepoint %d is same "
+ "as target's tracepoint %d at %s.\n"),
+ t->number, utp->number,
+ paddress (get_current_arch (), utp->addr));
else
{
t = create_tracepoint_from_upload (utp);
if (t)
- printf_filtered (_("Created tracepoint %d for target's tracepoint %d at %s.\n"),
- t->number, utp->number, paddress (get_current_arch (), utp->addr));
+ printf_filtered (_("Created tracepoint %d for "
+ "target's tracepoint %d at %s.\n"),
+ t->number, utp->number,
+ paddress (get_current_arch (), utp->addr));
else
- printf_filtered (_("Failed to create tracepoint for target's tracepoint %d at %s, skipping it.\n"),
- utp->number, paddress (get_current_arch (), utp->addr));
+ printf_filtered (_("Failed to create tracepoint for target's "
+ "tracepoint %d at %s, skipping it.\n"),
+ utp->number,
+ paddress (get_current_arch (), utp->addr));
}
/* Whether found or created, record the number used by the
target, to help with mapping target tracepoints back to their
{
tsv = find_matching_tsv (utsv);
if (tsv)
- printf_filtered (_("Assuming trace state variable $%s is same as target's variable %d.\n"),
- tsv->name, utsv->number);
+ {
+ if (info_verbose)
+ printf_filtered (_("Assuming trace state variable $%s "
+ "is same as target's variable %d.\n"),
+ tsv->name, utsv->number);
+ }
else
{
tsv = create_tsv_from_upload (utsv);
- printf_filtered (_("Created trace state variable $%s for target's variable %d.\n"),
- tsv->name, utsv->number);
+ if (info_verbose)
+ printf_filtered (_("Created trace state variable "
+ "$%s for target's variable %d.\n"),
+ tsv->name, utsv->number);
}
/* Give precedence to numberings that come from the target. */
if (tsv)
filename = tilde_expand (filename);
if (!IS_ABSOLUTE_PATH(filename))
{
- temp = concat (current_directory, "/", filename, (char *)NULL);
+ temp = concat (current_directory, "/", filename, (char *) NULL);
xfree (filename);
filename = temp;
}
ts->stop_reason = trace_stop_reason_unknown;
ts->traceframe_count = -1;
ts->buffer_free = 0;
+ ts->disconnected_tracing = 0;
+ ts->circular_buffer = 0;
/* Read through a section of newline-terminated lines that
define things like tracepoints. */
/* Parse the part of trace status syntax that is shared between
the remote protocol and the trace file reader. */
-extern char *unpack_varlen_hex (char *buff, ULONGEST *result);
-
void
parse_trace_status (char *line, struct trace_status *ts)
{
- char *p = line, *p1, *p_temp;
+ char *p = line, *p1, *p2, *p_temp;
ULONGEST val;
ts->running_known = 1;
ts->running = (*p++ == '1');
ts->stop_reason = trace_stop_reason_unknown;
+ xfree (ts->error_desc);
+ ts->error_desc = NULL;
ts->traceframe_count = -1;
ts->traceframes_created = -1;
ts->buffer_free = -1;
ts->buffer_size = -1;
+ ts->disconnected_tracing = 0;
+ ts->circular_buffer = 0;
while (*p++)
{
p = unpack_varlen_hex (++p1, &val);
ts->stop_reason = trace_never_run;
}
- else if (strncmp (p, stop_reason_names[tracepoint_passcount], p1 - p) == 0)
+ else if (strncmp (p, stop_reason_names[tracepoint_passcount],
+ p1 - p) == 0)
{
p = unpack_varlen_hex (++p1, &val);
ts->stop_reason = tracepoint_passcount;
p = unpack_varlen_hex (++p1, &val);
ts->stop_reason = tstop_command;
}
+ else if (strncmp (p, stop_reason_names[trace_disconnected], p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = trace_disconnected;
+ }
+ else if (strncmp (p, stop_reason_names[tracepoint_error], p1 - p) == 0)
+ {
+ p2 = strchr (++p1, ':');
+ if (p2 != p1)
+ {
+ int end;
+
+ ts->error_desc = xmalloc ((p2 - p1) / 2 + 1);
+ end = hex2bin (p1, ts->error_desc, (p2 - p1) / 2);
+ ts->error_desc[end] = '\0';
+ }
+ else
+ ts->error_desc = xstrdup ("");
+
+ p = unpack_varlen_hex (++p2, &val);
+ ts->stopping_tracepoint = val;
+ ts->stop_reason = tracepoint_error;
+ }
else if (strncmp (p, "tframes", p1 - p) == 0)
{
p = unpack_varlen_hex (++p1, &val);
p = unpack_varlen_hex (++p1, &val);
ts->buffer_size = val;
}
+ else if (strncmp (p, "disconn", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->disconnected_tracing = val;
+ }
+ else if (strncmp (p, "circular", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->circular_buffer = val;
+ }
else
{
/* Silently skip unknown optional info. */
}
}
-/* Given a line of text defining a tracepoint or tracepoint action, parse
- it into an "uploaded tracepoint". */
+/* Given a line of text defining a part of a tracepoint, parse it into
+ an "uploaded tracepoint". */
void
parse_tracepoint_definition (char *line, struct uploaded_tp **utpp)
{
char *p;
char piece;
- ULONGEST num, addr, step, pass, orig_size, xlen;
- int enabled, i;
+ ULONGEST num, addr, step, pass, orig_size, xlen, start;
+ int enabled, end;
enum bptype type;
- char *cond;
+ char *cond, *srctype, *buf;
struct uploaded_tp *utp = NULL;
p = line;
p++;
p = unpack_varlen_hex (p, &orig_size);
}
+ else if (*p == 'S')
+ {
+ type = bp_static_tracepoint;
+ p++;
+ }
else if (*p == 'X')
{
p++;
p += 2 * xlen;
}
else
- warning ("Unrecognized char '%c' in tracepoint definition, skipping rest", *p);
+ warning (_("Unrecognized char '%c' in tracepoint "
+ "definition, skipping rest"), *p);
}
utp = get_uploaded_tp (num, addr, utpp);
utp->type = type;
else if (piece == 'A')
{
utp = get_uploaded_tp (num, addr, utpp);
- utp->actions[utp->numactions++] = xstrdup (p);
+ VEC_safe_push (char_ptr, utp->actions, xstrdup (p));
}
else if (piece == 'S')
{
utp = get_uploaded_tp (num, addr, utpp);
- utp->step_actions[utp->num_step_actions++] = xstrdup (p);
+ VEC_safe_push (char_ptr, utp->step_actions, xstrdup (p));
+ }
+ else if (piece == 'Z')
+ {
+ /* Parse a chunk of source form definition. */
+ utp = get_uploaded_tp (num, addr, utpp);
+ srctype = p;
+ p = strchr (p, ':');
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &start);
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &xlen);
+ p++; /* skip a colon */
+
+ buf = alloca (strlen (line));
+
+ end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
+ buf[end] = '\0';
+
+ if (strncmp (srctype, "at:", strlen ("at:")) == 0)
+ utp->at_string = xstrdup (buf);
+ else if (strncmp (srctype, "cond:", strlen ("cond:")) == 0)
+ utp->cond_string = xstrdup (buf);
+ else if (strncmp (srctype, "cmd:", strlen ("cmd:")) == 0)
+ VEC_safe_push (char_ptr, utp->cmd_strings, xstrdup (buf));
}
else
{
- error ("Invalid tracepoint piece");
+ /* Don't error out, the target might be sending us optional
+ info that we don't care about. */
+ warning (_("Unrecognized tracepoint piece '%c', ignoring"), piece);
}
}
perror_with_name (trace_filename);
else if (gotten < 2)
error (_("Premature end of file while reading trace file"));
+ tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
+ gdbarch_byte_order
+ (target_gdbarch));
tp = get_tracepoint_by_number_on_target (tpnum);
/* FIXME this is a poor heuristic if multiple locations */
{
short tpnum;
int tfnum = 0, found = 0, gotten;
- int data_size;
+ unsigned int data_size;
struct breakpoint *tp;
off_t offset, tframe_offset;
ULONGEST tfaddr;
perror_with_name (trace_filename);
else if (gotten < 2)
error (_("Premature end of file while reading trace file"));
+ tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
+ gdbarch_byte_order
+ (target_gdbarch));
offset += 2;
if (tpnum == 0)
break;
perror_with_name (trace_filename);
else if (gotten < 4)
error (_("Premature end of file while reading trace file"));
+ data_size = (unsigned int) extract_unsigned_integer
+ ((gdb_byte *) &data_size, 4,
+ gdbarch_byte_order (target_gdbarch));
offset += 4;
switch (type)
{
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
char block_type;
- int i, pos, offset, regn, regsize, gotten;
+ int pos, offset, regn, regsize, gotten, pc_regno;
unsigned short mlen;
char *regs;
perror_with_name (trace_filename);
else if (gotten < 2)
error (_("Premature end of file while reading trace file"));
+ mlen = (unsigned short)
+ extract_unsigned_integer ((gdb_byte *) &mlen, 2,
+ gdbarch_byte_order
+ (target_gdbarch));
lseek (trace_fd, mlen, SEEK_CUR);
pos += (8 + 2 + mlen);
break;
break;
}
}
+
+ /* We get here if no register data has been found. Although we
+ don't like making up numbers, GDB has all manner of troubles when
+ the target says some register is not available. Filling in with
+ zeroes is a reasonable fallback. */
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ regcache_raw_supply (regcache, regn, NULL);
+
+ /* We can often usefully guess that the PC is going to be the same
+ as the address of the tracepoint. */
+ pc_regno = gdbarch_pc_regnum (gdbarch);
+ if (pc_regno >= 0 && (regno == -1 || regno == pc_regno))
+ {
+ struct breakpoint *tp = get_tracepoint (tracepoint_number);
+
+ if (tp && tp->loc)
+ {
+ /* But don't try to guess if tracepoint is multi-location... */
+ if (tp->loc->next)
+ {
+ warning ("Tracepoint %d has multiple "
+ "locations, cannot infer $pc",
+ tp->number);
+ return;
+ }
+ /* ... or does while-stepping. */
+ if (tp->step_count > 0)
+ {
+ warning ("Tracepoint %d does while-stepping, cannot infer $pc",
+ tp->number);
+ return;
+ }
+
+ store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
+ gdbarch_byte_order (gdbarch),
+ tp->loc->address);
+ regcache_raw_supply (regcache, pc_regno, regs);
+ }
+ }
}
static LONGEST
{
char block_type;
int pos, gotten;
- ULONGEST maddr;
+ ULONGEST maddr, amt;
unsigned short mlen;
/* We're only doing regular memory for now. */
perror_with_name (trace_filename);
else if (gotten < 8)
error (_("Premature end of file while reading trace file"));
-
+ maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
+ gdbarch_byte_order
+ (target_gdbarch));
gotten = read (trace_fd, &mlen, 2);
if (gotten < 0)
perror_with_name (trace_filename);
else if (gotten < 2)
error (_("Premature end of file while reading trace file"));
- if (maddr <= offset && (offset + len) <= (maddr + mlen))
- {
- gotten = read (trace_fd, readbuf, mlen);
+ mlen = (unsigned short)
+ extract_unsigned_integer ((gdb_byte *) &mlen, 2,
+ gdbarch_byte_order
+ (target_gdbarch));
+ /* If the block includes the first part of the desired
+ range, return as much it has; GDB will re-request the
+ remainder, which might be in a different block of this
+ trace frame. */
+ if (maddr <= offset && offset < (maddr + mlen))
+ {
+ amt = (maddr + mlen) - offset;
+ if (amt > len)
+ amt = len;
+
+ gotten = read (trace_fd, readbuf, amt);
if (gotten < 0)
perror_with_name (trace_filename);
- else if (gotten < mlen)
- error (_("Premature end of file qwhile reading trace file"));
-
- return mlen;
- }
+ /* While it's acceptable to return less than was
+ originally asked for, it's not acceptable to return
+ less than what this block claims to contain. */
+ else if (gotten < amt)
+ error (_("Premature end of file while reading trace file"));
+ return amt;
+ }
lseek (trace_fd, mlen, SEEK_CUR);
pos += (8 + 2 + mlen);
break;
break;
}
}
+
+ /* It's unduly pedantic to refuse to look at the executable for
+ read-only pieces; so do the equivalent of readonly regions aka
+ QTro packet. */
+ /* FIXME account for relocation at some point */
+ if (exec_bfd)
+ {
+ asection *s;
+ bfd_size_type size;
+ bfd_vma vma;
+
+ for (s = exec_bfd->sections; s; s = s->next)
+ {
+ if ((s->flags & SEC_LOAD) == 0 ||
+ (s->flags & SEC_READONLY) == 0)
+ continue;
+
+ vma = s->vma;
+ size = bfd_get_section_size (s);
+ if (vma <= offset && offset < (vma + size))
+ {
+ amt = (vma + size) - offset;
+ if (amt > len)
+ amt = len;
+
+ amt = bfd_get_section_contents (exec_bfd, s,
+ readbuf, offset - vma, amt);
+ return amt;
+ }
+ }
+ }
+
/* Indicate failure to find the requested memory block. */
return -1;
}
perror_with_name (trace_filename);
else if (gotten < 2)
error (_("Premature end of file while reading trace file"));
+ mlen = (unsigned short)
+ extract_unsigned_integer ((gdb_byte *) &mlen, 2,
+ gdbarch_byte_order
+ (target_gdbarch));
lseek (trace_fd, mlen, SEEK_CUR);
pos += (8 + 2 + mlen);
break;
perror_with_name (trace_filename);
else if (gotten < 4)
error (_("Premature end of file while reading trace file"));
+ vnum = (int) extract_signed_integer ((gdb_byte *) &vnum, 4,
+ gdbarch_byte_order
+ (target_gdbarch));
if (tsvnum == vnum)
{
gotten = read (trace_fd, val, 8);
perror_with_name (trace_filename);
else if (gotten < 8)
error (_("Premature end of file while reading trace file"));
+ *val = extract_signed_integer ((gdb_byte *)val, 8,
+ gdbarch_byte_order
+ (target_gdbarch));
return 1;
}
lseek (trace_fd, 8, SEEK_CUR);
return 0;
}
+static int
+tfile_has_all_memory (struct target_ops *ops)
+{
+ return 1;
+}
+
static int
tfile_has_memory (struct target_ops *ops)
{
{
tfile_ops.to_shortname = "tfile";
tfile_ops.to_longname = "Local trace dump file";
- tfile_ops.to_doc =
- "Use a trace file as a target. Specify the filename of the trace file.";
+ tfile_ops.to_doc
+ = "Use a trace file as a target. Specify the filename of the trace file.";
tfile_ops.to_open = tfile_open;
tfile_ops.to_close = tfile_close;
tfile_ops.to_fetch_registers = tfile_fetch_registers;
tfile_ops.to_files_info = tfile_files_info;
tfile_ops.to_get_trace_status = tfile_get_trace_status;
tfile_ops.to_trace_find = tfile_trace_find;
- tfile_ops.to_get_trace_state_variable_value = tfile_get_trace_state_variable_value;
- /* core_stratum might seem more logical, but GDB doesn't like having
- more than one core_stratum vector. */
+ tfile_ops.to_get_trace_state_variable_value
+ = tfile_get_trace_state_variable_value;
tfile_ops.to_stratum = process_stratum;
+ tfile_ops.to_has_all_memory = tfile_has_all_memory;
tfile_ops.to_has_memory = tfile_has_memory;
tfile_ops.to_has_stack = tfile_has_stack;
tfile_ops.to_has_registers = tfile_has_registers;
tfile_ops.to_magic = OPS_MAGIC;
}
+/* Given a line of text defining a static tracepoint marker, parse it
+ into a "static tracepoint marker" object. Throws an error is
+ parsing fails. If PP is non-null, it points to one past the end of
+ the parsed marker definition. */
+
+void
+parse_static_tracepoint_marker_definition (char *line, char **pp,
+ struct static_tracepoint_marker *marker)
+{
+ char *p, *endp;
+ ULONGEST addr;
+ int end;
+
+ p = line;
+ p = unpack_varlen_hex (p, &addr);
+ p++; /* skip a colon */
+
+ marker->gdbarch = target_gdbarch;
+ marker->address = (CORE_ADDR) addr;
+
+ endp = strchr (p, ':');
+ if (endp == NULL)
+ error (_("bad marker definition: %s"), line);
+
+ marker->str_id = xmalloc (endp - p + 1);
+ end = hex2bin (p, (gdb_byte *) marker->str_id, (endp - p + 1) / 2);
+ marker->str_id[end] = '\0';
+
+ p += 2 * end;
+ p++; /* skip a colon */
+
+ marker->extra = xmalloc (strlen (p) + 1);
+ end = hex2bin (p, (gdb_byte *) marker->extra, strlen (p) / 2);
+ marker->extra[end] = '\0';
+
+ if (pp)
+ *pp = p;
+}
+
+/* Release a static tracepoint marker's contents. Note that the
+ object itself isn't released here. There objects are usually on
+ the stack. */
+
+void
+release_static_tracepoint_marker (struct static_tracepoint_marker *marker)
+{
+ xfree (marker->str_id);
+ marker->str_id = NULL;
+}
+
+/* Print MARKER to gdb_stdout. */
+
+static void
+print_one_static_tracepoint_marker (int count,
+ struct static_tracepoint_marker *marker)
+{
+ struct command_line *l;
+ struct symbol *sym;
+
+ char wrap_indent[80];
+ char extra_field_indent[80];
+ struct ui_stream *stb = ui_out_stream_new (uiout);
+ struct cleanup *old_chain = make_cleanup_ui_out_stream_delete (stb);
+ struct cleanup *bkpt_chain;
+ VEC(breakpoint_p) *tracepoints;
+
+ struct symtab_and_line sal;
+
+ init_sal (&sal);
+
+ sal.pc = marker->address;
+
+ tracepoints = static_tracepoints_here (marker->address);
+
+ bkpt_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "marker");
+
+ /* A counter field to help readability. This is not a stable
+ identifier! */
+ ui_out_field_int (uiout, "count", count);
+
+ ui_out_field_string (uiout, "marker-id", marker->str_id);
+
+ ui_out_field_fmt (uiout, "enabled", "%c",
+ !VEC_empty (breakpoint_p, tracepoints) ? 'y' : 'n');
+ ui_out_spaces (uiout, 2);
+
+ strcpy (wrap_indent, " ");
+
+ if (gdbarch_addr_bit (marker->gdbarch) <= 32)
+ strcat (wrap_indent, " ");
+ else
+ strcat (wrap_indent, " ");
+
+ strcpy (extra_field_indent, " ");
+
+ ui_out_field_core_addr (uiout, "addr", marker->gdbarch, marker->address);
+
+ sal = find_pc_line (marker->address, 0);
+ sym = find_pc_sect_function (marker->address, NULL);
+ if (sym)
+ {
+ ui_out_text (uiout, "in ");
+ ui_out_field_string (uiout, "func",
+ SYMBOL_PRINT_NAME (sym));
+ ui_out_wrap_hint (uiout, wrap_indent);
+ ui_out_text (uiout, " at ");
+ }
+ else
+ ui_out_field_skip (uiout, "func");
+
+ if (sal.symtab != NULL)
+ {
+ ui_out_field_string (uiout, "file", sal.symtab->filename);
+ ui_out_text (uiout, ":");
+
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ char *fullname = symtab_to_fullname (sal.symtab);
+
+ if (fullname)
+ ui_out_field_string (uiout, "fullname", fullname);
+ }
+ else
+ ui_out_field_skip (uiout, "fullname");
+
+ ui_out_field_int (uiout, "line", sal.line);
+ }
+ else
+ {
+ ui_out_field_skip (uiout, "fullname");
+ ui_out_field_skip (uiout, "line");
+ }
+
+ ui_out_text (uiout, "\n");
+ ui_out_text (uiout, extra_field_indent);
+ ui_out_text (uiout, _("Data: \""));
+ ui_out_field_string (uiout, "extra-data", marker->extra);
+ ui_out_text (uiout, "\"\n");
+
+ if (!VEC_empty (breakpoint_p, tracepoints))
+ {
+ struct cleanup *cleanup_chain;
+ int ix;
+ struct breakpoint *b;
+
+ cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout,
+ "tracepoints-at");
+
+ ui_out_text (uiout, extra_field_indent);
+ ui_out_text (uiout, _("Probed by static tracepoints: "));
+ for (ix = 0; VEC_iterate(breakpoint_p, tracepoints, ix, b); ix++)
+ {
+ if (ix > 0)
+ ui_out_text (uiout, ", ");
+ ui_out_text (uiout, "#");
+ ui_out_field_int (uiout, "tracepoint-id", b->number);
+ }
+
+ do_cleanups (cleanup_chain);
+
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_int (uiout, "number-of-tracepoints",
+ VEC_length(breakpoint_p, tracepoints));
+ else
+ ui_out_text (uiout, "\n");
+ }
+ VEC_free (breakpoint_p, tracepoints);
+
+ do_cleanups (bkpt_chain);
+ do_cleanups (old_chain);
+}
+
+static void
+info_static_tracepoint_markers_command (char *arg, int from_tty)
+{
+ VEC(static_tracepoint_marker_p) *markers;
+ struct cleanup *old_chain;
+ struct static_tracepoint_marker *marker;
+ int i;
+
+ old_chain
+ = make_cleanup_ui_out_table_begin_end (uiout, 5, -1,
+ "StaticTracepointMarkersTable");
+
+ ui_out_table_header (uiout, 7, ui_left, "counter", "Cnt");
+
+ ui_out_table_header (uiout, 40, ui_left, "marker-id", "ID");
+
+ ui_out_table_header (uiout, 3, ui_left, "enabled", "Enb");
+ if (gdbarch_addr_bit (target_gdbarch) <= 32)
+ ui_out_table_header (uiout, 10, ui_left, "addr", "Address");
+ else
+ ui_out_table_header (uiout, 18, ui_left, "addr", "Address");
+ ui_out_table_header (uiout, 40, ui_noalign, "what", "What");
+
+ ui_out_table_body (uiout);
+
+ markers = target_static_tracepoint_markers_by_strid (NULL);
+ make_cleanup (VEC_cleanup (static_tracepoint_marker_p), &markers);
+
+ for (i = 0;
+ VEC_iterate (static_tracepoint_marker_p,
+ markers, i, marker);
+ i++)
+ {
+ print_one_static_tracepoint_marker (i + 1, marker);
+ release_static_tracepoint_marker (marker);
+ }
+
+ do_cleanups (old_chain);
+}
+
+/* The $_sdata convenience variable is a bit special. We don't know
+ for sure type of the value until we actually have a chance to fetch
+ the data --- the size of the object depends on what has been
+ collected. We solve this by making $_sdata be an internalvar that
+ creates a new value on access. */
+
+/* Return a new value with the correct type for the sdata object of
+ the current trace frame. Return a void value if there's no object
+ available. */
+
+static struct value *
+sdata_make_value (struct gdbarch *gdbarch, struct internalvar *var)
+{
+ LONGEST size;
+ gdb_byte *buf;
+
+ /* We need to read the whole object before we know its size. */
+ size = target_read_alloc (¤t_target,
+ TARGET_OBJECT_STATIC_TRACE_DATA,
+ NULL, &buf);
+ if (size >= 0)
+ {
+ struct value *v;
+ struct type *type;
+
+ type = init_vector_type (builtin_type (gdbarch)->builtin_true_char,
+ size);
+ v = allocate_value (type);
+ memcpy (value_contents_raw (v), buf, size);
+ xfree (buf);
+ return v;
+ }
+ else
+ return allocate_value (builtin_type (gdbarch)->builtin_void);
+}
+
/* module initialization */
void
_initialize_tracepoint (void)
{
struct cmd_list_element *c;
+ /* Explicitly create without lookup, since that tries to create a
+ value with a void typed value, and when we get here, gdbarch
+ isn't initialized yet. At this point, we're quite sure there
+ isn't another convenience variable of the same name. */
+ create_internalvar_type_lazy ("_sdata", sdata_make_value);
+
traceframe_number = -1;
tracepoint_number = -1;
add_info ("tvariables", tvariables_info, _("\
Status of trace state variables and their values.\n\
+"));
+
+ add_info ("static-tracepoint-markers",
+ info_static_tracepoint_markers_command, _("\
+List target static tracepoints markers.\n\
"));
add_prefix_cmd ("tfind", class_trace, trace_find_command, _("\
add_cmd ("line", class_trace, trace_find_line_command, _("\
Select a trace frame by source line.\n\
-Argument can be a line number (with optional source file), \n\
+Argument can be a line number (with optional source file),\n\
a function name, or '*' followed by an address.\n\
Default argument is 'the next source line that was traced'."),
&tfindlist);
$regs -- all registers.\n\
$args -- all function arguments.\n\
$locals -- all variables local to the block/function scope.\n\
+ $_sdata -- static tracepoint data (ignored for non-static tracepoints).\n\
Note: this command can only be used in a tracepoint \"actions\" list."));
add_com ("teval", class_trace, teval_pseudocommand, _("\
add_com ("actions", class_trace, trace_actions_command, _("\
Specify the actions to be taken at a tracepoint.\n\
-Tracepoint actions may include collecting of specified data, \n\
-single-stepping, or enabling/disabling other tracepoints, \n\
+Tracepoint actions may include collecting of specified data,\n\
+single-stepping, or enabling/disabling other tracepoints,\n\
depending on target's capabilities."));
default_collect = xstrdup ("");