/* Tracing functionality for remote targets in custom GDB protocol
- Copyright (C) 1997-2013 Free Software Foundation, Inc.
+ Copyright (C) 1997-2014 Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbcmd.h"
#include "value.h"
#include "target.h"
+#include "target-dcache.h"
#include "language.h"
-#include "gdb_string.h"
+#include <string.h>
#include "inferior.h"
#include "breakpoint.h"
#include "tracepoint.h"
#include "filenames.h"
#include "gdbthread.h"
#include "stack.h"
-#include "gdbcore.h"
#include "remote.h"
#include "source.h"
#include "ax.h"
#include "cli/cli-utils.h"
#include "probe.h"
#include "ctf.h"
-#include "completer.h"
#include "filestuff.h"
+#include "rsp-low.h"
/* readline include files */
#include "readline/readline.h"
/* readline defines this. */
#undef savestring
-#ifdef HAVE_UNISTD_H
#include <unistd.h>
-#endif
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
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);
void (*deprecated_trace_start_stop_hook) (int start, int from_tty);
-extern void (*deprecated_readline_begin_hook) (char *, ...);
-extern char *(*deprecated_readline_hook) (char *);
-extern void (*deprecated_readline_end_hook) (void);
-
/*
Tracepoint.c:
/* Tracepoint for last traceframe collected. */
static int tracepoint_number;
-/* Symbol for function for last traceframe collected. */
-static struct symbol *traceframe_fun;
-
-/* Symtab and line for last traceframe collected. */
-static struct symtab_and_line traceframe_sal;
-
/* 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
if (info != NULL)
{
VEC_free (mem_range_s, info->memory);
+ VEC_free (int, info->tvars);
xfree (info);
}
set_traceframe_context (struct frame_info *trace_frame)
{
CORE_ADDR trace_pc;
+ struct symbol *traceframe_fun;
+ struct symtab_and_line traceframe_sal;
/* Save as globals for internal use. */
if (trace_frame != NULL
return NULL;
}
+/* Look for a trace state variable of the given number. Return NULL if
+ not found. */
+
+struct trace_state_variable *
+find_trace_state_variable_by_number (int number)
+{
+ struct trace_state_variable *tsv;
+ int ix;
+
+ for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+ if (tsv->number == number)
+ return tsv;
+
+ return NULL;
+}
+
static void
delete_trace_state_variable (const char *name)
{
memrange_absolute = -1
};
-struct memrange
-{
- int type; /* memrange_absolute for absolute memory range,
- else basereg number. */
- bfd_signed_vma start;
- bfd_signed_vma end;
-};
-
-struct collection_list
- {
- unsigned char regs_mask[32]; /* room for up to 256 regs */
- long listsize;
- long next_memrange;
- struct memrange *list;
- long aexpr_listsize; /* size of array pointed to by expr_list elt */
- long next_aexpr_elt;
- struct agent_expr **aexpr_list;
-
- /* True is the user requested a collection of "$_sdata", "static
- tracepoint data". */
- int strace_data;
- };
-
/* MEMRANGE functions: */
static int memrange_cmp (const void *, const void *);
collect_symbol (p->collect, sym, p->gdbarch, p->frame_regno,
p->frame_offset, p->pc, p->trace_string);
p->count++;
+
+ VEC_safe_push (char_ptr, p->collect->wholly_collected,
+ xstrdup (print_name));
}
/* Add all locals (or args) symbols to collection list. */
xfree (list->aexpr_list);
xfree (list->list);
+
+ VEC_free (char_ptr, list->wholly_collected);
+ VEC_free (char_ptr, list->computed);
}
/* A cleanup wrapper for function clear_collection_list. */
return *str_list;
}
+/* Add the printed expression EXP to *LIST. */
+
+static void
+append_exp (struct expression *exp, VEC(char_ptr) **list)
+{
+ struct ui_file *tmp_stream = mem_fileopen ();
+ char *text;
+
+ print_expression (exp, tmp_stream);
+
+ text = ui_file_xstrdup (tmp_stream, NULL);
+
+ VEC_safe_push (char_ptr, *list, text);
+ ui_file_delete (tmp_stream);
+}
static void
encode_actions_1 (struct command_line *action,
check_typedef (exp->elts[1].type);
add_memrange (collect, memrange_absolute, addr,
TYPE_LENGTH (exp->elts[1].type));
+ append_exp (exp, &collect->computed);
break;
case OP_VAR_VALUE:
- collect_symbol (collect,
- exp->elts[2].symbol,
- tloc->gdbarch,
- frame_reg,
- frame_offset,
- tloc->address,
- trace_string);
+ {
+ struct symbol *sym = exp->elts[2].symbol;
+ char_ptr name = (char_ptr) SYMBOL_NATURAL_NAME (sym);
+
+ collect_symbol (collect,
+ exp->elts[2].symbol,
+ tloc->gdbarch,
+ frame_reg,
+ frame_offset,
+ tloc->address,
+ trace_string);
+ VEC_safe_push (char_ptr,
+ collect->wholly_collected,
+ name);
+ }
break;
default: /* Full-fledged expression. */
}
}
}
+
+ append_exp (exp, &collect->computed);
break;
} /* switch */
do_cleanups (old_chain);
} /* for */
}
-/* Render all actions into gdb protocol. */
+/* Encode actions of tracepoint TLOC->owner and fill TRACEPOINT_LIST
+ and STEPPING_LIST. Return a cleanup pointer to clean up both
+ TRACEPOINT_LIST and STEPPING_LIST. */
-void
-encode_actions (struct bp_location *tloc, char ***tdp_actions,
- char ***stepping_actions)
+struct cleanup *
+encode_actions_and_make_cleanup (struct bp_location *tloc,
+ struct collection_list *tracepoint_list,
+ struct collection_list *stepping_list)
{
char *default_collect_line = NULL;
struct command_line *actions;
struct command_line *default_collect_action = NULL;
int frame_reg;
LONGEST frame_offset;
- struct cleanup *back_to;
- struct collection_list tracepoint_list, stepping_list;
-
- back_to = make_cleanup (null_cleanup, NULL);
+ struct cleanup *back_to, *return_chain;
- init_collection_list (&tracepoint_list);
- init_collection_list (&stepping_list);
+ return_chain = make_cleanup (null_cleanup, NULL);
+ init_collection_list (tracepoint_list);
+ init_collection_list (stepping_list);
- make_cleanup (do_clear_collection_list, &tracepoint_list);
- make_cleanup (do_clear_collection_list, &stepping_list);
-
- *tdp_actions = NULL;
- *stepping_actions = NULL;
+ make_cleanup (do_clear_collection_list, tracepoint_list);
+ make_cleanup (do_clear_collection_list, stepping_list);
+ back_to = make_cleanup (null_cleanup, NULL);
gdbarch_virtual_frame_pointer (tloc->gdbarch,
tloc->address, &frame_reg, &frame_offset);
actions = all_tracepoint_actions_and_cleanup (tloc->owner);
encode_actions_1 (actions, tloc, frame_reg, frame_offset,
- &tracepoint_list, &stepping_list);
+ tracepoint_list, stepping_list);
+
+ memrange_sortmerge (tracepoint_list);
+ memrange_sortmerge (stepping_list);
+
+ do_cleanups (back_to);
+ return return_chain;
+}
+
+/* Render all actions into gdb protocol. */
- memrange_sortmerge (&tracepoint_list);
- memrange_sortmerge (&stepping_list);
+void
+encode_actions_rsp (struct bp_location *tloc, char ***tdp_actions,
+ char ***stepping_actions)
+{
+ struct collection_list tracepoint_list, stepping_list;
+ struct cleanup *cleanup;
+
+ *tdp_actions = NULL;
+ *stepping_actions = NULL;
+
+ cleanup = encode_actions_and_make_cleanup (tloc, &tracepoint_list,
+ &stepping_list);
*tdp_actions = stringify_collection_list (&tracepoint_list);
*stepping_actions = stringify_collection_list (&stepping_list);
- do_cleanups (back_to);
+ do_cleanups (cleanup);
}
static void
/* 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);
+ (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);
+ (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);
+ (long int) (ts->stop_time / 1000000),
+ (long int) (ts->stop_time % 1000000));
/* Now report any per-tracepoint status available. */
tp_vec = all_tracepoints ();
char buf[100];
xsnprintf (buf, sizeof buf, "%ld.%06ld",
- (long int) ts->start_time / 1000000,
- (long int) ts->start_time % 1000000);
+ (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);
+ (long int) (ts->stop_time / 1000000),
+ (long int) (ts->stop_time % 1000000));
ui_out_field_string (uiout, "stop-time", buf);
}
}
else
print_what = SRC_AND_LOC;
- print_stack_frame (get_selected_frame (NULL), 1, print_what);
+ print_stack_frame (get_selected_frame (NULL), 1, print_what, 1);
do_displays ();
}
}
traceframe. Set *STEPPING_FRAME_P to 1 if the current traceframe
is a stepping traceframe. */
-static struct bp_location *
+struct bp_location *
get_traceframe_location (int *stepping_frame_p)
{
struct tracepoint *t;
tracepoint_number, traceframe_number);
old_chain = make_cleanup (null_cleanup, NULL);
+
+ /* This command only makes sense for the current frame, not the
+ selected frame. */
+ make_cleanup_restore_current_thread ();
+ select_frame (get_current_frame ());
+
actions = all_tracepoint_actions_and_cleanup (loc->owner);
trace_dump_actions (actions, 0, stepping_frame, from_tty);
srctype, 0, (int) strlen (src));
if (strlen (buf) + strlen (src) * 2 >= buf_size)
error (_("Source string too long for buffer"));
- bin2hex ((gdb_byte *) src, buf + strlen (buf), 0);
+ bin2hex ((gdb_byte *) src, buf + strlen (buf), strlen (src));
return -1;
}
writer->fp = gdb_fopen_cloexec (writer->pathname, "wb");
if (writer->fp == NULL)
error (_("Unable to open file '%s' for saving trace data (%s)"),
- filename, safe_strerror (errno));
+ writer->pathname, safe_strerror (errno));
}
/* This is the implementation of trace_file_write_ops method
{
char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
- bin2hex ((gdb_byte *) ts->stop_desc, buf, 0);
+ bin2hex ((gdb_byte *) ts->stop_desc, buf, strlen (ts->stop_desc));
fprintf (writer->fp, ":%s", buf);
}
fprintf (writer->fp, ":%x", ts->stopping_tracepoint);
fprintf (writer->fp, ";disconn:%x", ts->disconnected_tracing);
if (ts->circular_buffer)
fprintf (writer->fp, ";circular:%x", ts->circular_buffer);
+ if (ts->start_time)
+ {
+ fprintf (writer->fp, ";starttime:%s",
+ phex_nz (ts->start_time, sizeof (ts->start_time)));
+ }
+ if (ts->stop_time)
+ {
+ fprintf (writer->fp, ";stoptime:%s",
+ phex_nz (ts->stop_time, sizeof (ts->stop_time)));
+ }
if (ts->notes != NULL)
{
char *buf = (char *) alloca (strlen (ts->notes) * 2 + 1);
- bin2hex ((gdb_byte *) ts->notes, buf, 0);
+ bin2hex ((gdb_byte *) ts->notes, buf, strlen (ts->notes));
fprintf (writer->fp, ";notes:%s", buf);
}
if (ts->user_name != NULL)
{
char *buf = (char *) alloca (strlen (ts->user_name) * 2 + 1);
- bin2hex ((gdb_byte *) ts->user_name, buf, 0);
+ bin2hex ((gdb_byte *) ts->user_name, buf, strlen (ts->user_name));
fprintf (writer->fp, ";username:%s", buf);
}
fprintf (writer->fp, "\n");
if (utsv->name)
{
buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
- bin2hex ((gdb_byte *) (utsv->name), buf, 0);
+ bin2hex ((gdb_byte *) (utsv->name), buf, strlen (utsv->name));
}
fprintf (writer->fp, "tsv %x:%s:%x:%s\n",
}
if (ex.reason < 0)
{
- /* Pop the partially set up target. */
- pop_target ();
+ /* Remove the partially set up target. */
+ unpush_target (&tfile_ops);
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."));
merge_uploaded_trace_state_variables (&uploaded_tsvs);
merge_uploaded_tracepoints (&uploaded_tps);
+
+ post_create_inferior (&tfile_ops, from_tty);
}
/* Interpret the given line from the definitions part of the trace
/* Close the trace file and generally clean up. */
static void
-tfile_close (void)
+tfile_close (struct target_ops *self)
{
int pid;
if (trace_fd < 0)
return;
+ pid = ptid_get_pid (inferior_ptid);
+ inferior_ptid = null_ptid; /* Avoid confusion from thread stuff. */
+ exit_inferior_silent (pid);
+
close (trace_fd);
trace_fd = -1;
xfree (trace_filename);
/* The trace status for a file is that tracing can never be run. */
static int
-tfile_get_trace_status (struct trace_status *ts)
+tfile_get_trace_status (struct target_ops *self, struct trace_status *ts)
{
/* Other bits of trace status were collected as part of opening the
trace files, so nothing to do here. */
}
static void
-tfile_get_tracepoint_status (struct breakpoint *tp, struct uploaded_tp *utp)
+tfile_get_tracepoint_status (struct target_ops *self,
+ 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. */
each. */
static int
-tfile_trace_find (enum trace_find_type type, int num,
+tfile_trace_find (struct target_ops *self, enum trace_find_type type, int num,
CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
{
short tpnum;
/* 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))
+
+ /* XXX This guessing code below only works if the PC register isn't
+ a pseudo-register. The value of a pseudo-register isn't stored
+ in the (non-readonly) regcache -- instead it's recomputed
+ (probably from some other cached raw register) whenever the
+ register is read. This guesswork should probably move to some
+ higher layer. */
+ if (pc_regno < 0 || pc_regno >= gdbarch_num_regs (gdbarch))
+ return;
+
+ if (regno == -1 || regno == pc_regno)
{
struct tracepoint *tp = get_tracepoint (tracepoint_number);
}
}
-static LONGEST
+static enum target_xfer_status
tfile_xfer_partial (struct target_ops *ops, enum target_object object,
const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+ const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len)
{
/* We're only doing regular memory for now. */
if (object != TARGET_OBJECT_MEMORY)
- return -1;
+ return TARGET_XFER_E_IO;
if (readbuf == NULL)
error (_("tfile_xfer_partial: trace file is read-only"));
if (maddr != offset)
lseek (trace_fd, offset - maddr, SEEK_CUR);
tfile_read (readbuf, amt);
- return amt;
+ *xfered_len = amt;
+ return TARGET_XFER_OK;
}
/* Skip over this block. */
if (amt > len)
amt = len;
- amt = bfd_get_section_contents (exec_bfd, s,
- readbuf, offset - vma, amt);
- return amt;
+ *xfered_len = bfd_get_section_contents (exec_bfd, s,
+ readbuf, offset - vma, amt);
+ return TARGET_XFER_OK;
}
}
}
/* Indicate failure to find the requested memory block. */
- return -1;
+ return TARGET_XFER_E_IO;
}
/* Iterate through the blocks of a trace frame, looking for a 'V'
block with a matching tsv number. */
static int
-tfile_get_trace_state_variable_value (int tsvnum, LONGEST *val)
+tfile_get_trace_state_variable_value (struct target_ops *self,
+ int tsvnum, LONGEST *val)
{
int pos;
int found = 0;
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. */
break;
}
case 'V':
+ {
+ int vnum;
+
+ tfile_read ((gdb_byte *) &vnum, 4);
+ VEC_safe_push (int, info->tvars, vnum);
+ }
case 'R':
case 'S':
{
}
static struct traceframe_info *
-tfile_traceframe_info (void)
+tfile_traceframe_info (struct target_ops *self)
{
struct traceframe_info *info = XCNEW (struct traceframe_info);
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;
}
r->length = *length_p;
}
+/* Handle the start of a <tvar> element. */
+
+static void
+traceframe_info_start_tvar (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;
+ const char *id_attrib = xml_find_attribute (attributes, "id")->value;
+ int id = gdb_xml_parse_ulongest (parser, id_attrib);
+
+ VEC_safe_push (int, info->tvars, id);
+}
+
/* Discard the constructed trace frame info (if an error occurs). */
static void
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};
+static const struct gdb_xml_attribute tvar_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, 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 },
+ { "tvar", tvar_attributes, NULL,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ traceframe_info_start_tvar, NULL },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
This is where we avoid re-fetching the object from the target if we
already have it cached. */
-static struct traceframe_info *
+struct traceframe_info *
get_traceframe_info (void)
{
if (traceframe_info == NULL)