#include "filenames.h"
#include "gdbthread.h"
#include "stack.h"
+#include "gdbcore.h"
#include "ax.h"
#include "ax-gdb.h"
/* else just return */
}
+/* Report the results of checking the agent expression, as errors or
+ internal errors. */
+
+static void
+report_agent_reqs_errors (struct agent_expr *aexpr, struct agent_reqs *areqs)
+{
+ /* All of the "flaws" are serious bytecode generation issues that
+ should never occur. */
+ if (areqs->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)
+ internal_error (__FILE__, __LINE__,
+ _("expression has min height < 0"));
+
+ /* Issue this error if the stack is predicted to get too deep. The
+ limit is rather arbitrary; a better scheme might be for the
+ target to report how much stack it will have available. The
+ depth roughly corresponds to parenthesization, so a limit of 20
+ amounts to 20 levels of expression nesting, which is actually
+ a pretty big hairy expression. */
+ if (areqs->max_height > 20)
+ error (_("Expression is too complicated."));
+}
+
/* worker function */
-enum actionline_type
+void
validate_actionline (char **line, struct breakpoint *t)
{
struct cmd_list_element *c;
struct cleanup *old_chain = NULL;
char *p, *tmp_p;
struct bp_location *loc;
+ struct agent_expr *aexpr;
+ struct agent_reqs areqs;
/* if EOF is typed, *line is NULL */
if (*line == NULL)
- return END;
+ return;
for (p = *line; isspace ((int) *p);)
p++;
/* Symbol lookup etc. */
if (*p == '\0') /* empty line: just prompt for another line. */
- return BADLINE;
+ return;
if (*p == '#') /* comment line */
- return GENERIC;
+ return;
c = lookup_cmd (&p, cmdlist, "", -1, 1);
if (c == 0)
- {
- warning (_("'%s' is not an action that I know, or is ambiguous."),
- p);
- return BADLINE;
- }
+ error (_("`%s' is not a tracepoint action, or is ambiguous."), p);
if (cmd_cfunc_eq (c, collect_pseudocommand))
{
- struct agent_expr *aexpr;
- struct agent_reqs areqs;
-
do
{ /* repeat over a comma-separated list */
QUIT; /* allow user to bail out with ^C */
{
if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST)
{
- warning (_("constant %s (value %ld) will not be collected."),
- SYMBOL_PRINT_NAME (exp->elts[2].symbol),
- SYMBOL_VALUE (exp->elts[2].symbol));
- return BADLINE;
+ error (_("constant `%s' (value %ld) will not be collected."),
+ SYMBOL_PRINT_NAME (exp->elts[2].symbol),
+ SYMBOL_VALUE (exp->elts[2].symbol));
}
else if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_OPTIMIZED_OUT)
{
- warning (_("%s is optimized away and cannot be collected."),
- SYMBOL_PRINT_NAME (exp->elts[2].symbol));
- return BADLINE;
+ error (_("`%s' is optimized away and cannot be collected."),
+ SYMBOL_PRINT_NAME (exp->elts[2].symbol));
}
}
make_cleanup_free_agent_expr (aexpr);
if (aexpr->len > MAX_AGENT_EXPR_LEN)
- error (_("expression too complicated, try simplifying"));
+ error (_("Expression is too complicated."));
ax_reqs (aexpr, &areqs);
(void) make_cleanup (xfree, areqs.reg_mask);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
-
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
-
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ report_agent_reqs_errors (aexpr, &areqs);
do_cleanups (old_chain);
}
}
while (p && *p++ == ',');
- return GENERIC;
}
+
else if (cmd_cfunc_eq (c, teval_pseudocommand))
{
- struct agent_expr *aexpr;
-
do
{ /* repeat over a comma-separated list */
QUIT; /* allow user to bail out with ^C */
make_cleanup_free_agent_expr (aexpr);
if (aexpr->len > MAX_AGENT_EXPR_LEN)
- error (_("expression too complicated, try simplifying"));
+ error (_("Expression is too complicated."));
+
+ ax_reqs (aexpr, &areqs);
+ (void) make_cleanup (xfree, areqs.reg_mask);
+
+ report_agent_reqs_errors (aexpr, &areqs);
do_cleanups (old_chain);
}
}
while (p && *p++ == ',');
- return GENERIC;
}
+
else if (cmd_cfunc_eq (c, while_stepping_pseudocommand))
{
char *steparg; /* in case warning is necessary */
p++;
steparg = p;
- if (*p == '\0' ||
- (t->step_count = strtol (p, &p, 0)) == 0)
- {
- error (_("'%s': bad step-count."), *line);
- }
- return STEPPING;
+ if (*p == '\0' || (t->step_count = strtol (p, &p, 0)) == 0)
+ error (_("while-stepping step count `%s' is malformed."), *line);
}
+
else if (cmd_cfunc_eq (c, end_actions_pseudocommand))
- return END;
+ ;
+
else
- {
- error (_("'%s' is not a supported tracepoint action."), *line);
- }
+ error (_("`%s' is not a supported tracepoint action."), *line);
}
enum {
old_chain1 = make_cleanup_free_agent_expr (aexpr);
ax_reqs (aexpr, &areqs);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
-
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+
+ report_agent_reqs_errors (aexpr, &areqs);
discard_cleanups (old_chain1);
add_aexpr (collect, aexpr);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
ax_reqs (aexpr, &areqs);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ report_agent_reqs_errors (aexpr, &areqs);
discard_cleanups (old_chain1);
add_aexpr (collect, aexpr);
old_chain1 = make_cleanup_free_agent_expr (aexpr);
ax_reqs (aexpr, &areqs);
- if (areqs.flaw != agent_flaw_none)
- error (_("malformed expression"));
- if (areqs.min_height < 0)
- error (_("gdb: Internal error: expression has min height < 0"));
- if (areqs.max_height > 20)
- error (_("expression too complicated, try simplifying"));
+ report_agent_reqs_errors (aexpr, &areqs);
discard_cleanups (old_chain1);
/* Even though we're not officially collecting, add
gdbarch_virtual_frame_pointer (t->gdbarch,
t->loc->address, &frame_reg, &frame_offset);
- actions = t->commands->commands;
+ actions = breakpoint_commands (t);
/* If there are default expressions to collect, make up a collect
action and prepend to the action list to encode. Note that since
if (*default_collect)
{
char *line;
- enum actionline_type linetype;
default_collect_line = xstrprintf ("collect %s", default_collect);
make_cleanup (xfree, default_collect_line);
line = default_collect_line;
- linetype = validate_actionline (&line, t);
- if (linetype != BADLINE)
- {
- default_collect_action = xmalloc (sizeof (struct command_line));
- make_cleanup (xfree, default_collect_action);
- default_collect_action->next = t->commands->commands;
- default_collect_action->line = line;
- actions = default_collect_action;
- }
+ validate_actionline (&line, t);
+
+ default_collect_action = xmalloc (sizeof (struct command_line));
+ make_cleanup (xfree, default_collect_action);
+ default_collect_action->next = actions;
+ default_collect_action->line = line;
+ actions = default_collect_action;
}
encode_actions_1 (actions, t, tloc, frame_reg, frame_offset,
&tracepoint_list, &stepping_list);
else if (ts->running)
{
printf_filtered (_("Trace is running on the target.\n"));
- if (disconnected_tracing)
- printf_filtered (_("Trace will continue if GDB disconnects.\n"));
- else
- printf_filtered (_("Trace will stop if GDB disconnects.\n"));
}
else
{
ts->buffer_free);
}
+ if (ts->disconnected_tracing)
+ printf_filtered (_("Trace will continue if GDB disconnects.\n"));
+ else
+ printf_filtered (_("Trace will stop if GDB disconnects.\n"));
+
+ if (ts->circular_buffer)
+ printf_filtered (_("Trace buffer is circular.\n"));
+
/* Now report on what we're doing with tfind. */
if (traceframe_number >= 0)
printf_filtered (_("Looking at trace frame %d, tracepoint %d.\n"),
ui_out_field_int (uiout, "buffer-free", (int) ts->buffer_free);
}
-
+/* This function handles the details of what to do about an ongoing
+ tracing run if the user has asked to detach or otherwise disconnect
+ from the target. */
void
-disconnect_or_stop_tracing (int from_tty)
+disconnect_tracing (int from_tty)
{
/* It can happen that the target that was tracing went away on its
own, and we didn't notice. Get a status update, and if the
if (target_get_trace_status (current_trace_status ()) < 0)
current_trace_status ()->running = 0;
+ /* If running interactively, give the user the option to cancel and
+ then decide what to do differently with the run. Scripts are
+ just going to disconnect and let the target deal with it,
+ according to how it's been instructed previously via
+ disconnected-tracing. */
if (current_trace_status ()->running && from_tty)
{
- int cont = query (_("Trace is running. Continue tracing after detach? "));
- /* Note that we send the query result without affecting the
- user's setting of disconnected_tracing, so that the answer is
- a one-time-only. */
- send_disconnected_tracing_value (cont);
-
- /* Also ensure that we do the equivalent of a tstop command if
- tracing is not to continue after the detach. */
- if (!cont)
- stop_tracing ();
+ if (current_trace_status ()->disconnected_tracing)
+ {
+ if (!query (_("Trace is running and will continue after detach; detach anyway? ")))
+ error (_("Not confirmed."));
+ }
+ else
+ {
+ if (!query (_("Trace is running but will stop on detach; detach anyway? ")))
+ error (_("Not confirmed."));
+ }
}
/* Also we want to be out of tfind mode, otherwise things can get
*comma = ',';
}
-/* tdump command */
+
+/* Helper for trace_dump_command. Dump the action list starting at
+ ACTION. STEPPING_ACTIONS is true if we're iterating over the
+ actions of the body of a while-stepping action. STEPPING_FRAME is
+ set if the current traceframe was determined to be a while-stepping
+ traceframe. */
+
static void
-trace_dump_command (char *args, int from_tty)
+trace_dump_actions (struct command_line *action,
+ int stepping_actions, int stepping_frame,
+ int from_tty)
{
- struct regcache *regcache;
- struct gdbarch *gdbarch;
- struct breakpoint *t;
- struct command_line *action;
char *action_exp, *next_comma;
- struct cleanup *old_cleanups;
- int stepping_actions = 0;
- int stepping_frame = 0;
- struct bp_location *loc;
-
- if (tracepoint_number == -1)
- {
- warning (_("No current trace frame."));
- return;
- }
-
- t = get_tracepoint (tracepoint_number);
-
- if (t == NULL)
- error (_("No known tracepoint matches 'current' tracepoint #%d."),
- tracepoint_number);
-
- old_cleanups = make_cleanup (null_cleanup, NULL);
-
- printf_filtered ("Data collected at tracepoint %d, trace frame %d:\n",
- tracepoint_number, traceframe_number);
-
- /* The current frame is a trap frame if the frame PC is equal
- to the tracepoint PC. If not, then the current frame was
- collected during single-stepping. */
-
- regcache = get_current_regcache ();
- gdbarch = get_regcache_arch (regcache);
- /* If the traceframe's address matches any of the tracepoint's
- locations, assume it is a direct hit rather than a while-stepping
- frame. (FIXME this is not reliable, should record each frame's
- type.) */
- stepping_frame = 1;
- for (loc = t->loc; loc; loc = loc->next)
- if (loc->address == regcache_read_pc (regcache))
- stepping_frame = 0;
-
- for (action = t->commands->commands; action; action = action->next)
+ for (; action != NULL; action = action->next)
{
struct cmd_list_element *cmd;
error (_("Bad action list item: %s"), action_exp);
if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand))
- stepping_actions = 1;
- else if (cmd_cfunc_eq (cmd, end_actions_pseudocommand))
- stepping_actions = 0;
+ {
+ int i;
+
+ for (i = 0; i < action->body_count; ++i)
+ trace_dump_actions (action->body_list[i],
+ 1, stepping_frame, from_tty);
+ }
else if (cmd_cfunc_eq (cmd, collect_pseudocommand))
{
/* Display the collected data.
}
}
}
- discard_cleanups (old_cleanups);
+}
+
+/* The tdump command. */
+
+static void
+trace_dump_command (char *args, int from_tty)
+{
+ struct regcache *regcache;
+ struct breakpoint *t;
+ int stepping_frame = 0;
+ struct bp_location *loc;
+
+ if (tracepoint_number == -1)
+ {
+ warning (_("No current trace frame."));
+ return;
+ }
+
+ t = get_tracepoint (tracepoint_number);
+
+ if (t == NULL)
+ error (_("No known tracepoint matches 'current' tracepoint #%d."),
+ tracepoint_number);
+
+ printf_filtered ("Data collected at tracepoint %d, trace frame %d:\n",
+ tracepoint_number, traceframe_number);
+
+ /* The current frame is a trap frame if the frame PC is equal
+ to the tracepoint PC. If not, then the current frame was
+ collected during single-stepping. */
+
+ regcache = get_current_regcache ();
+
+ /* If the traceframe's address matches any of the tracepoint's
+ locations, assume it is a direct hit rather than a while-stepping
+ frame. (FIXME this is not reliable, should record each frame's
+ type.) */
+ stepping_frame = 1;
+ for (loc = t->loc; loc; loc = loc->next)
+ if (loc->address == regcache_read_pc (regcache))
+ stepping_frame = 0;
+
+ trace_dump_actions (breakpoint_commands (t), 0, stepping_frame, from_tty);
}
/* Encode a piece of a tracepoint's source-level definition in a form
struct uploaded_tp *uploaded_tps = NULL, *utp;
struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
int a;
- struct uploaded_string *cmd;
+ char *act;
LONGEST gotten = 0;
ULONGEST offset = 0;
#define MAX_TRACE_UPLOAD 2000
fprintf (fp, ";tfree:%x", ts->buffer_free);
if (ts->buffer_size >= 0)
fprintf (fp, ";tsize:%x", ts->buffer_size);
+ if (ts->disconnected_tracing)
+ fprintf (fp, ";disconn:%x", ts->disconnected_tracing);
+ if (ts->circular_buffer)
+ fprintf (fp, ";circular:%x", ts->circular_buffer);
fprintf (fp, "\n");
/* Note that we want to upload tracepoints and save those, rather
fprintf (fp, ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
utp->cond);
fprintf (fp, "\n");
- for (a = 0; a < utp->numactions; ++a)
+ for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
fprintf (fp, "tp A%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->actions[a]);
- for (a = 0; a < utp->num_step_actions; ++a)
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
fprintf (fp, "tp S%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->step_actions[a]);
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
if (utp->at_string)
{
encode_source_string (utp->number, utp->addr,
"cond", utp->cond_string, buf, MAX_TRACE_UPLOAD);
fprintf (fp, "tp Z%s\n", buf);
}
- for (cmd = utp->cmd_strings; cmd; cmd = cmd->next)
+ for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
{
- encode_source_string (utp->number, utp->addr, "cmd", cmd->str,
+ encode_source_string (utp->number, utp->addr, "cmd", act,
buf, MAX_TRACE_UPLOAD);
fprintf (fp, "tp Z%s\n", buf);
}
memset (utp, 0, sizeof (struct uploaded_tp));
utp->number = num;
utp->addr = addr;
+ utp->actions = NULL;
+ utp->step_actions = NULL;
+ utp->cmd_strings = NULL;
utp->next = *utpp;
*utpp = utp;
return utp;
ts->stop_reason = trace_stop_reason_unknown;
ts->traceframe_count = -1;
ts->buffer_free = 0;
+ ts->disconnected_tracing = 0;
+ ts->circular_buffer = 0;
/* Read through a section of newline-terminated lines that
define things like tracepoints. */
ts->traceframes_created = -1;
ts->buffer_free = -1;
ts->buffer_size = -1;
+ ts->disconnected_tracing = 0;
+ ts->circular_buffer = 0;
while (*p++)
{
p = unpack_varlen_hex (++p1, &val);
ts->stop_reason = tstop_command;
}
+ else if (strncmp (p, stop_reason_names[trace_disconnected], p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = trace_disconnected;
+ }
else if (strncmp (p, stop_reason_names[tracepoint_error], p1 - p) == 0)
{
p2 = strchr (++p1, ':');
p = unpack_varlen_hex (++p1, &val);
ts->buffer_size = val;
}
+ else if (strncmp (p, "disconn", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->disconnected_tracing = val;
+ }
+ else if (strncmp (p, "circular", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->circular_buffer = val;
+ }
else
{
/* Silently skip unknown optional info. */
else if (piece == 'A')
{
utp = get_uploaded_tp (num, addr, utpp);
- utp->actions[utp->numactions++] = xstrdup (p);
+ VEC_safe_push (char_ptr, utp->actions, xstrdup (p));
}
else if (piece == 'S')
{
utp = get_uploaded_tp (num, addr, utpp);
- utp->step_actions[utp->num_step_actions++] = xstrdup (p);
+ VEC_safe_push (char_ptr, utp->step_actions, xstrdup (p));
}
else if (piece == 'Z')
{
else if (strncmp (srctype, "cond:", strlen ("cond:")) == 0)
utp->cond_string = xstrdup (buf);
else if (strncmp (srctype, "cmd:", strlen ("cmd:")) == 0)
- {
- /* FIXME consider using a vector? */
- struct uploaded_string *last, *newlast;
- newlast = (struct uploaded_string *) xmalloc (sizeof (struct uploaded_string));
- newlast->str = xstrdup (buf);
- newlast->next = NULL;
- if (utp->cmd_strings)
- {
- for (last = utp->cmd_strings; last->next; last = last->next)
- ;
- last->next = newlast;
- }
- else
- utp->cmd_strings = newlast;
- }
+ VEC_safe_push (char_ptr, utp->cmd_strings, xstrdup (buf));
}
else
{
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
char block_type;
- int i, pos, offset, regn, regsize, gotten;
+ int i, pos, offset, regn, regsize, gotten, pc_regno;
unsigned short mlen;
char *regs;
break;
}
}
+
+ /* We get here if no register data has been found. Although we
+ don't like making up numbers, GDB has all manner of troubles when
+ the target says some register is not available. Filling in with
+ zeroes is a reasonable fallback. */
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ regcache_raw_supply (regcache, regn, NULL);
+
+ /* We can often usefully guess that the PC is going to be the same
+ as the address of the tracepoint. */
+ pc_regno = gdbarch_pc_regnum (gdbarch);
+ if (pc_regno >= 0 && (regno == -1 || regno == pc_regno))
+ {
+ struct breakpoint *tp = get_tracepoint (tracepoint_number);
+
+ if (tp && tp->loc)
+ {
+ /* But don't try to guess if tracepoint is multi-location... */
+ if (tp->loc->next)
+ {
+ warning ("Tracepoint %d has multiple locations, cannot infer $pc",
+ tp->number);
+ return;
+ }
+ /* ... or does while-stepping. */
+ if (tp->step_count > 0)
+ {
+ warning ("Tracepoint %d does while-stepping, cannot infer $pc",
+ tp->number);
+ return;
+ }
+
+ store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
+ gdbarch_byte_order (gdbarch),
+ tp->loc->address);
+ regcache_raw_supply (regcache, pc_regno, regs);
+ }
+ }
}
static LONGEST
{
char block_type;
int pos, gotten;
- ULONGEST maddr;
+ ULONGEST maddr, amt;
unsigned short mlen;
/* We're only doing regular memory for now. */
perror_with_name (trace_filename);
else if (gotten < 2)
error (_("Premature end of file while reading trace file"));
- if (maddr <= offset && (offset + len) <= (maddr + mlen))
- {
- gotten = read (trace_fd, readbuf, mlen);
- if (gotten < 0)
- perror_with_name (trace_filename);
- else if (gotten < mlen)
- error (_("Premature end of file qwhile reading trace file"));
-
- return mlen;
- }
+ /* 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;
+
+ read (trace_fd, readbuf, amt);
+ return amt;
+ }
lseek (trace_fd, mlen, SEEK_CUR);
pos += (8 + 2 + mlen);
break;
break;
}
}
+
+ /* It's unduly pedantic to refuse to look at the executable for
+ read-only pieces; so do the equivalent of readonly regions aka
+ QTro packet. */
+ /* FIXME account for relocation at some point */
+ if (exec_bfd)
+ {
+ asection *s;
+ bfd_size_type size;
+ bfd_vma lma;
+
+ for (s = exec_bfd->sections; s; s = s->next)
+ {
+ if ((s->flags & SEC_LOAD) == 0 ||
+ (s->flags & SEC_READONLY) == 0)
+ continue;
+
+ lma = s->lma;
+ size = bfd_get_section_size (s);
+ if (lma <= offset && offset < (lma + size))
+ {
+ amt = (lma + size) - offset;
+ if (amt > len)
+ amt = len;
+
+ amt = bfd_get_section_contents (exec_bfd, s,
+ readbuf, offset - lma, amt);
+ return amt;
+ }
+ }
+ }
+
/* Indicate failure to find the requested memory block. */
return -1;
}
return 0;
}
+static int
+tfile_has_all_memory (struct target_ops *ops)
+{
+ return 1;
+}
+
static int
tfile_has_memory (struct target_ops *ops)
{
/* core_stratum might seem more logical, but GDB doesn't like having
more than one core_stratum vector. */
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;