/* 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.
+ Copyright (C) 1997-2012 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbthread.h"
#include "stack.h"
#include "gdbcore.h"
-
+#include "remote.h"
+#include "source.h"
#include "ax.h"
#include "ax-gdb.h"
+#include "memrange.h"
+#include "exceptions.h"
+#include "cli/cli-utils.h"
+#include "probe.h"
/* readline include files */
#include "readline/readline.h"
extern int hex2bin (const char *hex, gdb_byte *bin, int count);
extern int bin2hex (const gdb_byte *bin, char *hex, int count);
-extern void stop_tracing ();
-
/* Maximum length of an agent aexpression.
This accounts for the fact that packets are limited to 400 bytes
(which includes everything -- including the checksum), and assumes
large. (400 - 31)/2 == 184 */
#define MAX_AGENT_EXPR_LEN 184
+#define TFILE_PID (1)
+
/* A hook used to notify the UI of tracepoint operations. */
void (*deprecated_trace_find_hook) (char *arg, int from_tty);
typedef struct trace_state_variable tsv_s;
DEF_VEC_O(tsv_s);
+/* An object describing the contents of a traceframe. */
+
+struct traceframe_info
+{
+ /* Collected memory. */
+ VEC(mem_range_s) *memory;
+};
+
static VEC(tsv_s) *tvariables;
/* The next integer to assign to a variable. */
/* Tracepoint for last traceframe collected. */
static int tracepoint_number;
-/* Symbol for function for last traceframe collected */
+/* Symbol for function for last traceframe collected. */
static struct symbol *traceframe_fun;
-/* Symtab and line for last traceframe collected */
+/* Symtab and line for last traceframe collected. */
static struct symtab_and_line traceframe_sal;
-/* Tracing command lists */
+/* The traceframe info of the current traceframe. NULL if we haven't
+ yet attempted to fetch it, or if the target does not support
+ fetching this object, or if we're not inspecting a traceframe
+ presently. */
+static struct traceframe_info *traceframe_info;
+
+/* Tracing command lists. */
static struct cmd_list_element *tfindlist;
/* List of expressions to collect by default at each tracepoint hit. */
static int circular_trace_buffer;
+/* Textual notes applying to the current and/or future trace runs. */
+
+char *trace_user = NULL;
+
+/* Textual notes applying to the current and/or future trace runs. */
+
+char *trace_notes = NULL;
+
+/* Textual notes applying to the stopping of a trace. */
+
+char *trace_stop_notes = NULL;
+
/* ======= Important command functions: ======= */
static void trace_actions_command (char *, int);
static void trace_start_command (char *, int);
static void add_register (struct collection_list *collection,
unsigned int regno);
-extern void send_disconnected_tracing_value (int value);
-
static void free_uploaded_tps (struct uploaded_tp **utpp);
static void free_uploaded_tsvs (struct uploaded_tsv **utsvp);
};
struct trace_status *
-current_trace_status ()
+current_trace_status (void)
{
return &trace_status;
}
+/* Destroy INFO. */
+
+static void
+free_traceframe_info (struct traceframe_info *info)
+{
+ if (info != NULL)
+ {
+ VEC_free (mem_range_s, info->memory);
+
+ xfree (info);
+ }
+}
+
+/* Free and clear the traceframe info cache of the current
+ traceframe. */
+
+static void
+clear_traceframe_info (void)
+{
+ free_traceframe_info (traceframe_info);
+ traceframe_info = NULL;
+}
+
/* Set traceframe number to NUM. */
static void
set_traceframe_num (int num)
}
/* Set externally visible debug variables for querying/printing
- the traceframe context (line, function, file) */
+ the traceframe context (line, function, file). */
static void
set_traceframe_context (struct frame_info *trace_frame)
{
CORE_ADDR trace_pc;
- if (trace_frame == NULL) /* Cease debugging any trace buffers. */
+ /* Save as globals for internal use. */
+ if (trace_frame != NULL
+ && get_frame_pc_if_available (trace_frame, &trace_pc))
+ {
+ traceframe_sal = find_pc_line (trace_pc, 0);
+ traceframe_fun = find_pc_function (trace_pc);
+
+ /* Save linenumber as "$trace_line", a debugger variable visible to
+ users. */
+ set_internalvar_integer (lookup_internalvar ("trace_line"),
+ traceframe_sal.line);
+ }
+ else
{
- traceframe_fun = 0;
- traceframe_sal.pc = traceframe_sal.line = 0;
- traceframe_sal.symtab = NULL;
- clear_internalvar (lookup_internalvar ("trace_func"));
- clear_internalvar (lookup_internalvar ("trace_file"));
+ init_sal (&traceframe_sal);
+ traceframe_fun = NULL;
set_internalvar_integer (lookup_internalvar ("trace_line"), -1);
- return;
}
- /* Save as globals for internal use. */
- trace_pc = get_frame_pc (trace_frame);
- traceframe_sal = find_pc_line (trace_pc, 0);
- traceframe_fun = find_pc_function (trace_pc);
-
- /* Save linenumber as "$trace_line", a debugger variable visible to
- users. */
- set_internalvar_integer (lookup_internalvar ("trace_line"),
- traceframe_sal.line);
-
/* Save func name as "$trace_func", a debugger variable visible to
users. */
if (traceframe_fun == NULL
return NULL;
}
-void
+static void
delete_trace_state_variable (const char *name)
{
struct trace_state_variable *tsv;
/* The 'tvariable' command collects a name and optional expression to
evaluate into an initial value. */
-void
+static void
trace_variable_command (char *args, int from_tty)
{
struct expression *expr;
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
+static 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);
int ix;
int count = 0;
struct cleanup *back_to;
+ struct ui_out *uiout = current_uiout;
if (VEC_length (tsv_s, tvariables) == 0 && !ui_out_is_mi_like_p (uiout))
{
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));
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)
{
- struct breakpoint *t;
+ struct tracepoint *t;
struct command_line *l;
- t = get_tracepoint_by_number (&args, 0, 1);
+ t = get_tracepoint_by_number (&args, NULL, 1);
if (t)
{
char *tmpbuf =
xstrprintf ("Enter actions for tracepoint %d, one per line.",
- t->number);
+ t->base.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);
+ breakpoint_set_commands (&t->base, l);
}
/* else just return */
}
internal errors. */
static void
-report_agent_reqs_errors (struct agent_expr *aexpr, struct agent_reqs *areqs)
+report_agent_reqs_errors (struct agent_expr *aexpr)
{
/* All of the "flaws" are serious bytecode generation issues that
should never occur. */
- if (areqs->flaw != agent_flaw_none)
+ 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 (areqs->min_height < 0)
+ if (aexpr->min_height < 0)
internal_error (__FILE__, __LINE__,
_("expression has min height < 0"));
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 (areqs->max_height > 20)
+ if (aexpr->max_height > 20)
error (_("Expression is too complicated."));
}
/* worker function */
void
-validate_actionline (char **line, struct breakpoint *t)
+validate_actionline (char **line, struct breakpoint *b)
{
struct cmd_list_element *c;
struct expression *exp = NULL;
char *p, *tmp_p;
struct bp_location *loc;
struct agent_expr *aexpr;
- struct agent_reqs areqs;
+ struct tracepoint *t = (struct tracepoint *) b;
- /* if EOF is typed, *line is NULL */
+ /* If EOF is typed, *line is NULL. */
if (*line == NULL)
return;
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 */
+ { /* Repeat over a comma-separated list. */
+ QUIT; /* Allow user to bail out with ^C. */
while (isspace ((int) *p))
p++;
- if (*p == '$') /* look for special pseudo-symbols */
+ 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 ("_ret", p + 1, 4)
+ || 0 == strncasecmp ("_sdata", p + 1, 6))
{
p = strchr (p, ',');
continue;
/* else fall thru, treat p as an expression and parse it! */
}
tmp_p = p;
- for (loc = t->loc; loc; loc = loc->next)
+ for (loc = t->base.loc; loc; loc = loc->next)
{
p = tmp_p;
- exp = parse_exp_1 (&p, block_for_pc (loc->address), 1);
+ exp = parse_exp_1 (&p, loc->address,
+ block_for_pc (loc->address), 1);
old_chain = make_cleanup (free_current_contents, &exp);
if (exp->elts[0].opcode == OP_VAR_VALUE)
{
if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST)
{
- error (_("constant `%s' (value %ld) will not be collected."),
+ error (_("constant `%s' (value %s) "
+ "will not be collected."),
SYMBOL_PRINT_NAME (exp->elts[2].symbol),
- SYMBOL_VALUE (exp->elts[2].symbol));
+ plongest (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)
{
- error (_("`%s' is optimized away and cannot be collected."),
+ error (_("`%s' is optimized away "
+ "and cannot be collected."),
SYMBOL_PRINT_NAME (exp->elts[2].symbol));
}
}
if (aexpr->len > MAX_AGENT_EXPR_LEN)
error (_("Expression is too complicated."));
- ax_reqs (aexpr, &areqs);
- (void) make_cleanup (xfree, areqs.reg_mask);
+ ax_reqs (aexpr);
- report_agent_reqs_errors (aexpr, &areqs);
+ report_agent_reqs_errors (aexpr);
do_cleanups (old_chain);
}
else if (cmd_cfunc_eq (c, teval_pseudocommand))
{
do
- { /* repeat over a comma-separated list */
- QUIT; /* allow user to bail out with ^C */
+ { /* Repeat over a comma-separated list. */
+ QUIT; /* Allow user to bail out with ^C. */
while (isspace ((int) *p))
p++;
tmp_p = p;
- for (loc = t->loc; loc; loc = loc->next)
+ for (loc = t->base.loc; loc; loc = loc->next)
{
p = tmp_p;
/* Only expressions are allowed for this action. */
- exp = parse_exp_1 (&p, block_for_pc (loc->address), 1);
+ exp = parse_exp_1 (&p, loc->address,
+ block_for_pc (loc->address), 1);
old_chain = make_cleanup (free_current_contents, &exp);
/* We have something to evaluate, make sure that the expr to
if (aexpr->len > MAX_AGENT_EXPR_LEN)
error (_("Expression is too complicated."));
- ax_reqs (aexpr, &areqs);
- (void) make_cleanup (xfree, areqs.reg_mask);
-
- report_agent_reqs_errors (aexpr, &areqs);
+ ax_reqs (aexpr);
+ report_agent_reqs_errors (aexpr);
do_cleanups (old_chain);
}
else if (cmd_cfunc_eq (c, while_stepping_pseudocommand))
{
- char *steparg; /* in case warning is necessary */
+ char *steparg; /* In case warning is necessary. */
while (isspace ((int) *p))
p++;
struct memrange
{
int type; /* memrange_absolute for absolute memory range,
- else basereg number */
+ else basereg number. */
bfd_signed_vma start;
bfd_signed_vma end;
};
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;
static int memrange_cmp (const void *, const void *);
-/* compare memranges for qsort */
+/* Compare memranges for qsort. */
static int
memrange_cmp (const void *va, const void *vb)
{
{
for (a = 0, b = 1; b < memranges->next_memrange; b++)
{
- if (memranges->list[a].type == memranges->list[b].type &&
- memranges->list[b].start - memranges->list[a].end <=
- MAX_REGISTER_SIZE)
+ /* If memrange b overlaps or is adjacent to memrange a,
+ merge them. */
+ if (memranges->list[a].type == memranges->list[b].type
+ && memranges->list[b].start <= memranges->list[a].end)
{
- /* memrange b starts before memrange a ends; merge them. */
if (memranges->list[b].end > memranges->list[a].end)
memranges->list[a].end = memranges->list[b].end;
continue; /* next b, same a */
collection->regs_mask[regno / 8] |= 1 << (regno % 8);
}
-/* Add a memrange to a collection list */
+/* Add a memrange to a collection list. */
static void
add_memrange (struct collection_list *memranges,
int type, bfd_signed_vma base,
memranges->listsize);
}
- if (type != memrange_absolute) /* Better collect the base register! */
+ if (type != memrange_absolute) /* Better collect the base register! */
add_register (memranges, type);
}
SYMBOL_CLASS (sym));
break;
case LOC_CONST:
- printf_filtered ("constant %s (value %ld) will not be collected.\n",
- SYMBOL_PRINT_NAME (sym), SYMBOL_VALUE (sym));
+ printf_filtered ("constant %s (value %s) will not be collected.\n",
+ SYMBOL_PRINT_NAME (sym), plongest (SYMBOL_VALUE (sym)));
break;
case LOC_STATIC:
offset = SYMBOL_VALUE_ADDRESS (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);
+ ax_reqs (aexpr);
- report_agent_reqs_errors (aexpr, &areqs);
+ report_agent_reqs_errors (aexpr);
discard_cleanups (old_chain1);
add_aexpr (collect, aexpr);
- /* take care of the registers */
- if (areqs.reg_mask_len > 0)
+ /* Take care of the registers. */
+ 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)
+ QUIT; /* Allow user to bail out with ^C. */
+ if (aexpr->reg_mask[ndx1] != 0)
{
- /* assume chars have 8 bits */
+ /* Assume chars have 8 bits. */
for (ndx2 = 0; ndx2 < 8; ndx2++)
- if (areqs.reg_mask[ndx1] & (1 << ndx2))
- /* it's used -- record it */
+ if (aexpr->reg_mask[ndx1] & (1 << ndx2))
+ /* It's used -- record it. */
add_register (collect, ndx1 * 8 + ndx2);
}
}
int count;
};
-/* The callback for the locals and args iterators */
+/* The callback for the locals and args iterators. */
static void
do_collect_symbol (const char *print_name,
p->count++;
}
-/* Add all locals (or args) symbols to collection list */
+/* Add all locals (or args) symbols to collection list. */
static void
add_local_symbols (struct collection_list *collect,
struct gdbarch *gdbarch, CORE_ADDR pc,
block = block_for_pc (pc);
if (block == NULL)
{
- warning (_("Can't collect args; no symbol table info available.\n"));
+ warning (_("Can't collect args; no symbol table info available."));
return;
}
}
}
+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 */
static void
clear_collection_list (struct collection_list *list)
}
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) */
+/* Reduce a collection list to string form (for gdb protocol). */
static char **
stringify_collection_list (struct collection_list *list, char *string)
{
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 */
+ if (list->regs_mask[i] != 0) /* Skip leading zeroes in regs_mask. */
break;
- if (list->regs_mask[i] != 0) /* prepare to send regs_mask to the stub */
+ if (list->regs_mask[i] != 0) /* Prepare to send regs_mask to the stub. */
{
if (info_verbose)
printf_filtered ("\nCollecting registers (mask): 0x");
*end++ = 'R';
for (; i >= 0; i--)
{
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
if (info_verbose)
printf_filtered ("%02X", list->regs_mask[i]);
sprintf (end, "%02X", list->regs_mask[i]);
printf_filtered ("Collecting memranges: \n");
for (i = 0, count = 0, end = temp_buf; i < list->next_memrange; i++)
{
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
sprintf_vma (tmp2, list->list[i].start);
if (info_verbose)
{
for (i = 0; i < list->next_aexpr_elt; i++)
{
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
if ((count + 10 + 2 * list->aexpr_list[i]->len) > MAX_AGENT_EXPR_LEN)
{
(*str_list)[ndx] = savestring (temp_buf, count);
{
char *action_exp;
struct expression *exp = NULL;
- struct command_line *actions;
int i;
struct value *tempval;
struct cmd_list_element *cmd;
for (; action; action = action->next)
{
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
action_exp = action->line;
while (isspace ((int) *action_exp))
action_exp++;
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 */
+ { /* Repeat over a comma-separated list. */
+ QUIT; /* Allow user to bail out with ^C. */
while (isspace ((int) *action_exp))
action_exp++;
if (0 == strncasecmp ("$reg", action_exp, 4))
{
- for (i = 0; i < gdbarch_num_regs (t->gdbarch); i++)
+ for (i = 0; i < gdbarch_num_regs (tloc->gdbarch); i++)
add_register (collect, i);
action_exp = strchr (action_exp, ','); /* more? */
}
else if (0 == strncasecmp ("$arg", action_exp, 4))
{
add_local_symbols (collect,
- t->gdbarch,
+ tloc->gdbarch,
tloc->address,
frame_reg,
frame_offset,
else if (0 == strncasecmp ("$loc", action_exp, 4))
{
add_local_symbols (collect,
- t->gdbarch,
+ tloc->gdbarch,
tloc->address,
frame_reg,
frame_offset,
'L');
action_exp = strchr (action_exp, ','); /* more? */
}
+ else if (0 == strncasecmp ("$_ret", action_exp, 5))
+ {
+ struct cleanup *old_chain1 = NULL;
+
+ aexpr = gen_trace_for_return_address (tloc->address,
+ tloc->gdbarch);
+
+ old_chain1 = make_cleanup_free_agent_expr (aexpr);
+
+ ax_reqs (aexpr);
+ report_agent_reqs_errors (aexpr);
+
+ discard_cleanups (old_chain1);
+ add_aexpr (collect, aexpr);
+
+ /* take care of the registers */
+ if (aexpr->reg_mask_len > 0)
+ {
+ int ndx1, ndx2;
+
+ for (ndx1 = 0; ndx1 < aexpr->reg_mask_len; ndx1++)
+ {
+ QUIT; /* allow user to bail out with ^C */
+ if (aexpr->reg_mask[ndx1] != 0)
+ {
+ /* assume chars have 8 bits */
+ for (ndx2 = 0; ndx2 < 8; ndx2++)
+ if (aexpr->reg_mask[ndx1] & (1 << ndx2))
+ /* it's used -- record it */
+ add_register (collect,
+ ndx1 * 8 + ndx2);
+ }
+ }
+ }
+
+ 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,
+ exp = parse_exp_1 (&action_exp, tloc->address,
block_for_pc (tloc->address), 1);
old_chain = make_cleanup (free_current_contents, &exp);
{
const char *name = &exp->elts[2].string;
- i = user_reg_map_name_to_regnum (t->gdbarch,
+ i = user_reg_map_name_to_regnum (tloc->gdbarch,
name, strlen (name));
if (i == -1)
internal_error (__FILE__, __LINE__,
}
case UNOP_MEMVAL:
- /* safe because we know it's a simple expression */
+ /* Safe because we know it's a simple expression. */
tempval = evaluate_expression (exp);
addr = value_address (tempval);
len = TYPE_LENGTH (check_typedef (exp->elts[1].type));
case OP_VAR_VALUE:
collect_symbol (collect,
exp->elts[2].symbol,
- t->gdbarch,
+ tloc->gdbarch,
frame_reg,
frame_offset,
tloc->address);
break;
- default: /* full-fledged expression */
+ default: /* Full-fledged expression. */
aexpr = gen_trace_for_expr (tloc->address, exp);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
- ax_reqs (aexpr, &areqs);
+ ax_reqs (aexpr);
- report_agent_reqs_errors (aexpr, &areqs);
+ report_agent_reqs_errors (aexpr);
discard_cleanups (old_chain1);
add_aexpr (collect, aexpr);
- /* take care of the registers */
- if (areqs.reg_mask_len > 0)
+ /* Take care of the registers. */
+ 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)
+ QUIT; /* Allow user to bail out with ^C. */
+ if (aexpr->reg_mask[ndx1] != 0)
{
- /* assume chars have 8 bits */
+ /* Assume chars have 8 bits. */
for (ndx2 = 0; ndx2 < 8; ndx2++)
- if (areqs.reg_mask[ndx1] & (1 << ndx2))
- /* it's used -- record it */
+ if (aexpr->reg_mask[ndx1] & (1 << ndx2))
+ /* It's used -- record it. */
add_register (collect,
ndx1 * 8 + ndx2);
}
else if (cmd_cfunc_eq (cmd, teval_pseudocommand))
{
do
- { /* repeat over a comma-separated list */
- QUIT; /* allow user to bail out with ^C */
+ { /* Repeat over a comma-separated list. */
+ QUIT; /* Allow user to bail out with ^C. */
while (isspace ((int) *action_exp))
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,
+ exp = parse_exp_1 (&action_exp, tloc->address,
block_for_pc (tloc->address), 1);
old_chain = make_cleanup (free_current_contents, &exp);
aexpr = gen_eval_for_expr (tloc->address, exp);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
- ax_reqs (aexpr, &areqs);
-
- report_agent_reqs_errors (aexpr, &areqs);
+ 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);
}
/* Render all actions into gdb protocol. */
-/*static*/ void
+
+void
encode_actions (struct breakpoint *t, struct bp_location *tloc,
char ***tdp_actions, char ***stepping_actions)
{
*tdp_actions = NULL;
*stepping_actions = NULL;
- gdbarch_virtual_frame_pointer (t->gdbarch,
- t->loc->address, &frame_reg, &frame_offset);
+ gdbarch_virtual_frame_pointer (tloc->gdbarch,
+ tloc->address, &frame_reg, &frame_offset);
actions = breakpoint_commands (t);
{
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;
collect->next_aexpr_elt++;
}
+static void
+process_tracepoint_on_disconnect (void)
+{
+ VEC(breakpoint_p) *tp_vec = NULL;
+ int ix;
+ struct breakpoint *b;
+ int has_pending_p = 0;
+
+ /* Check whether we still have pending tracepoint. If we have, warn the
+ user that pending tracepoint will no longer work. */
+ tp_vec = all_tracepoints ();
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++)
+ {
+ if (b->loc == NULL)
+ {
+ has_pending_p = 1;
+ break;
+ }
+ else
+ {
+ struct bp_location *loc1;
+
+ for (loc1 = b->loc; loc1; loc1 = loc1->next)
+ {
+ if (loc1->shlib_disabled)
+ {
+ has_pending_p = 1;
+ break;
+ }
+ }
+
+ if (has_pending_p)
+ break;
+ }
+ }
+ VEC_free (breakpoint_p, tp_vec);
+
+ if (has_pending_p)
+ warning (_("Pending tracepoints will not be resolved while"
+ " GDB is disconnected\n"));
+}
+
void
-start_tracing (void)
+start_tracing (char *notes)
{
- char buf[2048];
VEC(breakpoint_p) *tp_vec = NULL;
int ix;
- struct breakpoint *t;
+ struct breakpoint *b;
struct trace_state_variable *tsv;
- int any_enabled = 0;
-
+ int any_enabled = 0, num_to_download = 0;
+ int ret;
+
tp_vec = all_tracepoints ();
- /* No point in tracing without any 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++)
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++)
+ {
+ struct tracepoint *t = (struct tracepoint *) b;
+ struct bp_location *loc;
+
+ if (b->enable_state == bp_enabled)
+ any_enabled = 1;
+
+ if ((b->type == bp_fast_tracepoint
+ ? may_insert_fast_tracepoints
+ : may_insert_tracepoints))
+ ++num_to_download;
+ else
+ warning (_("May not insert %stracepoints, skipping tracepoint %d"),
+ (b->type == bp_fast_tracepoint ? "fast " : ""), b->number);
+ }
+
+ if (!any_enabled)
{
- if (t->enable_state == bp_enabled)
+ if (target_supports_enable_disable_tracepoint ())
+ warning (_("No tracepoints enabled"));
+ else
{
- any_enabled = 1;
- break;
+ /* No point in tracing with only disabled tracepoints that
+ cannot be re-enabled. */
+ VEC_free (breakpoint_p, tp_vec);
+ error (_("No tracepoints enabled, not starting trace"));
}
}
- /* No point in tracing with only disabled tracepoints. */
- if (!any_enabled)
+ if (num_to_download <= 0)
{
VEC_free (breakpoint_p, tp_vec);
- error (_("No tracepoints enabled, not starting trace"));
+ 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++)
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++)
{
+ struct tracepoint *t = (struct tracepoint *) b;
+ struct bp_location *loc;
+
+ /* Clear `inserted' flag. */
+ for (loc = b->loc; loc; loc = loc->next)
+ loc->inserted = 0;
+
+ if ((b->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;
+
+ for (loc = b->loc; loc; loc = loc->next)
+ {
+ /* Since tracepoint locations are never duplicated, `inserted'
+ flag should be zero. */
+ gdb_assert (!loc->inserted);
+
+ target_download_tracepoint (loc);
+
+ loc->inserted = 1;
+ }
+
+ t->number_on_target = b->number;
+
+ for (loc = b->loc; loc; loc = loc->next)
+ if (loc->probe != NULL)
+ loc->probe->pops->set_semaphore (loc->probe, loc->gdbarch);
}
VEC_free (breakpoint_p, tp_vec);
target_set_disconnected_tracing (disconnected_tracing);
target_set_circular_trace_buffer (circular_trace_buffer);
+ if (!notes)
+ notes = trace_notes;
+ ret = target_set_trace_notes (trace_user, notes, NULL);
+
+ if (!ret && (trace_user || notes))
+ warning (_("Target does not support trace user/notes, info ignored"));
+
/* Now insert traps and begin collecting data. */
target_trace_start ();
set_tracepoint_num (-1);
set_traceframe_context (NULL);
current_trace_status()->running = 1;
+ clear_traceframe_info ();
}
-/* tstart command:
-
- Tell target to clear any previous trace experiment.
- Walk the list of tracepoints, and send them (and their actions)
- to the target. If no errors,
- Tell target to start a new trace experiment. */
+/* The tstart command requests the target to start a new trace run.
+ The command passes any arguments it has to the target verbatim, as
+ an optional "trace note". This is useful as for instance a warning
+ to other users if the trace runs disconnected, and you don't want
+ anybody else messing with the target. */
static void
trace_start_command (char *args, int from_tty)
error (_("New trace run not started."));
}
- start_tracing ();
+ start_tracing (args);
}
-/* tstop command */
+/* The tstop command stops the tracing run. The command passes any
+ supplied arguments to the target verbatim as a "stop note"; if the
+ target supports trace notes, then it will be reported back as part
+ of the trace run's status. */
+
static void
trace_stop_command (char *args, int from_tty)
{
if (!current_trace_status ()->running)
error (_("Trace is not running."));
- stop_tracing ();
+ stop_tracing (args);
}
void
-stop_tracing (void)
+stop_tracing (char *note)
{
+ int ret;
+ VEC(breakpoint_p) *tp_vec = NULL;
+ int ix;
+ struct breakpoint *t;
+
target_trace_stop ();
- /* should change in response to reply? */
+
+ tp_vec = all_tracepoints ();
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+ {
+ struct bp_location *loc;
+
+ if ((t->type == bp_fast_tracepoint
+ ? !may_insert_fast_tracepoints
+ : !may_insert_tracepoints))
+ continue;
+
+ for (loc = t->loc; loc; loc = loc->next)
+ {
+ /* GDB can be totally absent in some disconnected trace scenarios,
+ but we don't really care if this semaphore goes out of sync.
+ That's why we are decrementing it here, but not taking care
+ in other places. */
+ if (loc->probe != NULL)
+ loc->probe->pops->clear_semaphore (loc->probe, loc->gdbarch);
+ }
+ }
+
+ VEC_free (breakpoint_p, tp_vec);
+
+ if (!note)
+ note = trace_stop_notes;
+ ret = target_set_trace_notes (NULL, NULL, note);
+
+ if (!ret && note)
+ warning (_("Target does not support trace notes, note ignored"));
+
+ /* Should change in response to reply? */
current_trace_status ()->running = 0;
}
trace_status_command (char *args, int from_tty)
{
struct trace_status *ts = current_trace_status ();
- int status;
+ int status, ix;
+ VEC(breakpoint_p) *tp_vec = NULL;
+ struct breakpoint *t;
status = target_get_trace_status (ts);
printf_filtered (_("No trace has been run on the target.\n"));
break;
case tstop_command:
- printf_filtered (_("Trace stopped by a tstop command.\n"));
+ if (ts->stop_desc)
+ printf_filtered (_("Trace stopped by a tstop command (%s).\n"),
+ ts->stop_desc);
+ else
+ printf_filtered (_("Trace stopped by a tstop command.\n"));
break;
case trace_buffer_full:
printf_filtered (_("Trace stopped because the buffer was full.\n"));
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);
+ printf_filtered (_("Trace stopped by an "
+ "error (%s, tracepoint %d).\n"),
+ ts->stop_desc, ts->stopping_tracepoint);
else
printf_filtered (_("Trace stopped by an error (%s).\n"),
- ts->error_desc);
+ ts->stop_desc);
break;
case trace_stop_reason_unknown:
printf_filtered (_("Trace stopped for an unknown reason.\n"));
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)
if (ts->circular_buffer)
printf_filtered (_("Trace buffer is circular.\n"));
+ if (ts->user_name && strlen (ts->user_name) > 0)
+ printf_filtered (_("Trace user is %s.\n"), ts->user_name);
+
+ if (ts->notes && strlen (ts->notes) > 0)
+ printf_filtered (_("Trace notes: %s.\n"), ts->notes);
+
/* Now report on what we're doing with tfind. */
if (traceframe_number >= 0)
printf_filtered (_("Looking at trace frame %d, tracepoint %d.\n"),
traceframe_number, tracepoint_number);
else
printf_filtered (_("Not looking at any trace frame.\n"));
+
+ /* Report start/stop times if supplied. */
+ if (ts->start_time)
+ {
+ if (ts->stop_time)
+ {
+ LONGEST run_time = ts->stop_time - ts->start_time;
+
+ /* Reporting a run time is more readable than two long numbers. */
+ printf_filtered (_("Trace started at %ld.%06ld secs, stopped %ld.%06ld secs later.\n"),
+ (long int) ts->start_time / 1000000,
+ (long int) ts->start_time % 1000000,
+ (long int) run_time / 1000000,
+ (long int) run_time % 1000000);
+ }
+ else
+ printf_filtered (_("Trace started at %ld.%06ld secs.\n"),
+ (long int) ts->start_time / 1000000,
+ (long int) ts->start_time % 1000000);
+ }
+ else if (ts->stop_time)
+ printf_filtered (_("Trace stopped at %ld.%06ld secs.\n"),
+ (long int) ts->stop_time / 1000000,
+ (long int) ts->stop_time % 1000000);
+
+ /* Now report any per-tracepoint status available. */
+ tp_vec = all_tracepoints ();
+
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+ target_get_tracepoint_status (t, NULL);
+
+ VEC_free (breakpoint_p, tp_vec);
}
/* Report the trace status to uiout, in a way suitable for MI, and not
void
trace_status_mi (int on_stop)
{
+ struct ui_out *uiout = current_uiout;
struct trace_status *ts = current_trace_status ();
int status;
- char *string_status;
status = target_get_trace_status (ts);
stopping_tracepoint);
if (ts->stop_reason == tracepoint_error)
ui_out_field_string (uiout, "error-description",
- ts->error_desc);
+ ts->stop_desc);
}
}
}
ui_out_field_int (uiout, "disconnected", ts->disconnected_tracing);
ui_out_field_int (uiout, "circular", ts->circular_buffer);
+
+ ui_out_field_string (uiout, "user-name", ts->user_name);
+ ui_out_field_string (uiout, "notes", ts->notes);
+
+ {
+ char buf[100];
+
+ xsnprintf (buf, sizeof buf, "%ld.%06ld",
+ (long int) ts->start_time / 1000000,
+ (long int) ts->start_time % 1000000);
+ ui_out_field_string (uiout, "start-time", buf);
+ xsnprintf (buf, sizeof buf, "%ld.%06ld",
+ (long int) ts->stop_time / 1000000,
+ (long int) ts->stop_time % 1000000);
+ ui_out_field_string (uiout, "stop-time", buf);
+ }
}
/* This function handles the details of what to do about an ongoing
disconnected-tracing. */
if (current_trace_status ()->running && from_tty)
{
+ process_tracepoint_on_disconnect ();
+
if (current_trace_status ()->disconnected_tracing)
{
- if (!query (_("Trace is running and will continue after detach; detach anyway? ")))
+ 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? ")))
+ if (!query (_("Trace is running but will "
+ "stop on detach; detach anyway? ")))
error (_("Not confirmed."));
}
}
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_current_traceframe (-1);
set_traceframe_context (NULL);
}
{
int target_frameno = -1, target_tracept = -1;
struct frame_id old_frame_id = null_frame_id;
- char *reply;
- struct breakpoint *tp;
+ struct tracepoint *tp;
+ struct ui_out *uiout = current_uiout;
/* 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
{
if (info_verbose)
printf_filtered ("End of trace buffer.\n");
-#if 0 /* dubious now? */
+#if 0 /* dubious now? */
/* The following will not recurse, since it's
special-cased. */
trace_find_command ("-1", from_tty);
registers_changed ();
target_dcache_invalidate ();
set_traceframe_num (target_frameno);
- set_tracepoint_num (tp ? tp->number : target_tracept);
+ clear_traceframe_info ();
+ set_tracepoint_num (tp ? tp->base.number : target_tracept);
if (target_frameno == -1)
set_traceframe_context (NULL);
else
{
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
/* tfind command */
static void
trace_find_command (char *args, int from_tty)
-{ /* this should only be called with a numeric argument */
+{ /* This should only be called with a numeric argument. */
int frameno = -1;
if (current_trace_status ()->running && !current_trace_status ()->from_file)
- error ("May not look at trace frames while trace is running.");
+ error (_("May not look at trace frames while trace is running."));
if (args == 0 || *args == 0)
{ /* TFIND with no args means find NEXT trace frame. */
if (traceframe_number == -1)
- frameno = 0; /* "next" is first one */
+ frameno = 0; /* "next" is first one. */
else
frameno = traceframe_number + 1;
}
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.");
+ error (_("May not look at trace frames while trace is running."));
if (args == 0 || *args == 0)
pc = regcache_read_pc (get_current_regcache ());
trace_find_tracepoint_command (char *args, int from_tty)
{
int tdp;
- struct breakpoint *tp;
+ struct tracepoint *tp;
if (current_trace_status ()->running && !current_trace_status ()->from_file)
- error ("May not look at trace frames while trace is running.");
+ error (_("May not look at trace frames while trace is running."));
if (args == 0 || *args == 0)
{
if (tracepoint_number == -1)
error (_("No current tracepoint -- please supply an argument."));
else
- tdp = tracepoint_number; /* default is current TDP */
+ tdp = tracepoint_number; /* Default is current TDP. */
}
else
tdp = parse_and_eval_long (args);
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.");
+ error (_("May not look at trace frames while trace is running."));
if (args == 0 || *args == 0)
{
}
else
{
- sals = decode_line_spec (args, 1);
+ sals = decode_line_with_current_source (args, DECODE_LINE_FUNFIRSTLINE);
sal = sals.sals[0];
}
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)
- error ("May not look at trace frames while trace is running.");
+ error (_("May not look at trace frames while trace is running."));
if (args == 0 || *args == 0)
{ /* XXX FIXME: what should default behavior be? */
if (0 != (tmp = strchr (args, ',')))
{
- *tmp++ = '\0'; /* terminate start address */
+ *tmp++ = '\0'; /* Terminate start address. */
while (isspace ((int) *tmp))
tmp++;
start = parse_and_eval_address (args);
stop = parse_and_eval_address (tmp);
}
else
- { /* no explicit end address? */
+ { /* No explicit end address? */
start = parse_and_eval_address (args);
stop = start + 1; /* ??? */
}
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)
- error ("May not look at trace frames while trace is running.");
+ error (_("May not look at trace frames while trace is running."));
if (args == 0 || *args == 0)
- { /* XXX FIXME: what should default behavior be? */
+ { /* XXX FIXME: what should default behavior be? */
printf_filtered ("Usage: tfind outside <startaddr>,<endaddr>\n");
return;
}
if (0 != (tmp = strchr (args, ',')))
{
- *tmp++ = '\0'; /* terminate start address */
+ *tmp++ = '\0'; /* Terminate start address. */
while (isspace ((int) *tmp))
tmp++;
start = parse_and_eval_address (args);
stop = parse_and_eval_address (tmp);
}
else
- { /* no explicit end address? */
+ { /* No explicit end address? */
start = parse_and_eval_address (args);
stop = start + 1; /* ??? */
}
struct symbol *sym;
struct minimal_symbol *msym;
struct block *block;
- char **canonical, *symname, *save_args = args;
- struct dict_iterator iter;
+ const char *symname;
+ char *save_args = args;
+ struct block_iterator iter;
int j, count = 0;
struct gdbarch *gdbarch;
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);
+ sals = decode_line_1 (&args, DECODE_LINE_FUNFIRSTLINE, NULL, 0);
if (sals.nelts == 0)
- return; /* presumably decode_line_1 has already warned */
+ return; /* Presumably decode_line_1 has already warned. */
- /* Resolve line numbers to PC */
+ /* Resolve line numbers to PC. */
resolve_sal_pc (&sals.sals[0]);
block = block_for_pc (sals.sals[0].pc);
while (block != 0)
{
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
ALL_BLOCK_SYMBOLS (block, iter, sym)
{
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
if (count == 0)
printf_filtered ("Scope for %s:\n", save_args);
count++;
symname = SYMBOL_PRINT_NAME (sym);
if (symname == NULL || *symname == '\0')
- continue; /* probably botched, certainly useless */
+ continue; /* Probably botched, certainly useless. */
gdbarch = get_objfile_arch (SYMBOL_SYMTAB (sym)->objfile);
switch (SYMBOL_CLASS (sym))
{
default:
- case LOC_UNDEF: /* messed up symbol? */
+ case LOC_UNDEF: /* Messed up symbol? */
printf_filtered ("a bogus symbol, class %d.\n",
SYMBOL_CLASS (sym));
- count--; /* don't count this one */
+ count--; /* Don't count this one. */
continue;
case LOC_CONST:
- printf_filtered ("a constant with value %ld (0x%lx)",
- SYMBOL_VALUE (sym), SYMBOL_VALUE (sym));
+ printf_filtered ("a constant with value %s (%s)",
+ plongest (SYMBOL_VALUE (sym)),
+ hex_string (SYMBOL_VALUE (sym)));
break;
case LOC_CONST_BYTES:
printf_filtered ("constant bytes: ");
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",
gdbarch_register_name (gdbarch, regno));
break;
case LOC_ARG:
- printf_filtered ("an argument at stack/frame offset %ld",
- SYMBOL_VALUE (sym));
+ printf_filtered ("an argument at stack/frame offset %s",
+ plongest (SYMBOL_VALUE (sym)));
break;
case LOC_LOCAL:
- printf_filtered ("a local variable at frame offset %ld",
- SYMBOL_VALUE (sym));
+ printf_filtered ("a local variable at frame offset %s",
+ plongest (SYMBOL_VALUE (sym)));
break;
case LOC_REF_ARG:
- printf_filtered ("a reference argument at offset %ld",
- SYMBOL_VALUE (sym));
+ printf_filtered ("a reference argument at offset %s",
+ plongest (SYMBOL_VALUE (sym)));
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))
{
struct cmd_list_element *cmd;
- QUIT; /* allow user to bail out with ^C */
+ QUIT; /* Allow user to bail out with ^C. */
action_exp = action->line;
while (isspace ((int) *action_exp))
action_exp++;
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 */
+ { /* Repeat over a comma-separated list. */
+ QUIT; /* Allow user to bail out with ^C. */
if (*action_exp == ',')
action_exp++;
while (isspace ((int) *action_exp))
if (0 == strncasecmp (action_exp, "$reg", 4))
registers_info (NULL, from_tty);
+ else if (0 == strncasecmp (action_exp, "$_ret", 5))
+ ;
else if (0 == strncasecmp (action_exp, "$loc", 4))
locals_info (NULL, from_tty);
else if (0 == strncasecmp (action_exp, "$arg", 4))
trace_dump_command (char *args, int from_tty)
{
struct regcache *regcache;
- struct breakpoint *t;
+ struct tracepoint *t;
int stepping_frame = 0;
struct bp_location *loc;
char *line, *default_collect_line = NULL;
frame. (FIXME this is not reliable, should record each frame's
type.) */
stepping_frame = 1;
- for (loc = t->loc; loc; loc = loc->next)
+ for (loc = t->base.loc; loc; loc = loc->next)
if (loc->address == regcache_read_pc (regcache))
stepping_frame = 0;
- actions = breakpoint_commands (t);
+ actions = breakpoint_commands (&t->base);
/* If there is a default-collect list, make up a collect command,
prepend to the tracepoint's commands, and pass the whole mess to
default_collect_line = xstrprintf ("collect %s", default_collect);
old_chain = make_cleanup (xfree, default_collect_line);
line = default_collect_line;
- validate_actionline (&line, t);
+ validate_actionline (&line, &t->base);
default_collect_action = xmalloc (sizeof (struct command_line));
make_cleanup (xfree, default_collect_action);
default_collect_action->next = actions;
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));
+ 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);
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));
(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);
+ char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
+
+ bin2hex ((gdb_byte *) ts->stop_desc, buf, 0);
fprintf (fp, ":%s", buf);
}
fprintf (fp, ":%x", ts->stopping_tracepoint);
target_upload_tracepoints (&uploaded_tps);
+ for (utp = uploaded_tps; utp; utp = utp->next)
+ target_get_tracepoint_status (NULL, utp);
+
for (utp = uploaded_tps; utp; utp = utp->next)
{
fprintf (fp, "tp T%x:%s:%c:%x:%x",
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)), act);
- for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
+ 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)), act);
if (utp->at_string)
if (utp->cond_string)
{
encode_source_string (utp->number, utp->addr,
- "cond", utp->cond_string, buf, MAX_TRACE_UPLOAD);
+ "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)
buf, MAX_TRACE_UPLOAD);
fprintf (fp, "tp Z%s\n", buf);
}
+ fprintf (fp, "tp V%x:%s:%x:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ utp->hit_count,
+ phex_nz (utp->traceframe_usage,
+ sizeof (utp->traceframe_usage)));
}
free_uploaded_tps (&uploaded_tps);
trace_save (filename, target_does_save);
if (from_tty)
- printf_filtered (_("Trace data saved to file '%s'.\n"), args);
+ printf_filtered (_("Trace data saved to file '%s'.\n"), filename);
do_cleanups (back_to);
}
/* Tell the target what to do with an ongoing tracing run if GDB
disconnects for some reason. */
-void
-send_disconnected_tracing_value (int value)
-{
- target_set_disconnected_tracing (value);
-}
-
static void
set_disconnected_tracing (char *args, int from_tty,
struct cmd_list_element *c)
{
- send_disconnected_tracing_value (disconnected_tracing);
+ target_set_disconnected_tracing (disconnected_tracing);
}
static void
target_set_circular_trace_buffer (circular_trace_buffer);
}
+static void
+set_trace_user (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ int ret;
+
+ ret = target_set_trace_notes (trace_user, NULL, NULL);
+
+ if (!ret)
+ warning (_("Target does not support trace notes, user ignored"));
+}
+
+static void
+set_trace_notes (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ int ret;
+
+ ret = target_set_trace_notes (NULL, trace_notes, NULL);
+
+ if (!ret)
+ warning (_("Target does not support trace notes, note ignored"));
+}
+
+static void
+set_trace_stop_notes (char *args, int from_tty,
+ struct cmd_list_element *c)
+{
+ int ret;
+
+ ret = target_set_trace_notes (NULL, NULL, trace_stop_notes);
+
+ if (!ret)
+ warning (_("Target does not support trace notes, stop note ignored"));
+}
+
/* Convert the memory pointed to by mem into hex, placing result in buf.
* Return a pointer to the last char put in buf (null)
* "stolen" from sparc-stub.c
if NUM is already current. */
void
-set_traceframe_number (int num)
+set_current_traceframe (int num)
{
int newnum;
/* Changing the traceframe changes our view of registers and of the
frame chain. */
registers_changed ();
+
+ clear_traceframe_info ();
+}
+
+/* Make the traceframe NUM be the current trace frame, and do nothing
+ more. */
+
+void
+set_traceframe_number (int num)
+{
+ traceframe_number = num;
}
/* A cleanup used when switching away and back from tfind mode. */
{
struct current_traceframe_cleanup *old = arg;
- set_traceframe_number (old->traceframe_number);
+ set_current_traceframe (old->traceframe_number);
}
static void
restore_current_traceframe_cleanup_dtor);
}
+struct cleanup *
+make_cleanup_restore_traceframe_number (void)
+{
+ return make_cleanup_restore_integer (&traceframe_number);
+}
+
/* Given a number and address, return an uploaded tracepoint with that
number, creating if necessary. */
/* Given a number and address, return an uploaded tracepoint with that
number, creating if necessary. */
-struct uploaded_tsv *
+static struct uploaded_tsv *
get_uploaded_tsv (int num, struct uploaded_tsv **utsvp)
{
struct uploaded_tsv *utsv;
}
}
-/* Look for an existing tracepoint that seems similar enough to the
- uploaded one. Enablement isn't compared, because the user can
- toggle that freely, and may have done so in anticipation of the
- next trace run. */
+/* FIXME this function is heuristic and will miss the cases where the
+ conditional is semantically identical but differs in whitespace,
+ such as "x == 0" vs "x==0". */
-struct breakpoint *
-find_matching_tracepoint (struct uploaded_tp *utp)
+static int
+cond_string_is_same (char *str1, char *str2)
{
- VEC(breakpoint_p) *tp_vec = all_tracepoints ();
+ if (str1 == NULL || str2 == NULL)
+ return (str1 == str2);
+
+ return (strcmp (str1, str2) == 0);
+}
+
+/* Look for an existing tracepoint that seems similar enough to the
+ uploaded one. Enablement isn't compared, because the user can
+ toggle that freely, and may have done so in anticipation of the
+ next trace run. Return the location of matched tracepoint. */
+
+static struct bp_location *
+find_matching_tracepoint_location (struct uploaded_tp *utp)
+{
+ VEC(breakpoint_p) *tp_vec = all_tracepoints ();
int ix;
- struct breakpoint *t;
+ struct breakpoint *b;
struct bp_location *loc;
- for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++)
{
- if (t->type == utp->type
+ struct tracepoint *t = (struct tracepoint *) b;
+
+ if (b->type == utp->type
&& t->step_count == utp->step
&& t->pass_count == utp->pass
- /* FIXME also test conditionals and actions */
+ && cond_string_is_same (t->base.cond_string, utp->cond_string)
+ /* FIXME also test actions. */
)
{
/* Scan the locations for an address match. */
- for (loc = t->loc; loc; loc = loc->next)
+ for (loc = b->loc; loc; loc = loc->next)
{
if (loc->address == utp->addr)
- return t;
+ return loc;
}
}
}
merge_uploaded_tracepoints (struct uploaded_tp **uploaded_tps)
{
struct uploaded_tp *utp;
- struct breakpoint *t;
/* Look for GDB tracepoints that match up with our uploaded versions. */
for (utp = *uploaded_tps; utp; utp = utp->next)
{
- 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));
+ struct bp_location *loc;
+ struct tracepoint *t;
+
+ loc = find_matching_tracepoint_location (utp);
+ if (loc)
+ {
+ /* Mark this location as already inserted. */
+ loc->inserted = 1;
+ t = (struct tracepoint *) loc->owner;
+ printf_filtered (_("Assuming tracepoint %d is same "
+ "as target's tracepoint %d at %s.\n"),
+ loc->owner->number, utp->number,
+ paddress (loc->gdbarch, 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->base.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
/* Trace state variables don't have much to identify them beyond their
name, so just use that to detect matches. */
-struct trace_state_variable *
+static struct trace_state_variable *
find_matching_tsv (struct uploaded_tsv *utsv)
{
if (!utsv->name)
return find_trace_state_variable (utsv->name);
}
-struct trace_state_variable *
+static struct trace_state_variable *
create_tsv_from_upload (struct uploaded_tsv *utsv)
{
const char *namebase;
- char buf[20];
+ char *buf;
int try_num = 0;
struct trace_state_variable *tsv;
+ struct cleanup *old_chain;
if (utsv->name)
{
namebase = utsv->name;
- sprintf (buf, "%s", namebase);
+ buf = xstrprintf ("%s", namebase);
}
else
{
namebase = "__tsv";
- sprintf (buf, "%s_%d", namebase, try_num++);
+ buf = xstrprintf ("%s_%d", namebase, try_num++);
}
/* Fish for a name that is not in use. */
- /* (should check against all internal vars?) */
+ /* (should check against all internal vars?) */
while (find_trace_state_variable (buf))
- sprintf (buf, "%s_%d", namebase, try_num++);
+ {
+ xfree (buf);
+ buf = xstrprintf ("%s_%d", namebase, try_num++);
+ }
+
+ old_chain = make_cleanup (xfree, buf);
/* We have an available name, create the variable. */
- tsv = create_trace_state_variable (xstrdup (buf));
+ tsv = create_trace_state_variable (buf);
tsv->initial_value = utsv->initial_value;
tsv->builtin = utsv->builtin;
+ do_cleanups (old_chain);
+
return tsv;
}
{
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)
int trace_fd = -1;
off_t trace_frames_offset;
off_t cur_offset;
+int cur_traceframe_number;
int cur_data_size;
int trace_regblock_size;
struct uploaded_tp **utpp,
struct uploaded_tsv **utsvp);
+/* Read SIZE bytes into READBUF from the trace frame, starting at
+ TRACE_FD's current position. Note that this call `read'
+ underneath, hence it advances the file's seek position. Throws an
+ error if the `read' syscall fails, or less than SIZE bytes are
+ read. */
+
+static void
+tfile_read (gdb_byte *readbuf, int size)
+{
+ int gotten;
+
+ gotten = read (trace_fd, readbuf, size);
+ if (gotten < 0)
+ perror_with_name (trace_filename);
+ else if (gotten < size)
+ error (_("Premature end of file while reading trace file"));
+}
+
static void
tfile_open (char *filename, int from_tty)
{
+ volatile struct gdb_exception ex;
char *temp;
struct cleanup *old_chain;
int flags;
int scratch_chan;
char header[TRACE_HEADER_SIZE];
- char linebuf[1000]; /* should be max remote packet size or so */
+ char linebuf[1000]; /* Should be max remote packet size or so. */
char byte;
- int bytes, i, gotten;
+ int bytes, i;
struct trace_status *ts;
struct uploaded_tp *uploaded_tps = NULL;
struct uploaded_tsv *uploaded_tsvs = NULL;
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;
}
/* Looks semi-reasonable. Toss the old trace file and work on the new. */
- discard_cleanups (old_chain); /* Don't free filename any more */
+ discard_cleanups (old_chain); /* Don't free filename any more. */
unpush_target (&tfile_ops);
- push_target (&tfile_ops);
-
trace_filename = xstrdup (filename);
trace_fd = scratch_chan;
bytes = 0;
/* Read the file header and test for validity. */
- gotten = read (trace_fd, &header, TRACE_HEADER_SIZE);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < TRACE_HEADER_SIZE)
- error (_("Premature end of file while reading trace file"));
+ tfile_read ((gdb_byte *) &header, TRACE_HEADER_SIZE);
bytes += TRACE_HEADER_SIZE;
if (!(header[0] == 0x7f
&& (strncmp (header + 1, "TRACE0\n", 7) == 0)))
error (_("File is not a valid trace file."));
+ push_target (&tfile_ops);
+
trace_regblock_size = 0;
ts = current_trace_status ();
/* We know we're working with a file. */
ts->disconnected_tracing = 0;
ts->circular_buffer = 0;
- /* Read through a section of newline-terminated lines that
- define things like tracepoints. */
- i = 0;
- while (1)
- {
- gotten = read (trace_fd, &byte, 1);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 1)
- error (_("Premature end of file while reading trace file"));
+ cur_traceframe_number = -1;
- ++bytes;
- if (byte == '\n')
+ TRY_CATCH (ex, RETURN_MASK_ALL)
+ {
+ /* Read through a section of newline-terminated lines that
+ define things like tracepoints. */
+ i = 0;
+ while (1)
{
- /* Empty line marks end of the definition section. */
- if (i == 0)
- break;
- linebuf[i] = '\0';
- i = 0;
- tfile_interp_line (linebuf, &uploaded_tps, &uploaded_tsvs);
+ tfile_read (&byte, 1);
+
+ ++bytes;
+ if (byte == '\n')
+ {
+ /* Empty line marks end of the definition section. */
+ if (i == 0)
+ break;
+ linebuf[i] = '\0';
+ i = 0;
+ tfile_interp_line (linebuf, &uploaded_tps, &uploaded_tsvs);
+ }
+ else
+ linebuf[i++] = byte;
+ if (i >= 1000)
+ error (_("Excessively long lines in trace file"));
}
- else
- linebuf[i++] = byte;
- if (i >= 1000)
- error (_("Excessively long lines in trace file"));
+
+ /* Record the starting offset of the binary trace data. */
+ trace_frames_offset = bytes;
+
+ /* If we don't have a blocksize, we can't interpret the
+ traceframes. */
+ if (trace_regblock_size == 0)
+ error (_("No register block size recorded in trace file"));
}
+ if (ex.reason < 0)
+ {
+ /* Pop the partially set up target. */
+ pop_target ();
+ throw_exception (ex);
+ }
+
+ inferior_appeared (current_inferior (), TFILE_PID);
+ inferior_ptid = pid_to_ptid (TFILE_PID);
+ add_thread_silent (inferior_ptid);
+
+ if (ts->traceframe_count <= 0)
+ warning (_("No traceframes present in this file."));
/* Add the file's tracepoints and variables into the current mix. */
merge_uploaded_tracepoints (&uploaded_tps);
- /* Record the starting offset of the binary trace data. */
- trace_frames_offset = bytes;
-
- /* If we don't have a blocksize, we can't interpret the
- traceframes. */
- if (trace_regblock_size == 0)
- error (_("No register block size recorded in trace file"));
- if (ts->traceframe_count <= 0)
- {
- warning ("No traceframes present in this file.");
- return;
- }
-
-#define TFILE_PID (1)
- inferior_appeared (current_inferior (), TFILE_PID);
- inferior_ptid = pid_to_ptid (TFILE_PID);
- add_thread_silent (inferior_ptid);
-
post_create_inferior (&tfile_ops, from_tty);
-
-#if 0
- /* FIXME this will get defined in MI patch submission */
- tfind_1 (tfind_number, 0, 0, 0, 0);
-#endif
}
/* Interpret the given line from the definitions part of the trace
parse_tsv_definition (p, utsvp);
}
else
- warning ("Ignoring trace file definition \"%s\"", line);
+ warning (_("Ignoring trace file definition \"%s\""), line);
}
/* 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, *p2, *p_temp;
+ char *p = line, *p1, *p2, *p3, *p_temp;
+ int end;
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;
+ xfree (ts->stop_desc);
+ ts->stop_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;
+ xfree (ts->user_name);
+ ts->user_name = NULL;
+ xfree (ts->notes);
+ ts->notes = NULL;
+ ts->start_time = ts->stop_time = 0;
while (*p++)
{
if (p1 == NULL)
error (_("Malformed trace status, at %s\n\
Status line: '%s'\n"), p, line);
+ p3 = strchr (p, ';');
+ if (p3 == NULL)
+ p3 = p + strlen (p);
if (strncmp (p, stop_reason_names[trace_buffer_full], p1 - p) == 0)
{
p = unpack_varlen_hex (++p1, &val);
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;
}
else if (strncmp (p, stop_reason_names[tstop_command], p1 - p) == 0)
{
- p = unpack_varlen_hex (++p1, &val);
+ p2 = strchr (++p1, ':');
+ if (!p2 || p2 > p3)
+ {
+ /*older style*/
+ p2 = p1;
+ }
+ else if (p2 != p1)
+ {
+ ts->stop_desc = xmalloc (strlen (line));
+ end = hex2bin (p1, ts->stop_desc, (p2 - p1) / 2);
+ ts->stop_desc[end] = '\0';
+ }
+ else
+ ts->stop_desc = xstrdup ("");
+
+ p = unpack_varlen_hex (++p2, &val);
ts->stop_reason = tstop_command;
}
else if (strncmp (p, stop_reason_names[trace_disconnected], 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';
+ ts->stop_desc = xmalloc ((p2 - p1) / 2 + 1);
+ end = hex2bin (p1, ts->stop_desc, (p2 - p1) / 2);
+ ts->stop_desc[end] = '\0';
}
else
- ts->error_desc = xstrdup ("");
+ ts->stop_desc = xstrdup ("");
p = unpack_varlen_hex (++p2, &val);
ts->stopping_tracepoint = val;
p = unpack_varlen_hex (++p1, &val);
ts->circular_buffer = val;
}
+ else if (strncmp (p, "starttime", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->start_time = val;
+ }
+ else if (strncmp (p, "stoptime", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_time = val;
+ }
+ else if (strncmp (p, "username", p1 - p) == 0)
+ {
+ ++p1;
+ ts->user_name = xmalloc (strlen (p) / 2);
+ end = hex2bin (p1, ts->user_name, (p3 - p1) / 2);
+ ts->user_name[end] = '\0';
+ p = p3;
+ }
+ else if (strncmp (p, "notes", p1 - p) == 0)
+ {
+ ++p1;
+ ts->notes = xmalloc (strlen (p) / 2);
+ end = hex2bin (p1, ts->notes, (p3 - p1) / 2);
+ ts->notes[end] = '\0';
+ p = p3;
+ }
else
{
/* Silently skip unknown optional info. */
}
}
+void
+parse_tracepoint_status (char *p, struct breakpoint *bp,
+ struct uploaded_tp *utp)
+{
+ ULONGEST uval;
+ struct tracepoint *tp = (struct tracepoint *) bp;
+
+ p = unpack_varlen_hex (p, &uval);
+ if (tp)
+ tp->base.hit_count += uval;
+ else
+ utp->hit_count += uval;
+ p = unpack_varlen_hex (p + 1, &uval);
+ if (tp)
+ tp->traceframe_usage += uval;
+ else
+ utp->traceframe_usage += uval;
+ /* Ignore any extra, allowing for future extensions. */
+}
+
/* Given a line of text defining a part of a tracepoint, parse it into
an "uploaded tracepoint". */
char *p;
char piece;
ULONGEST num, addr, step, pass, orig_size, xlen, start;
- int enabled, i, end;
+ int enabled, end;
enum bptype type;
- char *cond, *srctype, *src, *buf;
+ 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 (strncmp (srctype, "cmd:", strlen ("cmd:")) == 0)
VEC_safe_push (char_ptr, utp->cmd_strings, xstrdup (buf));
}
+ else if (piece == 'V')
+ {
+ utp = get_uploaded_tp (num, addr, utpp);
+
+ parse_tracepoint_status (p, NULL, utp);
+ }
else
{
/* Don't error out, the target might be sending us optional
return;
pid = ptid_get_pid (inferior_ptid);
- inferior_ptid = null_ptid; /* Avoid confusion from thread stuff */
+ inferior_ptid = null_ptid; /* Avoid confusion from thread stuff. */
exit_inferior_silent (pid);
close (trace_fd);
trace_fd = -1;
- if (trace_filename)
- xfree (trace_filename);
+ xfree (trace_filename);
+ trace_filename = NULL;
}
static void
tfile_files_info (struct target_ops *t)
{
- /* (it would be useful to mention the name of the file) */
+ /* (it would be useful to mention the name of the file). */
printf_filtered ("Looking at a trace file.\n");
}
return -1;
}
+static void
+tfile_get_tracepoint_status (struct breakpoint *tp, struct uploaded_tp *utp)
+{
+ /* Other bits of trace status were collected as part of opening the
+ trace files, so nothing to do here. */
+}
+
/* Given the position of a traceframe in the file, figure out what
address the frame was collected at. This would normally be the
value of a collected PC register, but if not available, we
{
ULONGEST addr = 0;
short tpnum;
- struct breakpoint *tp;
+ struct tracepoint *tp;
off_t saved_offset = cur_offset;
- int gotten;
- /* FIXME dig pc out of collected registers */
+ /* FIXME dig pc out of collected registers. */
/* Fall back to using tracepoint address. */
lseek (trace_fd, tframe_offset, SEEK_SET);
- gotten = read (trace_fd, &tpnum, 2);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 2)
- error (_("Premature end of file while reading trace file"));
+ tfile_read ((gdb_byte *) &tpnum, 2);
+ 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 */
- if (tp && tp->loc)
- addr = tp->loc->address;
+ /* FIXME this is a poor heuristic if multiple locations. */
+ if (tp && tp->base.loc)
+ addr = tp->base.loc->address;
/* Restore our seek position. */
cur_offset = saved_offset;
return addr;
}
+/* Make tfile's selected traceframe match GDB's selected
+ traceframe. */
+
+static void
+set_tfile_traceframe (void)
+{
+ int newnum;
+
+ if (cur_traceframe_number == get_traceframe_number ())
+ return;
+
+ /* Avoid recursion, tfile_trace_find calls us again. */
+ cur_traceframe_number = get_traceframe_number ();
+
+ newnum = target_trace_find (tfind_number,
+ get_traceframe_number (), 0, 0, NULL);
+
+ /* Should not happen. If it does, all bets are off. */
+ if (newnum != get_traceframe_number ())
+ warning (_("could not set tfile's traceframe"));
+}
+
/* Given a type of search and some parameters, scan the collection of
traceframes in the file looking for a match. When found, return
both the traceframe and tracepoint number, otherwise -1 for
ULONGEST addr1, ULONGEST addr2, int *tpp)
{
short tpnum;
- int tfnum = 0, found = 0, gotten;
- int data_size;
- struct breakpoint *tp;
+ int tfnum = 0, found = 0;
+ unsigned int data_size;
+ struct tracepoint *tp;
off_t offset, tframe_offset;
ULONGEST tfaddr;
+ /* Lookups other than by absolute frame number depend on the current
+ trace selected, so make sure it is correct on the tfile end
+ first. */
+ if (type != tfind_number)
+ set_tfile_traceframe ();
+ else if (num == -1)
+ {
+ if (tpp)
+ *tpp = -1;
+ return -1;
+ }
+
lseek (trace_fd, trace_frames_offset, SEEK_SET);
offset = trace_frames_offset;
while (1)
{
tframe_offset = offset;
- gotten = read (trace_fd, &tpnum, 2);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 2)
- error (_("Premature end of file while reading trace file"));
+ tfile_read ((gdb_byte *) &tpnum, 2);
+ tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
+ gdbarch_byte_order
+ (target_gdbarch));
offset += 2;
if (tpnum == 0)
break;
- gotten = read (trace_fd, &data_size, 4);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 4)
- error (_("Premature end of file while reading trace file"));
+ tfile_read ((gdb_byte *) &data_size, 4);
+ data_size = (unsigned int) extract_unsigned_integer
+ ((gdb_byte *) &data_size, 4,
+ gdbarch_byte_order (target_gdbarch));
offset += 4;
switch (type)
{
*tpp = tpnum;
cur_offset = offset;
cur_data_size = data_size;
+ cur_traceframe_number = tfnum;
return tfnum;
}
/* Skip past the traceframe's data. */
return -1;
}
+/* Prototype of the callback passed to tframe_walk_blocks. */
+typedef int (*walk_blocks_callback_func) (char blocktype, void *data);
+
+/* Callback for traceframe_walk_blocks, used to find a given block
+ type in a traceframe. */
+
+static int
+match_blocktype (char blocktype, void *data)
+{
+ char *wantedp = data;
+
+ if (*wantedp == blocktype)
+ return 1;
+
+ return 0;
+}
+
+/* Walk over all traceframe block starting at POS offset from
+ CUR_OFFSET, and call CALLBACK for each block found, passing in DATA
+ unmodified. If CALLBACK returns true, this returns the position in
+ the traceframe where the block is found, relative to the start of
+ the traceframe (cur_offset). Returns -1 if no callback call
+ returned true, indicating that all blocks have been walked. */
+
+static int
+traceframe_walk_blocks (walk_blocks_callback_func callback,
+ int pos, void *data)
+{
+ /* Iterate through a traceframe's blocks, looking for a block of the
+ requested type. */
+
+ lseek (trace_fd, cur_offset + pos, SEEK_SET);
+ while (pos < cur_data_size)
+ {
+ unsigned short mlen;
+ char block_type;
+
+ tfile_read (&block_type, 1);
+
+ ++pos;
+
+ if ((*callback) (block_type, data))
+ return pos;
+
+ switch (block_type)
+ {
+ case 'R':
+ lseek (trace_fd, cur_offset + pos + trace_regblock_size, SEEK_SET);
+ pos += trace_regblock_size;
+ break;
+ case 'M':
+ lseek (trace_fd, cur_offset + pos + 8, SEEK_SET);
+ tfile_read ((gdb_byte *) &mlen, 2);
+ 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;
+ case 'V':
+ lseek (trace_fd, cur_offset + pos + 4 + 8, SEEK_SET);
+ pos += (4 + 8);
+ break;
+ default:
+ error (_("Unknown block type '%c' (0x%x) in trace frame"),
+ block_type, block_type);
+ break;
+ }
+ }
+
+ return -1;
+}
+
+/* Convenience wrapper around traceframe_walk_blocks. Looks for the
+ position offset of a block of type TYPE_WANTED in the current trace
+ frame, starting at POS. Returns -1 if no such block was found. */
+
+static int
+traceframe_find_block_type (char type_wanted, int pos)
+{
+ return traceframe_walk_blocks (match_blocktype, pos, &type_wanted);
+}
+
/* Look for a block of saved registers in the traceframe, and get the
requested register from it. */
struct regcache *regcache, int regno)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
- char block_type;
- int i, pos, offset, regn, regsize, gotten, pc_regno;
- unsigned short mlen;
+ int offset, regn, regsize, pc_regno;
char *regs;
/* An uninitialized reg size says we're not going to be
if (!trace_regblock_size)
return;
+ set_tfile_traceframe ();
+
regs = alloca (trace_regblock_size);
- lseek (trace_fd, cur_offset, SEEK_SET);
- pos = 0;
- while (pos < cur_data_size)
+ if (traceframe_find_block_type ('R', 0) >= 0)
{
- gotten = read (trace_fd, &block_type, 1);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 1)
- error (_("Premature end of file while reading trace file"));
+ tfile_read (regs, trace_regblock_size);
- ++pos;
- switch (block_type)
+ /* Assume the block is laid out in GDB register number order,
+ each register with the size that it has in GDB. */
+ offset = 0;
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
{
- case 'R':
- gotten = read (trace_fd, regs, trace_regblock_size);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < trace_regblock_size)
- error (_("Premature end of file while reading trace file"));
-
- /* Assume the block is laid out in GDB register number order,
- each register with the size that it has in GDB. */
- offset = 0;
- for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ regsize = register_size (gdbarch, regn);
+ /* Make sure we stay within block bounds. */
+ if (offset + regsize >= trace_regblock_size)
+ break;
+ if (regcache_register_status (regcache, regn) == REG_UNKNOWN)
{
- regsize = register_size (gdbarch, regn);
- /* Make sure we stay within block bounds. */
- if (offset + regsize >= trace_regblock_size)
- break;
- if (!regcache_valid_p (regcache, regn))
+ if (regno == regn)
{
- if (regno == regn)
- {
- regcache_raw_supply (regcache, regno, regs + offset);
- break;
- }
- else if (regno == -1)
- {
- regcache_raw_supply (regcache, regn, regs + offset);
- }
+ regcache_raw_supply (regcache, regno, regs + offset);
+ break;
+ }
+ else if (regno == -1)
+ {
+ regcache_raw_supply (regcache, regn, regs + offset);
}
- offset += regsize;
}
- return;
- case 'M':
- lseek (trace_fd, 8, SEEK_CUR);
- 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"));
- lseek (trace_fd, mlen, SEEK_CUR);
- pos += (8 + 2 + mlen);
- break;
- case 'V':
- lseek (trace_fd, 4 + 8, SEEK_CUR);
- pos += (4 + 8);
- break;
- default:
- error ("Unknown block type '%c' (0x%x) in trace frame",
- block_type, block_type);
- break;
+ offset += regsize;
}
+ return;
}
- /* 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. */
+ /* We get here if no register data has been found. Mark registers
+ as unavailable. */
for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
regcache_raw_supply (regcache, regn, NULL);
pc_regno = gdbarch_pc_regnum (gdbarch);
if (pc_regno >= 0 && (regno == -1 || regno == pc_regno))
{
- struct breakpoint *tp = get_tracepoint (tracepoint_number);
+ struct tracepoint *tp = get_tracepoint (tracepoint_number);
- if (tp && tp->loc)
+ if (tp && tp->base.loc)
{
/* But don't try to guess if tracepoint is multi-location... */
- if (tp->loc->next)
+ if (tp->base.loc->next)
{
- warning ("Tracepoint %d has multiple locations, cannot infer $pc",
- tp->number);
+ warning (_("Tracepoint %d has multiple "
+ "locations, cannot infer $pc"),
+ tp->base.number);
return;
}
/* ... or does while-stepping. */
if (tp->step_count > 0)
{
- warning ("Tracepoint %d does while-stepping, cannot infer $pc",
- tp->number);
+ warning (_("Tracepoint %d does while-stepping, "
+ "cannot infer $pc"),
+ tp->base.number);
return;
}
store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
gdbarch_byte_order (gdbarch),
- tp->loc->address);
+ tp->base.loc->address);
regcache_raw_supply (regcache, pc_regno, regs);
}
}
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
- char block_type;
- int pos, gotten;
- ULONGEST maddr, amt;
- unsigned short mlen;
-
/* We're only doing regular memory for now. */
if (object != TARGET_OBJECT_MEMORY)
return -1;
if (readbuf == NULL)
- error ("tfile_xfer_partial: trace file is read-only");
+ error (_("tfile_xfer_partial: trace file is read-only"));
- lseek (trace_fd, cur_offset, SEEK_SET);
- pos = 0;
- while (pos < cur_data_size)
+ set_tfile_traceframe ();
+
+ if (traceframe_number != -1)
{
- gotten = read (trace_fd, &block_type, 1);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 1)
- error (_("Premature end of file while reading trace file"));
- ++pos;
- switch (block_type)
+ int pos = 0;
+
+ /* Iterate through the traceframe's blocks, looking for
+ memory. */
+ while ((pos = traceframe_find_block_type ('M', pos)) >= 0)
{
- case 'R':
- lseek (trace_fd, trace_regblock_size, SEEK_CUR);
- pos += trace_regblock_size;
- break;
- case 'M':
- gotten = read (trace_fd, &maddr, 8);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 8)
- error (_("Premature end of file while reading trace file"));
-
- 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"));
+ ULONGEST maddr, amt;
+ unsigned short mlen;
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+
+ tfile_read ((gdb_byte *) &maddr, 8);
+ maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
+ byte_order);
+ tfile_read ((gdb_byte *) &mlen, 2);
+ mlen = (unsigned short)
+ extract_unsigned_integer ((gdb_byte *) &mlen, 2, byte_order);
+
/* 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);
- /* 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"));
+ if (maddr != offset)
+ lseek (trace_fd, offset - maddr, SEEK_CUR);
+ tfile_read (readbuf, amt);
return amt;
- }
- lseek (trace_fd, mlen, SEEK_CUR);
+ }
+
+ /* Skip over this block. */
pos += (8 + 2 + mlen);
- break;
- case 'V':
- lseek (trace_fd, 4 + 8, SEEK_CUR);
- pos += (4 + 8);
- break;
- default:
- error ("Unknown block type '%c' (0x%x) in traceframe",
- block_type, block_type);
- 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 */
+ /* FIXME account for relocation at some point. */
if (exec_bfd)
{
asection *s;
bfd_size_type size;
- bfd_vma lma;
+ bfd_vma vma;
for (s = exec_bfd->sections; s; s = s->next)
{
- if ((s->flags & SEC_LOAD) == 0 ||
- (s->flags & SEC_READONLY) == 0)
+ if ((s->flags & SEC_LOAD) == 0
+ || (s->flags & SEC_READONLY) == 0)
continue;
- lma = s->lma;
+ vma = s->vma;
size = bfd_get_section_size (s);
- if (lma <= offset && offset < (lma + size))
+ if (vma <= offset && offset < (vma + size))
{
- amt = (lma + size) - offset;
+ ULONGEST amt;
+
+ amt = (vma + size) - offset;
if (amt > len)
amt = len;
amt = bfd_get_section_contents (exec_bfd, s,
- readbuf, offset - lma, amt);
+ readbuf, offset - vma, amt);
return amt;
}
}
static int
tfile_get_trace_state_variable_value (int tsvnum, LONGEST *val)
{
- char block_type;
- int pos, vnum, gotten;
- unsigned short mlen;
+ int pos;
+
+ set_tfile_traceframe ();
- lseek (trace_fd, cur_offset, SEEK_SET);
pos = 0;
- while (pos < cur_data_size)
+ while ((pos = traceframe_find_block_type ('V', pos)) >= 0)
{
- gotten = read (trace_fd, &block_type, 1);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 1)
- error (_("Premature end of file while reading trace file"));
- ++pos;
- switch (block_type)
+ int vnum;
+
+ tfile_read ((gdb_byte *) &vnum, 4);
+ vnum = (int) extract_signed_integer ((gdb_byte *) &vnum, 4,
+ gdbarch_byte_order
+ (target_gdbarch));
+ if (tsvnum == vnum)
{
- case 'R':
- lseek (trace_fd, trace_regblock_size, SEEK_CUR);
- pos += trace_regblock_size;
- break;
- case 'M':
- lseek (trace_fd, 8, SEEK_CUR);
- 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"));
- lseek (trace_fd, mlen, SEEK_CUR);
- pos += (8 + 2 + mlen);
- break;
- case 'V':
- gotten = read (trace_fd, &vnum, 4);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 4)
- error (_("Premature end of file while reading trace file"));
- if (tsvnum == vnum)
- {
- gotten = read (trace_fd, val, 8);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < 8)
- error (_("Premature end of file while reading trace file"));
- return 1;
- }
- lseek (trace_fd, 8, SEEK_CUR);
- pos += (4 + 8);
- break;
- default:
- error ("Unknown block type '%c' (0x%x) in traceframe",
- block_type, block_type);
- break;
+ tfile_read ((gdb_byte *) val, 8);
+ *val = extract_signed_integer ((gdb_byte *) val, 8,
+ gdbarch_byte_order
+ (target_gdbarch));
+ return 1;
}
+ pos += (4 + 8);
}
+
/* Didn't find anything. */
return 0;
}
static int
tfile_has_stack (struct target_ops *ops)
{
- return 1;
+ return traceframe_number != -1;
}
static int
tfile_has_registers (struct target_ops *ops)
+{
+ return traceframe_number != -1;
+}
+
+static int
+tfile_thread_alive (struct target_ops *ops, ptid_t ptid)
{
return 1;
}
+/* Callback for traceframe_walk_blocks. Builds a traceframe_info
+ object for the tfile target's current traceframe. */
+
+static int
+build_traceframe_info (char blocktype, void *data)
+{
+ struct traceframe_info *info = data;
+
+ switch (blocktype)
+ {
+ case 'M':
+ {
+ struct mem_range *r;
+ ULONGEST maddr;
+ unsigned short mlen;
+
+ tfile_read ((gdb_byte *) &maddr, 8);
+ tfile_read ((gdb_byte *) &mlen, 2);
+
+ r = VEC_safe_push (mem_range_s, info->memory, NULL);
+
+ r->start = maddr;
+ r->length = mlen;
+ break;
+ }
+ case 'V':
+ case 'R':
+ case 'S':
+ {
+ break;
+ }
+ default:
+ warning (_("Unhandled trace block type (%d) '%c ' "
+ "while building trace frame info."),
+ blocktype, blocktype);
+ break;
+ }
+
+ return 0;
+}
+
+static struct traceframe_info *
+tfile_traceframe_info (void)
+{
+ struct traceframe_info *info = XCNEW (struct traceframe_info);
+
+ traceframe_walk_blocks (build_traceframe_info, 0, info);
+ return info;
+}
+
static void
init_tfile_ops (void)
{
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_xfer_partial = tfile_xfer_partial;
tfile_ops.to_files_info = tfile_files_info;
tfile_ops.to_get_trace_status = tfile_get_trace_status;
+ tfile_ops.to_get_tracepoint_status = tfile_get_tracepoint_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_traceframe_info = tfile_traceframe_info;
+ tfile_ops.to_thread_alive = tfile_thread_alive;
tfile_ops.to_magic = OPS_MAGIC;
}
+void
+free_current_marker (void *arg)
+{
+ struct static_tracepoint_marker **marker_p = arg;
+
+ if (*marker_p != NULL)
+ {
+ release_static_tracepoint_marker (*marker_p);
+ xfree (*marker_p);
+ }
+ else
+ *marker_p = NULL;
+}
+
+/* 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_out *uiout = current_uiout;
+ 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);
+}
+
+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;
+ struct ui_out *uiout = current_uiout;
+ int i;
+
+ /* We don't have to check target_can_use_agent and agent's capability on
+ static tracepoint here, in order to be compatible with older GDBserver.
+ We don't check USE_AGENT is true or not, because static tracepoints
+ don't work without in-process agent, so we don't bother users to type
+ `set agent on' when to use static tracepoint. */
+
+ 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,
+ void *ignore)
+{
+ 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);
+}
+
+#if !defined(HAVE_LIBEXPAT)
+
+struct traceframe_info *
+parse_traceframe_info (const char *tframe_info)
+{
+ static int have_warned;
+
+ if (!have_warned)
+ {
+ have_warned = 1;
+ warning (_("Can not parse XML trace frame info; XML support "
+ "was disabled at compile time"));
+ }
+
+ return NULL;
+}
+
+#else /* HAVE_LIBEXPAT */
+
+#include "xml-support.h"
+
+/* Handle the start of a <memory> element. */
+
+static void
+traceframe_info_start_memory (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct traceframe_info *info = user_data;
+ struct mem_range *r = VEC_safe_push (mem_range_s, info->memory, NULL);
+ ULONGEST *start_p, *length_p;
+
+ start_p = xml_find_attribute (attributes, "start")->value;
+ length_p = xml_find_attribute (attributes, "length")->value;
+
+ r->start = *start_p;
+ r->length = *length_p;
+}
+
+/* Discard the constructed trace frame info (if an error occurs). */
+
+static void
+free_result (void *p)
+{
+ struct traceframe_info *result = p;
+
+ free_traceframe_info (result);
+}
+
+/* The allowed elements and attributes for an XML memory map. */
+
+static const struct gdb_xml_attribute memory_attributes[] = {
+ { "start", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { "length", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element traceframe_info_children[] = {
+ { "memory", memory_attributes, NULL,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ traceframe_info_start_memory, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element traceframe_info_elements[] = {
+ { "traceframe-info", NULL, traceframe_info_children, GDB_XML_EF_NONE,
+ NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+/* Parse a traceframe-info XML document. */
+
+struct traceframe_info *
+parse_traceframe_info (const char *tframe_info)
+{
+ struct traceframe_info *result;
+ struct cleanup *back_to;
+
+ result = XCNEW (struct traceframe_info);
+ back_to = make_cleanup (free_result, result);
+
+ if (gdb_xml_parse_quick (_("trace frame info"),
+ "traceframe-info.dtd", traceframe_info_elements,
+ tframe_info, result) == 0)
+ {
+ /* Parsed successfully, keep the result. */
+ discard_cleanups (back_to);
+
+ return result;
+ }
+
+ do_cleanups (back_to);
+ return NULL;
+}
+
+#endif /* HAVE_LIBEXPAT */
+
+/* Returns the traceframe_info object for the current traceframe.
+ This is where we avoid re-fetching the object from the target if we
+ already have it cached. */
+
+static struct traceframe_info *
+get_traceframe_info (void)
+{
+ if (traceframe_info == NULL)
+ traceframe_info = target_traceframe_info ();
+
+ return traceframe_info;
+}
+
+/* If the target supports the query, return in RESULT the set of
+ collected memory in the current traceframe, found within the LEN
+ bytes range starting at MEMADDR. Returns true if the target
+ supports the query, otherwise returns false, and RESULT is left
+ undefined. */
+
+int
+traceframe_available_memory (VEC(mem_range_s) **result,
+ CORE_ADDR memaddr, ULONGEST len)
+{
+ struct traceframe_info *info = get_traceframe_info ();
+
+ if (info != NULL)
+ {
+ struct mem_range *r;
+ int i;
+
+ *result = NULL;
+
+ for (i = 0; VEC_iterate (mem_range_s, info->memory, i, r); i++)
+ if (mem_ranges_overlap (r->start, r->length, memaddr, len))
+ {
+ ULONGEST lo1, hi1, lo2, hi2;
+ struct mem_range *nr;
+
+ lo1 = memaddr;
+ hi1 = memaddr + len;
+
+ lo2 = r->start;
+ hi2 = r->start + r->length;
+
+ nr = VEC_safe_push (mem_range_s, *result, NULL);
+
+ nr->start = max (lo1, lo2);
+ nr->length = min (hi1, hi2) - nr->start;
+ }
+
+ normalize_mem_ranges (*result);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Implementation of `sdata' variable. */
+
+static const struct internalvar_funcs sdata_funcs =
+{
+ sdata_make_value,
+ NULL,
+ NULL
+};
+
/* 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_funcs, NULL);
+
traceframe_number = -1;
tracepoint_number = -1;
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 */
+ /* FIXME add a trace variable completer. */
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);
add_com ("tstatus", class_trace, trace_status_command,
_("Display the status of the current trace data collection."));
- add_com ("tstop", class_trace, trace_stop_command,
- _("Stop trace data collection."));
+ add_com ("tstop", class_trace, trace_stop_command, _("\
+Stop trace data collection.\n\
+Usage: tstop [ <notes> ... ]\n\
+Any arguments supplied are recorded with the trace as a stop reason and\n\
+reported by tstatus (if the target supports trace notes)."));
- add_com ("tstart", class_trace, trace_start_command,
- _("Start trace data collection."));
+ add_com ("tstart", class_trace, trace_start_command, _("\
+Start trace data collection.\n\
+Usage: tstart [ <notes> ... ]\n\
+Any arguments supplied are recorded with the trace as a note and\n\
+reported by tstatus (if the target supports trace notes)."));
add_com ("end", class_trace, end_actions_pseudocommand, _("\
Ends a list of commands or actions.\n\
$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 ("");
&setlist,
&showlist);
+ add_setshow_string_cmd ("trace-user", class_trace,
+ &trace_user, _("\
+Set the user name to use for current and future trace runs"), _("\
+Show the user name to use for current and future trace runs"), NULL,
+ set_trace_user, NULL,
+ &setlist, &showlist);
+
+ add_setshow_string_cmd ("trace-notes", class_trace,
+ &trace_notes, _("\
+Set notes string to use for current and future trace runs"), _("\
+Show the notes string to use for current and future trace runs"), NULL,
+ set_trace_notes, NULL,
+ &setlist, &showlist);
+
+ add_setshow_string_cmd ("trace-stop-notes", class_trace,
+ &trace_stop_notes, _("\
+Set notes string to use for future tstop commands"), _("\
+Show the notes string to use for future tstop commands"), NULL,
+ set_trace_stop_notes, NULL,
+ &setlist, &showlist);
+
init_tfile_ops ();
add_target (&tfile_ops);