/* Tracing functionality for remote targets in custom GDB protocol
- Copyright (C) 1997-2014 Free Software Foundation, Inc.
+ Copyright (C) 1997-2015 Free Software Foundation, Inc.
This file is part of GDB.
#include "target.h"
#include "target-dcache.h"
#include "language.h"
-#include <string.h>
#include "inferior.h"
#include "breakpoint.h"
#include "tracepoint.h"
#include "ax.h"
#include "ax-gdb.h"
#include "memrange.h"
-#include "exceptions.h"
#include "cli/cli-utils.h"
#include "probe.h"
#include "ctf.h"
#include "filestuff.h"
#include "rsp-low.h"
+#include "tracefile.h"
+#include "location.h"
/* readline include files */
#include "readline/readline.h"
#include <unistd.h>
-#ifndef O_LARGEFILE
-#define O_LARGEFILE 0
-#endif
-
/* 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);
static void add_register (struct collection_list *collection,
unsigned int regno);
-static void free_uploaded_tps (struct uploaded_tp **utpp);
-static void free_uploaded_tsvs (struct uploaded_tsv **utsvp);
-
static struct command_line *
all_tracepoint_actions_and_cleanup (struct breakpoint *t);
static struct trace_status trace_status;
-char *stop_reason_names[] = {
+const char *stop_reason_names[] = {
"tunknown",
"tnotrun",
"tstop",
struct tracepoint *t;
struct command_line *l;
- t = get_tracepoint_by_number (&args, NULL, 1);
+ t = get_tracepoint_by_number (&args, NULL);
if (t)
{
char *tmpbuf =
static int
memrange_cmp (const void *va, const void *vb)
{
- const struct memrange *a = va, *b = vb;
+ const struct memrange *a = (const struct memrange *) va;
+ const struct memrange *b = (const struct memrange *) vb;
if (a->type < b->type)
return -1;
if (memranges->next_memrange >= memranges->listsize)
{
memranges->listsize *= 2;
- memranges->list = xrealloc (memranges->list,
- memranges->listsize);
+ memranges->list = (struct memrange *) xrealloc (memranges->list,
+ memranges->listsize);
}
if (type != memrange_absolute) /* Better collect the base register! */
struct symbol *sym,
void *cb_data)
{
- struct add_local_symbols_data *p = cb_data;
+ struct add_local_symbols_data *p = (struct add_local_symbols_data *) cb_data;
collect_symbol (p->collect, sym, p->gdbarch, p->frame_regno,
p->frame_offset, p->pc, p->trace_string);
long frame_regno, long frame_offset, int type,
int trace_string)
{
- struct block *block;
+ const struct block *block;
struct add_local_symbols_data cb_data;
cb_data.collect = collect;
static void
do_clear_collection_list (void *list)
{
- struct collection_list *l = list;
+ struct collection_list *l = (struct collection_list *) list;
clear_collection_list (l);
}
memset (clist, 0, sizeof *clist);
clist->listsize = 128;
- clist->list = xcalloc (clist->listsize,
- sizeof (struct memrange));
+ clist->list = XCNEWVEC (struct memrange, clist->listsize);
clist->aexpr_listsize = 128;
- clist->aexpr_list = xcalloc (clist->aexpr_listsize,
- sizeof (struct agent_expr *));
+ clist->aexpr_list = XCNEWVEC (struct agent_expr *, clist->aexpr_listsize);
}
/* Reduce a collection list to string form (for gdb protocol). */
{
if (collect->next_aexpr_elt >= collect->aexpr_listsize)
{
- collect->aexpr_list =
- xrealloc (collect->aexpr_list,
- 2 * collect->aexpr_listsize * sizeof (struct agent_expr *));
+ collect->aexpr_list = XRESIZEVEC (struct agent_expr *,
+ collect->aexpr_list,
+ 2 * collect->aexpr_listsize);
collect->aexpr_listsize *= 2;
}
collect->aexpr_list[collect->next_aexpr_elt] = aexpr;
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);
+ if (loc->probe.probe != NULL
+ && loc->probe.probe->pops->set_semaphore != NULL)
+ loc->probe.probe->pops->set_semaphore (loc->probe.probe,
+ loc->probe.objfile,
+ loc->gdbarch);
if (bp_location_downloaded)
observer_notify_breakpoint_modified (b);
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);
+ if (loc->probe.probe != NULL
+ && loc->probe.probe->pops->clear_semaphore != NULL)
+ loc->probe.probe->pops->clear_semaphore (loc->probe.probe,
+ loc->probe.objfile,
+ loc->gdbarch);
}
}
}
}
+/* Error on looking at traceframes while trace is running. */
+
+void
+check_trace_running (struct trace_status *status)
+{
+ if (status->running && status->filename == NULL)
+ error (_("May not look at trace frames while trace is running."));
+}
+
/* trace_find_command takes a trace frame number n,
sends "QTFrame:<n>" to the target,
and accepts a reply that may contain several optional pieces
{ /* This should only be called with a numeric argument. */
int frameno = -1;
- if (current_trace_status ()->running
- && current_trace_status ()->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
+ check_trace_running (current_trace_status ());
if (args == 0 || *args == 0)
{ /* TFIND with no args means find NEXT trace frame. */
{
CORE_ADDR pc;
- if (current_trace_status ()->running
- && current_trace_status ()->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
+ check_trace_running (current_trace_status ());
if (args == 0 || *args == 0)
pc = regcache_read_pc (get_current_regcache ());
int tdp;
struct tracepoint *tp;
- if (current_trace_status ()->running
- && current_trace_status ()->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
+ check_trace_running (current_trace_status ());
if (args == 0 || *args == 0)
{
struct symtab_and_line sal;
struct cleanup *old_chain;
- if (current_trace_status ()->running
- && current_trace_status ()->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
+ check_trace_running (current_trace_status ());
if (args == 0 || *args == 0)
{
sal = find_pc_line (get_frame_pc (get_current_frame ()), 0);
sals.nelts = 1;
- sals.sals = (struct symtab_and_line *)
- xmalloc (sizeof (struct symtab_and_line));
+ sals.sals = XNEW (struct symtab_and_line);
sals.sals[0] = sal;
}
else
static CORE_ADDR start, stop;
char *tmp;
- if (current_trace_status ()->running
- && current_trace_status ()->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
+ check_trace_running (current_trace_status ());
if (args == 0 || *args == 0)
{ /* XXX FIXME: what should default behavior be? */
{
struct symtabs_and_lines sals;
struct symbol *sym;
- struct minimal_symbol *msym;
- struct block *block;
+ struct bound_minimal_symbol msym;
+ const struct block *block;
const char *symname;
char *save_args = args;
struct block_iterator iter;
int j, count = 0;
struct gdbarch *gdbarch;
int regno;
+ struct event_location *location;
+ struct cleanup *back_to;
if (args == 0 || *args == 0)
error (_("requires an argument (function, "
"line or *addr) to define a scope"));
- sals = decode_line_1 (&args, DECODE_LINE_FUNFIRSTLINE, NULL, 0);
+ location = string_to_event_location (&args, current_language);
+ back_to = make_cleanup_delete_event_location (location);
+ sals = decode_line_1 (location, DECODE_LINE_FUNFIRSTLINE, NULL, 0);
if (sals.nelts == 0)
- return; /* Presumably decode_line_1 has already warned. */
+ {
+ /* Presumably decode_line_1 has already warned. */
+ do_cleanups (back_to);
+ return;
+ }
/* Resolve line numbers to PC. */
resolve_sal_pc (&sals.sals[0]);
if (symname == NULL || *symname == '\0')
continue; /* Probably botched, certainly useless. */
- gdbarch = get_objfile_arch (SYMBOL_SYMTAB (sym)->objfile);
+ gdbarch = symbol_arch (sym);
printf_filtered ("Symbol %s is ", symname);
case LOC_UNRESOLVED:
msym = lookup_minimal_symbol (SYMBOL_LINKAGE_NAME (sym),
NULL, NULL);
- if (msym == NULL)
+ if (msym.minsym == NULL)
printf_filtered ("Unresolved Static");
else
{
printf_filtered ("static storage at address ");
printf_filtered ("%s",
paddress (gdbarch,
- SYMBOL_VALUE_ADDRESS (msym)));
+ BMSYMBOL_VALUE_ADDRESS (msym)));
}
break;
case LOC_OPTIMIZED_OUT:
if (count <= 0)
printf_filtered ("Scope for %s contains no locals or arguments.\n",
save_args);
+ do_cleanups (back_to);
}
/* Helper for trace_dump_command. Dump the action list starting at
{
size_t len = next_comma - action_exp;
- cmd = xrealloc (cmd, len + 1);
+ cmd = (char *) xrealloc (cmd, len + 1);
memcpy (cmd, action_exp, len);
cmd[len] = 0;
}
{
size_t len = strlen (action_exp);
- cmd = xrealloc (cmd, len + 1);
+ cmd = (char *) xrealloc (cmd, len + 1);
memcpy (cmd, action_exp, len + 1);
}
make_cleanup (xfree, default_collect_line);
validate_actionline (default_collect_line, t);
- default_collect_action = xmalloc (sizeof (struct command_line));
+ default_collect_action = XNEW (struct command_line);
make_cleanup (xfree, default_collect_action);
default_collect_action->next = actions;
default_collect_action->line = default_collect_line;
extern int
encode_source_string (int tpnum, ULONGEST addr,
- char *srctype, char *src, char *buf, int buf_size)
+ char *srctype, const char *src, char *buf, int buf_size)
{
if (80 + strlen (srctype) > buf_size)
error (_("Buffer too small for source encoding"));
return -1;
}
-/* Free trace file writer. */
+/* Tell the target what to do with an ongoing tracing run if GDB
+ disconnects for some reason. */
static void
-trace_file_writer_xfree (void *arg)
+set_disconnected_tracing (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- struct trace_file_writer *writer = arg;
-
- writer->ops->dtor (writer);
- xfree (writer);
+ target_set_disconnected_tracing (disconnected_tracing);
}
-/* TFILE trace writer. */
-
-struct tfile_trace_file_writer
+static void
+set_circular_trace_buffer (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- struct trace_file_writer base;
-
- /* File pointer to tfile trace file. */
- FILE *fp;
- /* Path name of the tfile trace file. */
- char *pathname;
-};
-
-/* This is the implementation of trace_file_write_ops method
- target_save. We just call the generic target
- target_save_trace_data to do target-side saving. */
+ target_set_circular_trace_buffer (circular_trace_buffer);
+}
-static int
-tfile_target_save (struct trace_file_writer *self,
- const char *filename)
+static void
+set_trace_buffer_size (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- int err = target_save_trace_data (filename);
-
- return (err >= 0);
+ target_set_trace_buffer_size (trace_buffer_size);
}
-/* This is the implementation of trace_file_write_ops method
- dtor. */
-
static void
-tfile_dtor (struct trace_file_writer *self)
+set_trace_user (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
+ int ret;
- xfree (writer->pathname);
+ ret = target_set_trace_notes (trace_user, NULL, NULL);
- if (writer->fp != NULL)
- fclose (writer->fp);
+ if (!ret)
+ warning (_("Target does not support trace notes, user ignored"));
}
-/* This is the implementation of trace_file_write_ops method
- start. It creates the trace file FILENAME and registers some
- cleanups. */
-
static void
-tfile_start (struct trace_file_writer *self, const char *filename)
+set_trace_notes (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
-
- writer->pathname = tilde_expand (filename);
- writer->fp = gdb_fopen_cloexec (writer->pathname, "wb");
- if (writer->fp == NULL)
- error (_("Unable to open file '%s' for saving trace data (%s)"),
- writer->pathname, safe_strerror (errno));
-}
+ int ret;
-/* This is the implementation of trace_file_write_ops method
- write_header. Write the TFILE header. */
+ ret = target_set_trace_notes (NULL, trace_notes, NULL);
-static void
-tfile_write_header (struct trace_file_writer *self)
-{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
- int written;
-
- /* Write a file header, with a high-bit-set char to indicate a
- binary file, plus a hint as what this file is, and a version
- number in case of future needs. */
- written = fwrite ("\x7fTRACE0\n", 8, 1, writer->fp);
- if (written < 1)
- perror_with_name (writer->pathname);
+ if (!ret)
+ warning (_("Target does not support trace notes, note ignored"));
}
-/* This is the implementation of trace_file_write_ops method
- write_regblock_type. Write the size of register block. */
-
static void
-tfile_write_regblock_type (struct trace_file_writer *self, int size)
+set_trace_stop_notes (char *args, int from_tty,
+ struct cmd_list_element *c)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
+ int ret;
- fprintf (writer->fp, "R %x\n", size);
+ ret = target_set_trace_notes (NULL, NULL, trace_stop_notes);
+
+ if (!ret)
+ warning (_("Target does not support trace notes, stop note ignored"));
}
-/* This is the implementation of trace_file_write_ops method
- write_status. */
+/* 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
+ */
+
+static const char hexchars[] = "0123456789abcdef";
-static void
-tfile_write_status (struct trace_file_writer *self,
- struct trace_status *ts)
+static char *
+mem2hex (gdb_byte *mem, char *buf, int count)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
+ gdb_byte ch;
- fprintf (writer->fp, "status %c;%s",
- (ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
- if (ts->stop_reason == tracepoint_error
- || ts->stop_reason == tstop_command)
+ while (count-- > 0)
{
- char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
+ ch = *mem++;
- bin2hex ((gdb_byte *) ts->stop_desc, buf, strlen (ts->stop_desc));
- fprintf (writer->fp, ":%s", buf);
- }
- fprintf (writer->fp, ":%x", ts->stopping_tracepoint);
- if (ts->traceframe_count >= 0)
- fprintf (writer->fp, ";tframes:%x", ts->traceframe_count);
- if (ts->traceframes_created >= 0)
- fprintf (writer->fp, ";tcreated:%x", ts->traceframes_created);
- if (ts->buffer_free >= 0)
- fprintf (writer->fp, ";tfree:%x", ts->buffer_free);
- if (ts->buffer_size >= 0)
- fprintf (writer->fp, ";tsize:%x", ts->buffer_size);
- if (ts->disconnected_tracing)
- 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)));
+ *buf++ = hexchars[ch >> 4];
+ *buf++ = hexchars[ch & 0xf];
}
- if (ts->notes != NULL)
- {
- char *buf = (char *) alloca (strlen (ts->notes) * 2 + 1);
- 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);
+ *buf = 0;
- bin2hex ((gdb_byte *) ts->user_name, buf, strlen (ts->user_name));
- fprintf (writer->fp, ";username:%s", buf);
- }
- fprintf (writer->fp, "\n");
+ return buf;
}
-/* This is the implementation of trace_file_write_ops method
- write_uploaded_tsv. */
+int
+get_traceframe_number (void)
+{
+ return traceframe_number;
+}
-static void
-tfile_write_uploaded_tsv (struct trace_file_writer *self,
- struct uploaded_tsv *utsv)
+int
+get_tracepoint_number (void)
{
- char *buf = "";
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
+ return tracepoint_number;
+}
- if (utsv->name)
+/* Make the traceframe NUM be the current trace frame. Does nothing
+ if NUM is already current. */
+
+void
+set_current_traceframe (int num)
+{
+ int newnum;
+
+ if (traceframe_number == num)
{
- buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
- bin2hex ((gdb_byte *) (utsv->name), buf, strlen (utsv->name));
+ /* Nothing to do. */
+ return;
}
- fprintf (writer->fp, "tsv %x:%s:%x:%s\n",
- utsv->number, phex_nz (utsv->initial_value, 8),
- utsv->builtin, buf);
+ newnum = target_trace_find (tfind_number, num, 0, 0, NULL);
- if (utsv->name)
- xfree (buf);
-}
+ if (newnum != num)
+ warning (_("could not change traceframe"));
-#define MAX_TRACE_UPLOAD 2000
+ set_traceframe_num (newnum);
-/* This is the implementation of trace_file_write_ops method
- write_uploaded_tp. */
+ /* Changing the traceframe changes our view of registers and of the
+ frame chain. */
+ registers_changed ();
-static void
-tfile_write_uploaded_tp (struct trace_file_writer *self,
- struct uploaded_tp *utp)
-{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
- int a;
- char *act;
- char buf[MAX_TRACE_UPLOAD];
-
- fprintf (writer->fp, "tp T%x:%s:%c:%x:%x",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- (utp->enabled ? 'E' : 'D'), utp->step, utp->pass);
- if (utp->type == bp_fast_tracepoint)
- fprintf (writer->fp, ":F%x", utp->orig_size);
- if (utp->cond)
- fprintf (writer->fp,
- ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
- utp->cond);
- fprintf (writer->fp, "\n");
- for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
- fprintf (writer->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->step_actions, a, act); ++a)
- fprintf (writer->fp, "tp S%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- if (utp->at_string)
- {
- encode_source_string (utp->number, utp->addr,
- "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
- fprintf (writer->fp, "tp Z%s\n", buf);
- }
- if (utp->cond_string)
- {
- encode_source_string (utp->number, utp->addr,
- "cond", utp->cond_string,
- buf, MAX_TRACE_UPLOAD);
- fprintf (writer->fp, "tp Z%s\n", buf);
- }
- for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
- {
- encode_source_string (utp->number, utp->addr, "cmd", act,
- buf, MAX_TRACE_UPLOAD);
- fprintf (writer->fp, "tp Z%s\n", buf);
- }
- fprintf (writer->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)));
+ clear_traceframe_info ();
}
-/* This is the implementation of trace_file_write_ops method
- write_definition_end. */
+/* A cleanup used when switching away and back from tfind mode. */
+
+struct current_traceframe_cleanup
+{
+ /* The traceframe we were inspecting. */
+ int traceframe_number;
+};
static void
-tfile_write_definition_end (struct trace_file_writer *self)
+do_restore_current_traceframe_cleanup (void *arg)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
+ struct current_traceframe_cleanup *old
+ = (struct current_traceframe_cleanup *) arg;
- fprintf (writer->fp, "\n");
+ set_current_traceframe (old->traceframe_number);
}
-/* This is the implementation of trace_file_write_ops method
- write_raw_data. */
-
static void
-tfile_write_raw_data (struct trace_file_writer *self, gdb_byte *buf,
- LONGEST len)
+restore_current_traceframe_cleanup_dtor (void *arg)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
+ struct current_traceframe_cleanup *old
+ = (struct current_traceframe_cleanup *) arg;
- if (fwrite (buf, len, 1, writer->fp) < 1)
- perror_with_name (writer->pathname);
+ xfree (old);
}
-/* This is the implementation of trace_file_write_ops method
- end. */
-
-static void
-tfile_end (struct trace_file_writer *self)
+struct cleanup *
+make_cleanup_restore_current_traceframe (void)
{
- struct tfile_trace_file_writer *writer
- = (struct tfile_trace_file_writer *) self;
- uint32_t gotten = 0;
+ struct current_traceframe_cleanup *old =
+ XNEW (struct current_traceframe_cleanup);
+
+ old->traceframe_number = traceframe_number;
- /* Mark the end of trace data. */
- if (fwrite (&gotten, 4, 1, writer->fp) < 1)
- perror_with_name (writer->pathname);
+ return make_cleanup_dtor (do_restore_current_traceframe_cleanup, old,
+ restore_current_traceframe_cleanup_dtor);
}
-/* Operations to write trace buffers into TFILE format. */
+/* Given a number and address, return an uploaded tracepoint with that
+ number, creating if necessary. */
-static const struct trace_file_write_ops tfile_write_ops =
+struct uploaded_tp *
+get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp)
{
- tfile_dtor,
- tfile_target_save,
- tfile_start,
- tfile_write_header,
- tfile_write_regblock_type,
- tfile_write_status,
- tfile_write_uploaded_tsv,
- tfile_write_uploaded_tp,
- tfile_write_definition_end,
- tfile_write_raw_data,
- NULL,
- tfile_end,
-};
+ struct uploaded_tp *utp;
-/* Helper macros. */
+ for (utp = *utpp; utp; utp = utp->next)
+ if (utp->number == num && utp->addr == addr)
+ return utp;
-#define TRACE_WRITE_R_BLOCK(writer, buf, size) \
- writer->ops->frame_ops->write_r_block ((writer), (buf), (size))
-#define TRACE_WRITE_M_BLOCK_HEADER(writer, addr, size) \
- writer->ops->frame_ops->write_m_block_header ((writer), (addr), \
- (size))
-#define TRACE_WRITE_M_BLOCK_MEMORY(writer, buf, size) \
- writer->ops->frame_ops->write_m_block_memory ((writer), (buf), \
- (size))
-#define TRACE_WRITE_V_BLOCK(writer, num, val) \
- writer->ops->frame_ops->write_v_block ((writer), (num), (val))
+ utp = XCNEW (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;
-/* Save tracepoint data to file named FILENAME through WRITER. WRITER
- determines the trace file format. If TARGET_DOES_SAVE is non-zero,
- the save is performed on the target, otherwise GDB obtains all trace
- data and saves it locally. */
+ return utp;
+}
-static void
-trace_save (const char *filename, struct trace_file_writer *writer,
- int target_does_save)
+void
+free_uploaded_tps (struct uploaded_tp **utpp)
{
- struct trace_status *ts = current_trace_status ();
- int status;
- struct uploaded_tp *uploaded_tps = NULL, *utp;
- struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
-
- ULONGEST offset = 0;
- gdb_byte buf[MAX_TRACE_UPLOAD];
-#define MAX_TRACE_UPLOAD 2000
- int written;
- enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
-
- /* If the target is to save the data to a file on its own, then just
- send the command and be done with it. */
- if (target_does_save)
+ struct uploaded_tp *next_one;
+
+ while (*utpp)
{
- if (!writer->ops->target_save (writer, filename))
- error (_("Target failed to save trace data to '%s'."),
- filename);
- return;
+ next_one = (*utpp)->next;
+ xfree (*utpp);
+ *utpp = next_one;
}
+}
- /* Get the trace status first before opening the file, so if the
- target is losing, we can get out without touching files. */
- status = target_get_trace_status (ts);
-
- writer->ops->start (writer, filename);
-
- writer->ops->write_header (writer);
-
- /* Write descriptive info. */
-
- /* Write out the size of a register block. */
- writer->ops->write_regblock_type (writer, trace_regblock_size);
+/* Given a number and address, return an uploaded tracepoint with that
+ number, creating if necessary. */
- /* Write out status of the tracing run (aka "tstatus" info). */
- writer->ops->write_status (writer, ts);
+struct uploaded_tsv *
+get_uploaded_tsv (int num, struct uploaded_tsv **utsvp)
+{
+ struct uploaded_tsv *utsv;
- /* Note that we want to upload tracepoints and save those, rather
- than simply writing out the local ones, because the user may have
- changed tracepoints in GDB in preparation for a future tracing
- run, or maybe just mass-deleted all types of breakpoints as part
- of cleaning up. So as not to contaminate the session, leave the
- data in its uploaded form, don't make into real tracepoints. */
+ for (utsv = *utsvp; utsv; utsv = utsv->next)
+ if (utsv->number == num)
+ return utsv;
- /* Get trace state variables first, they may be checked when parsing
- uploaded commands. */
+ utsv = XCNEW (struct uploaded_tsv);
+ utsv->number = num;
+ utsv->next = *utsvp;
+ *utsvp = utsv;
- target_upload_trace_state_variables (&uploaded_tsvs);
+ return utsv;
+}
- for (utsv = uploaded_tsvs; utsv; utsv = utsv->next)
- writer->ops->write_uploaded_tsv (writer, utsv);
+void
+free_uploaded_tsvs (struct uploaded_tsv **utsvp)
+{
+ struct uploaded_tsv *next_one;
- free_uploaded_tsvs (&uploaded_tsvs);
+ while (*utsvp)
+ {
+ next_one = (*utsvp)->next;
+ xfree (*utsvp);
+ *utsvp = next_one;
+ }
+}
- target_upload_tracepoints (&uploaded_tps);
+/* 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". */
- for (utp = uploaded_tps; utp; utp = utp->next)
- target_get_tracepoint_status (NULL, utp);
+static int
+cond_string_is_same (char *str1, char *str2)
+{
+ if (str1 == NULL || str2 == NULL)
+ return (str1 == str2);
- for (utp = uploaded_tps; utp; utp = utp->next)
- writer->ops->write_uploaded_tp (writer, utp);
+ return (strcmp (str1, str2) == 0);
+}
- free_uploaded_tps (&uploaded_tps);
+/* 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. */
- /* Mark the end of the definition section. */
- writer->ops->write_definition_end (writer);
+static struct bp_location *
+find_matching_tracepoint_location (struct uploaded_tp *utp)
+{
+ VEC(breakpoint_p) *tp_vec = all_tracepoints ();
+ int ix;
+ struct breakpoint *b;
+ struct bp_location *loc;
- /* Get and write the trace data proper. */
- while (1)
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++)
{
- LONGEST gotten = 0;
+ struct tracepoint *t = (struct tracepoint *) b;
- /* The writer supports writing the contents of trace buffer
- directly to trace file. Don't parse the contents of trace
- buffer. */
- if (writer->ops->write_trace_buffer != NULL)
- {
- /* We ask for big blocks, in the hopes of efficiency, but
- will take less if the target has packet size limitations
- or some such. */
- gotten = target_get_raw_trace_data (buf, offset,
- MAX_TRACE_UPLOAD);
- if (gotten < 0)
- error (_("Failure to get requested trace buffer data"));
- /* No more data is forthcoming, we're done. */
- if (gotten == 0)
- break;
-
- writer->ops->write_trace_buffer (writer, buf, gotten);
-
- offset += gotten;
- }
- else
- {
- uint16_t tp_num;
- uint32_t tf_size;
- /* Parse the trace buffers according to how data are stored
- in trace buffer in GDBserver. */
-
- gotten = target_get_raw_trace_data (buf, offset, 6);
-
- if (gotten == 0)
- break;
-
- /* Read the first six bytes in, which is the tracepoint
- number and trace frame size. */
- tp_num = (uint16_t)
- extract_unsigned_integer (&buf[0], 2, byte_order);
-
- tf_size = (uint32_t)
- extract_unsigned_integer (&buf[2], 4, byte_order);
-
- writer->ops->frame_ops->start (writer, tp_num);
- gotten = 6;
-
- if (tf_size > 0)
- {
- unsigned int block;
-
- offset += 6;
-
- for (block = 0; block < tf_size; )
- {
- gdb_byte block_type;
-
- /* We'll fetch one block each time, in order to
- handle the extremely large 'M' block. We first
- fetch one byte to get the type of the block. */
- gotten = target_get_raw_trace_data (buf, offset, 1);
- if (gotten < 1)
- error (_("Failure to get requested trace buffer data"));
-
- gotten = 1;
- block += 1;
- offset += 1;
-
- block_type = buf[0];
- switch (block_type)
- {
- case 'R':
- gotten
- = target_get_raw_trace_data (buf, offset,
- trace_regblock_size);
- if (gotten < trace_regblock_size)
- error (_("Failure to get requested trace"
- " buffer data"));
-
- TRACE_WRITE_R_BLOCK (writer, buf,
- trace_regblock_size);
- break;
- case 'M':
- {
- unsigned short mlen;
- ULONGEST addr;
- LONGEST t;
- int j;
-
- t = target_get_raw_trace_data (buf,offset, 10);
- if (t < 10)
- error (_("Failure to get requested trace"
- " buffer data"));
-
- offset += 10;
- block += 10;
-
- gotten = 0;
- addr = (ULONGEST)
- extract_unsigned_integer (buf, 8,
- byte_order);
- mlen = (unsigned short)
- extract_unsigned_integer (&buf[8], 2,
- byte_order);
-
- TRACE_WRITE_M_BLOCK_HEADER (writer, addr,
- mlen);
-
- /* The memory contents in 'M' block may be
- very large. Fetch the data from the target
- and write them into file one by one. */
- for (j = 0; j < mlen; )
- {
- unsigned int read_length;
-
- if (mlen - j > MAX_TRACE_UPLOAD)
- read_length = MAX_TRACE_UPLOAD;
- else
- read_length = mlen - j;
-
- t = target_get_raw_trace_data (buf,
- offset + j,
- read_length);
- if (t < read_length)
- error (_("Failure to get requested"
- " trace buffer data"));
-
- TRACE_WRITE_M_BLOCK_MEMORY (writer, buf,
- read_length);
-
- j += read_length;
- gotten += read_length;
- }
-
- break;
- }
- case 'V':
- {
- int vnum;
- LONGEST val;
-
- gotten
- = target_get_raw_trace_data (buf, offset,
- 12);
- if (gotten < 12)
- error (_("Failure to get requested"
- " trace buffer data"));
-
- vnum = (int) extract_signed_integer (buf,
- 4,
- byte_order);
- val
- = extract_signed_integer (&buf[4], 8,
- byte_order);
-
- TRACE_WRITE_V_BLOCK (writer, vnum, val);
- }
- break;
- default:
- error (_("Unknown block type '%c' (0x%x) in"
- " trace frame"),
- block_type, block_type);
- }
-
- block += gotten;
- offset += gotten;
- }
- }
- else
- offset += gotten;
-
- writer->ops->frame_ops->end (writer);
- }
- }
-
- writer->ops->end (writer);
-}
-
-/* Return a trace writer for TFILE format. */
-
-static struct trace_file_writer *
-tfile_trace_file_writer_new (void)
-{
- struct tfile_trace_file_writer *writer
- = xmalloc (sizeof (struct tfile_trace_file_writer));
-
- writer->base.ops = &tfile_write_ops;
- writer->fp = NULL;
- writer->pathname = NULL;
-
- return (struct trace_file_writer *) writer;
-}
-
-static void
-trace_save_command (char *args, int from_tty)
-{
- int target_does_save = 0;
- char **argv;
- char *filename = NULL;
- struct cleanup *back_to;
- int generate_ctf = 0;
- struct trace_file_writer *writer = NULL;
-
- if (args == NULL)
- error_no_arg (_("file in which to save trace data"));
-
- argv = gdb_buildargv (args);
- back_to = make_cleanup_freeargv (argv);
-
- for (; *argv; ++argv)
- {
- if (strcmp (*argv, "-r") == 0)
- target_does_save = 1;
- if (strcmp (*argv, "-ctf") == 0)
- generate_ctf = 1;
- else if (**argv == '-')
- error (_("unknown option `%s'"), *argv);
- else
- filename = *argv;
- }
-
- if (!filename)
- error_no_arg (_("file in which to save trace data"));
-
- if (generate_ctf)
- writer = ctf_trace_file_writer_new ();
- else
- writer = tfile_trace_file_writer_new ();
-
- make_cleanup (trace_file_writer_xfree, writer);
-
- trace_save (filename, writer, target_does_save);
-
- if (from_tty)
- printf_filtered (_("Trace data saved to %s '%s'.\n"),
- generate_ctf ? "directory" : "file", filename);
-
- do_cleanups (back_to);
-}
-
-/* Save the trace data to file FILENAME of tfile format. */
-
-void
-trace_save_tfile (const char *filename, int target_does_save)
-{
- struct trace_file_writer *writer;
- struct cleanup *back_to;
-
- writer = tfile_trace_file_writer_new ();
- back_to = make_cleanup (trace_file_writer_xfree, writer);
- trace_save (filename, writer, target_does_save);
- do_cleanups (back_to);
-}
-
-/* Save the trace data to dir DIRNAME of ctf format. */
-
-void
-trace_save_ctf (const char *dirname, int target_does_save)
-{
- struct trace_file_writer *writer;
- struct cleanup *back_to;
-
- writer = ctf_trace_file_writer_new ();
- back_to = make_cleanup (trace_file_writer_xfree, writer);
-
- trace_save (dirname, writer, target_does_save);
- do_cleanups (back_to);
-}
-
-/* Tell the target what to do with an ongoing tracing run if GDB
- disconnects for some reason. */
-
-static void
-set_disconnected_tracing (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- target_set_disconnected_tracing (disconnected_tracing);
-}
-
-static void
-set_circular_trace_buffer (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- target_set_circular_trace_buffer (circular_trace_buffer);
-}
-
-static void
-set_trace_buffer_size (char *args, int from_tty,
- struct cmd_list_element *c)
-{
- target_set_trace_buffer_size (trace_buffer_size);
-}
-
-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
- */
-
-static const char hexchars[] = "0123456789abcdef";
-
-static char *
-mem2hex (gdb_byte *mem, char *buf, int count)
-{
- gdb_byte ch;
-
- while (count-- > 0)
- {
- ch = *mem++;
-
- *buf++ = hexchars[ch >> 4];
- *buf++ = hexchars[ch & 0xf];
- }
-
- *buf = 0;
-
- return buf;
-}
-
-int
-get_traceframe_number (void)
-{
- return traceframe_number;
-}
-
-int
-get_tracepoint_number (void)
-{
- return tracepoint_number;
-}
-
-/* Make the traceframe NUM be the current trace frame. Does nothing
- if NUM is already current. */
-
-void
-set_current_traceframe (int num)
-{
- int newnum;
-
- if (traceframe_number == num)
- {
- /* Nothing to do. */
- return;
- }
-
- newnum = target_trace_find (tfind_number, num, 0, 0, NULL);
-
- if (newnum != num)
- warning (_("could not change traceframe"));
-
- set_traceframe_num (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
-{
- /* The traceframe we were inspecting. */
- int traceframe_number;
-};
-
-static void
-do_restore_current_traceframe_cleanup (void *arg)
-{
- struct current_traceframe_cleanup *old = arg;
-
- set_current_traceframe (old->traceframe_number);
-}
-
-static void
-restore_current_traceframe_cleanup_dtor (void *arg)
-{
- struct current_traceframe_cleanup *old = arg;
-
- xfree (old);
-}
-
-struct cleanup *
-make_cleanup_restore_current_traceframe (void)
-{
- struct current_traceframe_cleanup *old;
-
- old = xmalloc (sizeof (struct current_traceframe_cleanup));
- old->traceframe_number = traceframe_number;
-
- return make_cleanup_dtor (do_restore_current_traceframe_cleanup, old,
- 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. */
-
-struct uploaded_tp *
-get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp)
-{
- struct uploaded_tp *utp;
-
- for (utp = *utpp; utp; utp = utp->next)
- if (utp->number == num && utp->addr == addr)
- return utp;
- utp = (struct uploaded_tp *) xmalloc (sizeof (struct uploaded_tp));
- 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;
-}
-
-static void
-free_uploaded_tps (struct uploaded_tp **utpp)
-{
- struct uploaded_tp *next_one;
-
- while (*utpp)
- {
- next_one = (*utpp)->next;
- xfree (*utpp);
- *utpp = next_one;
- }
-}
-
-/* Given a number and address, return an uploaded tracepoint with that
- number, creating if necessary. */
-
-struct uploaded_tsv *
-get_uploaded_tsv (int num, struct uploaded_tsv **utsvp)
-{
- struct uploaded_tsv *utsv;
-
- for (utsv = *utsvp; utsv; utsv = utsv->next)
- if (utsv->number == num)
- return utsv;
- utsv = (struct uploaded_tsv *) xmalloc (sizeof (struct uploaded_tsv));
- memset (utsv, 0, sizeof (struct uploaded_tsv));
- utsv->number = num;
- utsv->next = *utsvp;
- *utsvp = utsv;
- return utsv;
-}
-
-static void
-free_uploaded_tsvs (struct uploaded_tsv **utsvp)
-{
- struct uploaded_tsv *next_one;
-
- while (*utsvp)
- {
- next_one = (*utsvp)->next;
- xfree (*utsvp);
- *utsvp = next_one;
- }
-}
-
-/* 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". */
-
-static int
-cond_string_is_same (char *str1, char *str2)
-{
- 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 *b;
- struct bp_location *loc;
-
- for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, b); ix++)
- {
- struct tracepoint *t = (struct tracepoint *) b;
-
- if (b->type == utp->type
- && t->step_count == utp->step
- && t->pass_count == utp->pass
- && cond_string_is_same (t->base.cond_string, utp->cond_string)
- /* FIXME also test actions. */
- )
+ if (b->type == utp->type
+ && t->step_count == utp->step
+ && t->pass_count == utp->pass
+ && cond_string_is_same (t->base.cond_string, utp->cond_string)
+ /* FIXME also test actions. */
+ )
{
/* Scan the locations for an address match. */
for (loc = b->loc; loc; loc = loc->next)
"$%s for target's variable %d.\n"),
tsv->name, utsv->number);
}
- /* Give precedence to numberings that come from the target. */
- if (tsv)
- tsv->number = utsv->number;
- }
-
- /* Renumber everything that didn't get a target-assigned number. */
- highest = 0;
- for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
- if (tsv->number > highest)
- highest = tsv->number;
-
- ++highest;
- for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
- if (tsv->number == 0)
- tsv->number = highest++;
-
- free_uploaded_tsvs (uploaded_tsvs);
-}
-
-/* target tfile command */
-
-static struct target_ops tfile_ops;
-
-/* Fill in tfile_ops with its defined operations and properties. */
-
-#define TRACE_HEADER_SIZE 8
-
-static char *trace_filename;
-static int trace_fd = -1;
-static off_t trace_frames_offset;
-static off_t cur_offset;
-static int cur_data_size;
-int trace_regblock_size;
-
-static void tfile_interp_line (char *line,
- 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. */
- gdb_byte byte;
- int bytes, i;
- struct trace_status *ts;
- struct uploaded_tp *uploaded_tps = NULL;
- struct uploaded_tsv *uploaded_tsvs = NULL;
-
- target_preopen (from_tty);
- if (!filename)
- error (_("No trace file specified."));
-
- filename = tilde_expand (filename);
- if (!IS_ABSOLUTE_PATH(filename))
- {
- temp = concat (current_directory, "/", filename, (char *) NULL);
- xfree (filename);
- filename = temp;
- }
-
- old_chain = make_cleanup (xfree, filename);
-
- flags = O_BINARY | O_LARGEFILE;
- flags |= O_RDONLY;
- scratch_chan = gdb_open_cloexec (filename, flags, 0);
- if (scratch_chan < 0)
- perror_with_name (filename);
-
- /* Looks semi-reasonable. Toss the old trace file and work on the new. */
-
- discard_cleanups (old_chain); /* Don't free filename any more. */
- unpush_target (&tfile_ops);
-
- trace_filename = xstrdup (filename);
- trace_fd = scratch_chan;
-
- bytes = 0;
- /* Read the file header and test for validity. */
- 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. Record its name. */
- ts->filename = trace_filename;
- /* Set defaults in case there is no status line. */
- ts->running_known = 0;
- ts->stop_reason = trace_stop_reason_unknown;
- ts->traceframe_count = -1;
- ts->buffer_free = 0;
- ts->disconnected_tracing = 0;
- ts->circular_buffer = 0;
-
- TRY_CATCH (ex, RETURN_MASK_ALL)
- {
- /* Read through a section of newline-terminated lines that
- define things like tracepoints. */
- i = 0;
- while (1)
- {
- 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"));
- }
-
- /* 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)
- {
- /* Remove the partially set up target. */
- unpush_target (&tfile_ops);
- throw_exception (ex);
+ /* Give precedence to numberings that come from the target. */
+ if (tsv)
+ tsv->number = utsv->number;
}
- 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. */
-
- /* Get trace state variables first, they may be checked when parsing
- uploaded commands. */
- 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
- file. */
+ /* Renumber everything that didn't get a target-assigned number. */
+ highest = 0;
+ for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+ if (tsv->number > highest)
+ highest = tsv->number;
-static void
-tfile_interp_line (char *line, struct uploaded_tp **utpp,
- struct uploaded_tsv **utsvp)
-{
- char *p = line;
+ ++highest;
+ for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+ if (tsv->number == 0)
+ tsv->number = highest++;
- if (strncmp (p, "R ", strlen ("R ")) == 0)
- {
- p += strlen ("R ");
- trace_regblock_size = strtol (p, &p, 16);
- }
- else if (strncmp (p, "status ", strlen ("status ")) == 0)
- {
- p += strlen ("status ");
- parse_trace_status (p, current_trace_status ());
- }
- else if (strncmp (p, "tp ", strlen ("tp ")) == 0)
- {
- p += strlen ("tp ");
- parse_tracepoint_definition (p, utpp);
- }
- else if (strncmp (p, "tsv ", strlen ("tsv ")) == 0)
- {
- p += strlen ("tsv ");
- parse_tsv_definition (p, utsvp);
- }
- else
- warning (_("Ignoring trace file definition \"%s\""), line);
+ free_uploaded_tsvs (uploaded_tsvs);
}
/* Parse the part of trace status syntax that is shared between
}
else if (p2 != p1)
{
- ts->stop_desc = xmalloc (strlen (line));
+ ts->stop_desc = (char *) xmalloc (strlen (line));
end = hex2bin (p1, (gdb_byte *) ts->stop_desc, (p2 - p1) / 2);
ts->stop_desc[end] = '\0';
}
p2 = strchr (++p1, ':');
if (p2 != p1)
{
- ts->stop_desc = xmalloc ((p2 - p1) / 2 + 1);
+ ts->stop_desc = (char *) xmalloc ((p2 - p1) / 2 + 1);
end = hex2bin (p1, (gdb_byte *) ts->stop_desc, (p2 - p1) / 2);
ts->stop_desc[end] = '\0';
}
else if (strncmp (p, "username", p1 - p) == 0)
{
++p1;
- ts->user_name = xmalloc (strlen (p) / 2);
+ ts->user_name = (char *) xmalloc (strlen (p) / 2);
end = hex2bin (p1, (gdb_byte *) 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);
+ ts->notes = (char *) xmalloc (strlen (p) / 2);
end = hex2bin (p1, (gdb_byte *) ts->notes, (p3 - p1) / 2);
ts->notes[end] = '\0';
p = p3;
p = unpack_varlen_hex (p, &xlen);
p++; /* skip a colon */
- buf = alloca (strlen (line));
+ buf = (char *) alloca (strlen (line));
end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
buf[end] = '\0';
- if (strncmp (srctype, "at:", strlen ("at:")) == 0)
+ if (startswith (srctype, "at:"))
utp->at_string = xstrdup (buf);
- else if (strncmp (srctype, "cond:", strlen ("cond:")) == 0)
+ else if (startswith (srctype, "cond:"))
utp->cond_string = xstrdup (buf);
- else if (strncmp (srctype, "cmd:", strlen ("cmd:")) == 0)
+ else if (startswith (srctype, "cmd:"))
VEC_safe_push (char_ptr, utp->cmd_strings, xstrdup (buf));
}
else if (piece == 'V')
int end;
struct uploaded_tsv *utsv = NULL;
- buf = alloca (strlen (line));
+ buf = (char *) alloca (strlen (line));
p = line;
p = unpack_varlen_hex (p, &num);
utsv->name = xstrdup (buf);
}
-/* Close the trace file and generally clean up. */
-
-static void
-tfile_close (void)
-{
- 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);
- trace_filename = NULL;
-
- trace_reset_local_state ();
-}
-
-static void
-tfile_files_info (struct target_ops *t)
-{
- printf_filtered ("\t`%s'\n", 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)
-{
- /* Other bits of trace status were collected as part of opening the
- trace files, so nothing to do here. */
-
- 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
- improvise. */
-
-static CORE_ADDR
-tfile_get_traceframe_address (off_t tframe_offset)
-{
- CORE_ADDR addr = 0;
- short tpnum;
- struct tracepoint *tp;
- off_t saved_offset = cur_offset;
-
- /* FIXME dig pc out of collected registers. */
-
- /* Fall back to using tracepoint address. */
- lseek (trace_fd, tframe_offset, SEEK_SET);
- 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->base.loc)
- addr = tp->base.loc->address;
-
- /* Restore our seek position. */
- cur_offset = saved_offset;
- lseek (trace_fd, cur_offset, SEEK_SET);
- return addr;
-}
-
-/* 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
- each. */
-
-static int
-tfile_trace_find (enum trace_find_type type, int num,
- CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
-{
- short tpnum;
- int tfnum = 0, found = 0;
- unsigned int data_size;
- struct tracepoint *tp;
- off_t offset, tframe_offset;
- CORE_ADDR tfaddr;
-
- 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;
- 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;
- 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;
-
- if (type == tfind_number)
- {
- /* Looking for a specific trace frame. */
- if (tfnum == num)
- found = 1;
- }
- else
- {
- /* Start from the _next_ trace frame. */
- if (tfnum > traceframe_number)
- {
- switch (type)
- {
- case tfind_pc:
- tfaddr = tfile_get_traceframe_address (tframe_offset);
- if (tfaddr == addr1)
- found = 1;
- break;
- case tfind_tp:
- tp = get_tracepoint (num);
- if (tp && tpnum == tp->number_on_target)
- found = 1;
- break;
- case tfind_range:
- tfaddr = tfile_get_traceframe_address (tframe_offset);
- if (addr1 <= tfaddr && tfaddr <= addr2)
- found = 1;
- break;
- case tfind_outside:
- tfaddr = tfile_get_traceframe_address (tframe_offset);
- if (!(addr1 <= tfaddr && tfaddr <= addr2))
- found = 1;
- break;
- default:
- internal_error (__FILE__, __LINE__, _("unknown tfind type"));
- }
- }
- }
-
- if (found)
- {
- if (tpp)
- *tpp = tpnum;
- cur_offset = offset;
- cur_data_size = data_size;
-
- return tfnum;
- }
- /* Skip past the traceframe's data. */
- lseek (trace_fd, data_size, SEEK_CUR);
- offset += data_size;
- /* Update our own count of traceframes. */
- ++tfnum;
- }
- /* Did not find what we were looking for. */
- if (tpp)
- *tpp = -1;
- 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 ((gdb_byte *) &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. */
-
-static void
-tfile_fetch_registers (struct target_ops *ops,
- struct regcache *regcache, int regno)
-{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
- int offset, regn, regsize, pc_regno;
- gdb_byte *regs;
-
- /* An uninitialized reg size says we're not going to be
- successful at getting register blocks. */
- if (!trace_regblock_size)
- return;
-
- regs = alloca (trace_regblock_size);
-
- if (traceframe_find_block_type ('R', 0) >= 0)
- {
- tfile_read (regs, trace_regblock_size);
-
- /* 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)
- {
- if (regno == regn)
- {
- regcache_raw_supply (regcache, regno, regs + offset);
- break;
- }
- else if (regno == -1)
- {
- regcache_raw_supply (regcache, regn, regs + offset);
- }
- }
- offset += regsize;
- }
- return;
- }
-
- /* 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);
-
- /* 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);
-
- /* 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);
-
- if (tp && tp->base.loc)
- {
- /* But don't try to guess if tracepoint is multi-location... */
- if (tp->base.loc->next)
- {
- 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->base.number);
- return;
- }
-
- store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
- gdbarch_byte_order (gdbarch),
- tp->base.loc->address);
- regcache_raw_supply (regcache, pc_regno, regs);
- }
- }
-}
-
-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, ULONGEST len,
- ULONGEST *xfered_len)
-{
- /* We're only doing regular memory for now. */
- if (object != TARGET_OBJECT_MEMORY)
- return TARGET_XFER_E_IO;
-
- if (readbuf == NULL)
- error (_("tfile_xfer_partial: trace file is read-only"));
-
- if (traceframe_number != -1)
- {
- int pos = 0;
-
- /* Iterate through the traceframe's blocks, looking for
- memory. */
- while ((pos = traceframe_find_block_type ('M', pos)) >= 0)
- {
- 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;
-
- if (maddr != offset)
- lseek (trace_fd, offset - maddr, SEEK_CUR);
- tfile_read (readbuf, amt);
- *xfered_len = amt;
- return TARGET_XFER_OK;
- }
-
- /* Skip over this block. */
- pos += (8 + 2 + mlen);
- }
- }
-
- /* It's unduly pedantic to refuse to look at the executable for
- read-only pieces; so do the equivalent of readonly regions aka
- QTro packet. */
- /* FIXME account for relocation at some point. */
- if (exec_bfd)
- {
- asection *s;
- bfd_size_type size;
- bfd_vma vma;
-
- for (s = exec_bfd->sections; s; s = s->next)
- {
- if ((s->flags & SEC_LOAD) == 0
- || (s->flags & SEC_READONLY) == 0)
- continue;
-
- vma = s->vma;
- size = bfd_get_section_size (s);
- if (vma <= offset && offset < (vma + size))
- {
- ULONGEST amt;
-
- amt = (vma + size) - offset;
- if (amt > len)
- amt = len;
-
- *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 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)
-{
- int pos;
- int found = 0;
-
- /* Iterate over blocks in current frame and find the last 'V'
- block in which tsv number is TSVNUM. In one trace frame, there
- may be multiple 'V' blocks created for a given trace variable,
- and the last matched 'V' block contains the updated value. */
- pos = 0;
- while ((pos = traceframe_find_block_type ('V', pos)) >= 0)
- {
- 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)
- {
- tfile_read ((gdb_byte *) val, 8);
- *val = extract_signed_integer ((gdb_byte *) val, 8,
- gdbarch_byte_order
- (target_gdbarch ()));
- found = 1;
- }
- pos += (4 + 8);
- }
-
- return found;
-}
-
-static int
-tfile_has_all_memory (struct target_ops *ops)
-{
- return 1;
-}
-
-static int
-tfile_has_memory (struct target_ops *ops)
-{
- return 1;
-}
-
-static int
-tfile_has_stack (struct target_ops *ops)
-{
- 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);
- maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
- gdbarch_byte_order
- (target_gdbarch ()));
- tfile_read ((gdb_byte *) &mlen, 2);
- mlen = (unsigned short)
- extract_unsigned_integer ((gdb_byte *) &mlen,
- 2, gdbarch_byte_order
- (target_gdbarch ()));
-
- r = VEC_safe_push (mem_range_s, info->memory, NULL);
-
- r->start = maddr;
- r->length = mlen;
- break;
- }
- case 'V':
- {
- int vnum;
-
- tfile_read ((gdb_byte *) &vnum, 4);
- VEC_safe_push (int, info->tvars, vnum);
- }
- 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_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;
- 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;
+ struct static_tracepoint_marker **marker_p
+ = (struct static_tracepoint_marker **) arg;
if (*marker_p != NULL)
{
if (endp == NULL)
error (_("bad marker definition: %s"), line);
- marker->str_id = xmalloc (endp - p + 1);
+ marker->str_id = (char *) 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);
+ marker->extra = (char *) xmalloc (strlen (p) + 1);
end = hex2bin (p, (gdb_byte *) marker->extra, strlen (p) / 2);
marker->extra[end] = '\0';
const struct gdb_xml_element *element,
void *user_data, VEC(gdb_xml_value_s) *attributes)
{
- struct traceframe_info *info = user_data;
+ struct traceframe_info *info = (struct traceframe_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;
+ start_p
+ = (ULONGEST *) xml_find_attribute (attributes, "start")->value;
+ length_p
+ = (ULONGEST *) xml_find_attribute (attributes, "length")->value;
r->start = *start_p;
r->length = *length_p;
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;
+ struct traceframe_info *info = (struct traceframe_info *) user_data;
+ const char *id_attrib
+ = (const char *) xml_find_attribute (attributes, "id")->value;
int id = gdb_xml_parse_ulongest (parser, id_attrib);
VEC_safe_push (int, info->tvars, id);
static void
free_result (void *p)
{
- struct traceframe_info *result = p;
+ struct traceframe_info *result = (struct traceframe_info *) p;
free_traceframe_info (result);
}
add_com ("tdump", class_trace, trace_dump_command,
_("Print everything collected at the current tracepoint."));
- add_com ("tsave", class_trace, trace_save_command, _("\
-Save the trace data to a file.\n\
-Use the '-ctf' option to save the data to CTF format.\n\
-Use the '-r' option to direct the target to save directly to the file,\n\
-using its own filesystem."));
-
c = add_com ("tvariable", class_trace, trace_variable_command,_("\
Define a trace state variable.\n\
Argument is a $-prefixed name, optionally followed\n\
Show the notes string to use for future tstop commands"), NULL,
set_trace_stop_notes, NULL,
&setlist, &showlist);
-
- init_tfile_ops ();
-
- add_target_with_completer (&tfile_ops, filename_completer);
}