+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_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;
+}
+
+/* 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. */
+
+static 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. */
+ )
+ {
+ /* Scan the locations for an address match. */
+ for (loc = b->loc; loc; loc = loc->next)
+ {
+ if (loc->address == utp->addr)
+ return loc;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Given a list of tracepoints uploaded from a target, attempt to
+ match them up with existing tracepoints, and create new ones if not
+ found. */
+
+void
+merge_uploaded_tracepoints (struct uploaded_tp **uploaded_tps)
+{
+ struct uploaded_tp *utp;
+
+ /* Look for GDB tracepoints that match up with our uploaded versions. */
+ for (utp = *uploaded_tps; utp; utp = utp->next)
+ {
+ struct bp_location *loc;
+ struct tracepoint *t;
+
+ loc = find_matching_tracepoint_location (utp);
+ if (loc)
+ {
+ /* Mark this location as already inserted. */
+ loc->inserted = 1;
+ t = (struct tracepoint *) loc->owner;
+ printf_filtered (_("Assuming tracepoint %d is same "
+ "as target's tracepoint %d at %s.\n"),
+ loc->owner->number, utp->number,
+ paddress (loc->gdbarch, utp->addr));
+ }
+ else
+ {
+ t = create_tracepoint_from_upload (utp);
+ if (t)
+ printf_filtered (_("Created tracepoint %d for "
+ "target's tracepoint %d at %s.\n"),
+ t->base.number, utp->number,
+ paddress (get_current_arch (), utp->addr));
+ else
+ printf_filtered (_("Failed to create tracepoint for target's "
+ "tracepoint %d at %s, skipping it.\n"),
+ utp->number,
+ paddress (get_current_arch (), utp->addr));
+ }
+ /* Whether found or created, record the number used by the
+ target, to help with mapping target tracepoints back to their
+ counterparts here. */
+ if (t)
+ t->number_on_target = utp->number;
+ }
+
+ free_uploaded_tps (uploaded_tps);
+}
+
+/* Trace state variables don't have much to identify them beyond their
+ name, so just use that to detect matches. */
+
+static struct trace_state_variable *
+find_matching_tsv (struct uploaded_tsv *utsv)
+{
+ if (!utsv->name)
+ return NULL;
+
+ return find_trace_state_variable (utsv->name);
+}
+
+static struct trace_state_variable *
+create_tsv_from_upload (struct uploaded_tsv *utsv)
+{
+ const char *namebase;
+ char *buf;
+ int try_num = 0;
+ struct trace_state_variable *tsv;
+ struct cleanup *old_chain;
+
+ if (utsv->name)
+ {
+ namebase = utsv->name;
+ buf = xstrprintf ("%s", namebase);
+ }
+ else
+ {
+ namebase = "__tsv";
+ buf = xstrprintf ("%s_%d", namebase, try_num++);
+ }
+
+ /* Fish for a name that is not in use. */
+ /* (should check against all internal vars?) */
+ while (find_trace_state_variable (buf))
+ {
+ xfree (buf);
+ buf = xstrprintf ("%s_%d", namebase, try_num++);
+ }
+
+ old_chain = make_cleanup (xfree, buf);
+
+ /* We have an available name, create the variable. */
+ tsv = create_trace_state_variable (buf);
+ tsv->initial_value = utsv->initial_value;
+ tsv->builtin = utsv->builtin;
+
+ observer_notify_tsv_created (tsv->name, tsv->initial_value);
+
+ do_cleanups (old_chain);
+
+ return tsv;
+}
+
+/* Given a list of uploaded trace state variables, try to match them
+ up with existing variables, or create additional ones. */
+
+void
+merge_uploaded_trace_state_variables (struct uploaded_tsv **uploaded_tsvs)
+{
+ int ix;
+ struct uploaded_tsv *utsv;
+ struct trace_state_variable *tsv;
+ int highest;
+
+ /* Most likely some numbers will have to be reassigned as part of
+ the merge, so clear them all in anticipation. */
+ for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+ tsv->number = 0;
+
+ for (utsv = *uploaded_tsvs; utsv; utsv = utsv->next)
+ {
+ tsv = find_matching_tsv (utsv);
+ if (tsv)
+ {
+ if (info_verbose)
+ printf_filtered (_("Assuming trace state variable $%s "
+ "is same as target's variable %d.\n"),
+ tsv->name, utsv->number);
+ }
+ else
+ {
+ tsv = create_tsv_from_upload (utsv);
+ if (info_verbose)
+ printf_filtered (_("Created trace state variable "
+ "$%s for target's variable %d.\n"),
+ tsv->name, utsv->number);
+ }
+ /* Give precedence to numberings that come from the target. */
+ if (tsv)
+ 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. */
+ char 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 = open (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. */
+ ts->from_file = 1;
+ /* 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)
+ {
+ /* Pop the partially set up target. */
+ pop_target ();
+ throw_exception (ex);
+ }
+
+ inferior_appeared (current_inferior (), TFILE_PID);
+ inferior_ptid = pid_to_ptid (TFILE_PID);
+ add_thread_silent (inferior_ptid);
+
+ if (ts->traceframe_count <= 0)
+ warning (_("No traceframes present in this file."));
+
+ /* Add the file's tracepoints and variables into the current mix. */
+
+ /* 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. */
+
+static void
+tfile_interp_line (char *line,
+ struct uploaded_tp **utpp, struct uploaded_tsv **utsvp)
+{
+ char *p = line;
+
+ 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);
+}
+
+/* Parse the part of trace status syntax that is shared between
+ the remote protocol and the trace file reader. */
+
+void
+parse_trace_status (char *line, struct trace_status *ts)
+{
+ char *p = line, *p1, *p2, *p3, *p_temp;
+ int end;
+ ULONGEST val;
+
+ ts->running_known = 1;
+ ts->running = (*p++ == '1');
+ ts->stop_reason = trace_stop_reason_unknown;
+ xfree (ts->stop_desc);
+ ts->stop_desc = NULL;
+ ts->traceframe_count = -1;
+ ts->traceframes_created = -1;
+ ts->buffer_free = -1;
+ ts->buffer_size = -1;
+ ts->disconnected_tracing = 0;
+ ts->circular_buffer = 0;
+ xfree (ts->user_name);
+ ts->user_name = NULL;
+ xfree (ts->notes);
+ ts->notes = NULL;
+ ts->start_time = ts->stop_time = 0;
+
+ while (*p++)
+ {
+ p1 = strchr (p, ':');
+ if (p1 == NULL)
+ error (_("Malformed trace status, at %s\n\
+Status line: '%s'\n"), p, line);
+ p3 = strchr (p, ';');
+ if (p3 == NULL)
+ p3 = p + strlen (p);
+ if (strncmp (p, stop_reason_names[trace_buffer_full], p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = trace_buffer_full;
+ }
+ else if (strncmp (p, stop_reason_names[trace_never_run], p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = trace_never_run;
+ }
+ else if (strncmp (p, stop_reason_names[tracepoint_passcount],
+ p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = tracepoint_passcount;
+ ts->stopping_tracepoint = val;
+ }
+ else if (strncmp (p, stop_reason_names[tstop_command], p1 - p) == 0)
+ {
+ p2 = strchr (++p1, ':');
+ if (!p2 || p2 > p3)
+ {
+ /*older style*/
+ p2 = p1;
+ }
+ else if (p2 != p1)
+ {
+ ts->stop_desc = xmalloc (strlen (line));
+ end = hex2bin (p1, ts->stop_desc, (p2 - p1) / 2);
+ ts->stop_desc[end] = '\0';
+ }
+ else
+ ts->stop_desc = xstrdup ("");
+
+ p = unpack_varlen_hex (++p2, &val);
+ ts->stop_reason = tstop_command;
+ }
+ else if (strncmp (p, stop_reason_names[trace_disconnected], p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = trace_disconnected;
+ }
+ else if (strncmp (p, stop_reason_names[tracepoint_error], p1 - p) == 0)
+ {
+ p2 = strchr (++p1, ':');
+ if (p2 != p1)
+ {
+ ts->stop_desc = xmalloc ((p2 - p1) / 2 + 1);
+ end = hex2bin (p1, ts->stop_desc, (p2 - p1) / 2);
+ ts->stop_desc[end] = '\0';
+ }
+ else
+ ts->stop_desc = xstrdup ("");
+
+ p = unpack_varlen_hex (++p2, &val);
+ ts->stopping_tracepoint = val;
+ ts->stop_reason = tracepoint_error;
+ }
+ else if (strncmp (p, "tframes", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->traceframe_count = val;
+ }
+ else if (strncmp (p, "tcreated", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->traceframes_created = val;
+ }
+ else if (strncmp (p, "tfree", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->buffer_free = val;
+ }
+ else if (strncmp (p, "tsize", p1 - p) == 0)
+ {
+ 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 if (strncmp (p, "starttime", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->start_time = val;
+ }
+ else if (strncmp (p, "stoptime", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_time = val;
+ }
+ else if (strncmp (p, "username", p1 - p) == 0)
+ {
+ ++p1;
+ ts->user_name = xmalloc (strlen (p) / 2);
+ end = hex2bin (p1, ts->user_name, (p3 - p1) / 2);
+ ts->user_name[end] = '\0';
+ p = p3;
+ }
+ else if (strncmp (p, "notes", p1 - p) == 0)
+ {
+ ++p1;
+ ts->notes = xmalloc (strlen (p) / 2);
+ end = hex2bin (p1, ts->notes, (p3 - p1) / 2);
+ ts->notes[end] = '\0';
+ p = p3;
+ }
+ else
+ {
+ /* Silently skip unknown optional info. */
+ p_temp = strchr (p1 + 1, ';');
+ if (p_temp)
+ p = p_temp;
+ else
+ /* Must be at the end. */
+ break;
+ }
+ }
+}
+
+void
+parse_tracepoint_status (char *p, struct breakpoint *bp,
+ struct uploaded_tp *utp)
+{
+ ULONGEST uval;
+ struct tracepoint *tp = (struct tracepoint *) bp;
+
+ p = unpack_varlen_hex (p, &uval);
+ if (tp)
+ tp->base.hit_count += uval;
+ else
+ utp->hit_count += uval;
+ p = unpack_varlen_hex (p + 1, &uval);
+ if (tp)
+ tp->traceframe_usage += uval;
+ else
+ utp->traceframe_usage += uval;
+ /* Ignore any extra, allowing for future extensions. */
+}
+
+/* Given a line of text defining a part of a tracepoint, parse it into
+ an "uploaded tracepoint". */
+
+void
+parse_tracepoint_definition (char *line, struct uploaded_tp **utpp)
+{
+ char *p;
+ char piece;
+ ULONGEST num, addr, step, pass, orig_size, xlen, start;
+ int enabled, end;
+ enum bptype type;
+ char *cond, *srctype, *buf;
+ struct uploaded_tp *utp = NULL;
+
+ p = line;
+ /* Both tracepoint and action definitions start with the same number
+ and address sequence. */
+ piece = *p++;
+ p = unpack_varlen_hex (p, &num);
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &addr);
+ p++; /* skip a colon */
+ if (piece == 'T')
+ {
+ enabled = (*p++ == 'E');
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &step);
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &pass);
+ type = bp_tracepoint;
+ cond = NULL;
+ /* Thumb through optional fields. */
+ while (*p == ':')
+ {
+ p++; /* skip a colon */
+ if (*p == 'F')
+ {
+ type = bp_fast_tracepoint;
+ p++;
+ p = unpack_varlen_hex (p, &orig_size);
+ }
+ else if (*p == 'S')
+ {
+ type = bp_static_tracepoint;
+ p++;
+ }
+ else if (*p == 'X')
+ {
+ p++;
+ p = unpack_varlen_hex (p, &xlen);
+ p++; /* skip a comma */
+ cond = (char *) xmalloc (2 * xlen + 1);
+ strncpy (cond, p, 2 * xlen);
+ cond[2 * xlen] = '\0';
+ p += 2 * xlen;
+ }
+ else
+ warning (_("Unrecognized char '%c' in tracepoint "
+ "definition, skipping rest"), *p);
+ }
+ utp = get_uploaded_tp (num, addr, utpp);
+ utp->type = type;
+ utp->enabled = enabled;
+ utp->step = step;
+ utp->pass = pass;
+ utp->cond = cond;
+ }
+ else if (piece == 'A')
+ {
+ utp = get_uploaded_tp (num, addr, utpp);
+ VEC_safe_push (char_ptr, utp->actions, xstrdup (p));
+ }
+ else if (piece == 'S')
+ {
+ utp = get_uploaded_tp (num, addr, utpp);
+ VEC_safe_push (char_ptr, utp->step_actions, xstrdup (p));
+ }
+ else if (piece == 'Z')
+ {
+ /* Parse a chunk of source form definition. */
+ utp = get_uploaded_tp (num, addr, utpp);
+ srctype = p;
+ p = strchr (p, ':');
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &start);
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &xlen);
+ p++; /* skip a colon */
+
+ buf = alloca (strlen (line));
+
+ end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
+ buf[end] = '\0';
+
+ if (strncmp (srctype, "at:", strlen ("at:")) == 0)
+ utp->at_string = xstrdup (buf);
+ else if (strncmp (srctype, "cond:", strlen ("cond:")) == 0)
+ utp->cond_string = xstrdup (buf);
+ else if (strncmp (srctype, "cmd:", strlen ("cmd:")) == 0)
+ VEC_safe_push (char_ptr, utp->cmd_strings, xstrdup (buf));
+ }
+ else if (piece == 'V')
+ {
+ utp = get_uploaded_tp (num, addr, utpp);
+
+ parse_tracepoint_status (p, NULL, utp);
+ }
+ else
+ {
+ /* Don't error out, the target might be sending us optional
+ info that we don't care about. */
+ warning (_("Unrecognized tracepoint piece '%c', ignoring"), piece);
+ }
+}
+
+/* Convert a textual description of a trace state variable into an
+ uploaded object. */
+
+void
+parse_tsv_definition (char *line, struct uploaded_tsv **utsvp)
+{
+ char *p, *buf;
+ ULONGEST num, initval, builtin;
+ int end;
+ struct uploaded_tsv *utsv = NULL;
+
+ buf = alloca (strlen (line));
+
+ p = line;
+ p = unpack_varlen_hex (p, &num);
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &initval);
+ p++; /* skip a colon */
+ p = unpack_varlen_hex (p, &builtin);
+ p++; /* skip a colon */
+ end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
+ buf[end] = '\0';
+
+ utsv = get_uploaded_tsv (num, utsvp);
+ utsv->initial_value = initval;
+ utsv->builtin = builtin;
+ utsv->name = xstrdup (buf);
+}
+
+/* Close the trace file and generally clean up. */
+
+static void
+tfile_close (int quitting)
+{
+ 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;
+}
+
+static void
+tfile_files_info (struct target_ops *t)
+{
+ /* (it would be useful to mention the name of the file). */
+ printf_filtered ("Looking at a trace file.\n");
+}
+
+/* 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 ULONGEST
+tfile_get_traceframe_address (off_t tframe_offset)
+{
+ ULONGEST 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,
+ ULONGEST addr1, ULONGEST addr2, int *tpp)
+{
+ short tpnum;
+ int tfnum = 0, found = 0;
+ unsigned int data_size;
+ struct tracepoint *tp;
+ off_t offset, tframe_offset;
+ ULONGEST 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;
+ switch (type)
+ {
+ case tfind_number:
+ if (tfnum == num)
+ found = 1;
+ break;
+ 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 (&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;
+ char *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);
+ if (pc_regno >= 0 && (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 LONGEST
+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)
+{
+ /* We're only doing regular memory for now. */
+ if (object != TARGET_OBJECT_MEMORY)
+ return -1;
+
+ if (readbuf == NULL)
+ error (_("tfile_xfer_partial: trace file is read-only"));
+
+ 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);
+ return amt;
+ }
+
+ /* 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;
+
+ amt = bfd_get_section_contents (exec_bfd, s,
+ readbuf, offset - vma, amt);
+ return amt;
+ }
+ }
+ }
+
+ /* Indicate failure to find the requested memory block. */
+ return -1;
+}
+
+/* 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;
+
+ 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 ()));
+ return 1;
+ }
+ pos += (4 + 8);
+ }
+
+ /* Didn't find anything. */
+ return 0;
+}
+
+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);
+ tfile_read ((gdb_byte *) &mlen, 2);
+
+ r = VEC_safe_push (mem_range_s, info->memory, NULL);
+
+ r->start = maddr;
+ r->length = mlen;
+ break;
+ }
+ case 'V':
+ case 'R':
+ case 'S':
+ {
+ break;
+ }
+ default:
+ warning (_("Unhandled trace block type (%d) '%c ' "
+ "while building trace frame info."),
+ blocktype, blocktype);
+ break;
+ }
+
+ return 0;
+}
+
+static struct traceframe_info *
+tfile_traceframe_info (void)
+{
+ struct traceframe_info *info = XCNEW (struct traceframe_info);
+
+ traceframe_walk_blocks (build_traceframe_info, 0, info);
+ return info;
+}
+
+static void
+init_tfile_ops (void)
+{
+ tfile_ops.to_shortname = "tfile";
+ tfile_ops.to_longname = "Local trace dump file";
+ tfile_ops.to_doc
+ = "Use a trace file as a target. Specify the filename of the trace file.";
+ tfile_ops.to_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;
+}
+