aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog76
-rw-r--r--gdb/NEWS10
-rw-r--r--gdb/ax-gdb.c1
-rw-r--r--gdb/breakpoint.c64
-rw-r--r--gdb/doc/ChangeLog6
-rw-r--r--gdb/doc/gdb.texinfo125
-rw-r--r--gdb/remote.c338
-rw-r--r--gdb/target.c18
-rw-r--r--gdb/target.h30
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.trace/tfile.c116
-rw-r--r--gdb/testsuite/gdb.trace/tfile.exp89
-rw-r--r--gdb/tracepoint.c1240
-rw-r--r--gdb/tracepoint.h80
14 files changed, 1966 insertions, 232 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 0627459..97ef2cc 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,78 @@
+2010-01-15 Stan Shebs <stan@codesourcery.com>
+
+ Add trace file support.
+ * tracepoint.h (enum trace_stop_reason): New enum.
+ (struct trace_status): New struct.
+ (parse_trace_status): Declare.
+ (struct uploaded_tp): Move here from remote.c,
+ add fields for actions.
+ (struct uploaded_tsv): New struct.
+ * tracepoint.c (tfile_ops): New target vector.
+ (trace_fd): New global.
+ (tfile_open): New function.
+ (tfile_close): New function.
+ (tfile_files_info): New function.
+ (tfile_get_trace_status): New function.
+ (tfile_get_traceframe_address): New function.
+ (tfile_trace_find): New function.
+ (tfile_fetch_registers): New function.
+ (tfile_xfer_partial): New function.
+ (tfile_get_trace_state_variable_value): New function.
+ (init_tfile_ops): New function.
+ (_initialize_tracepoint): Call it, add tfile target.
+ (trace_status): New global.
+ (current_trace_status): New function.
+ (trace_running_p): Remove, change all users to get from
+ current_trace_status()->running.
+ (get_trace_status): Remove.
+ (trace_status_command): Call target_get_trace_status directly,
+ report more detail including tracing stop reasons.
+ (trace_find_command): Always allow tfind on a file.
+ (trace_find_pc_command): Ditto.
+ (trace_find_tracepoint_command): Ditto.
+ (trace_find_line_command): Ditto.
+ (trace_find_range_command): Ditto.
+ (trace_find_outside_command): Ditto.
+ (trace_frames_offset, cur_offset): Declare as off_t.
+ (trace_regblock_size): Rename from reg_size, update users.
+ (parse_trace_status): New function.
+ (tfile_interp_line): New function.
+ (disconnect_or_stop_tracing): Ensure current trace
+ status before asking what to do.
+ (stop_reason_names): New global.
+ (trace_save_command): New command.
+ (get_uploaded_tp): Move here from remote.c.
+ (find_matching_tracepoint): Ditto.
+ (merge_uploaded_tracepoints): New function.
+ (parse_trace_status): Use stop_reason_names.
+ (_initialize_tracepoint): Define tsave command.
+ * target.h (target_ops): New fields to_save_trace_data,
+ to_upload_tracepoints, to_upload_trace_state_variables,
+ to_get_raw_trace_data, change to_get_trace_status
+ to take a pointer to a status struct.
+ (target_save_trace_data): New macro.
+ (target_upload_tracepoints): New macro.
+ (target_upload_trace_state_variables): New macro.
+ (target_get_raw_trace_data): New macro.
+ * target.c (update_current_target): Add new methods, change
+ signature of to_get_trace_status.
+ * remote.c (hex2bin): Make globally visible.
+ (bin2hex): Ditto.
+ (remote_download_trace_state_variable): Download name also.
+ (remote_get_trace_status): Update parameter, use
+ parse_trace_status.
+ (remote_save_trace_data): New function.
+ (remote_upload_tracepoints): New function.
+ (remote_upload_trace_state_variables): New function.
+ (remote_get_raw_trace_data): New function.
+ (remote_start_remote): Use them.
+ (_initialize_remote_ops): Add operations.
+ * ax-gdb.c: Include breakpoint.h.
+ * breakpoint.c (create_tracepoint_from_upload): Use
+ break_command_really, return tracepoint, warn about unimplemented
+ parts.
+ * NEWS: Mention trace file addition.
+
2010-01-15 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix compilation warning on gcc-3.4.
@@ -164,7 +239,6 @@
(SUBDIR_PYTHON_SRCS): Likewise.
(py-lazy-string.o): New rule.
-
2010-01-13 Doug Evans <dje@google.com>
* mi/mi-main.c (list_available_thread_groups): Avoid "may be used
diff --git a/gdb/NEWS b/gdb/NEWS
index 17d64fb..4b88ee7 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -61,6 +61,16 @@ Renesas RX rx
tell the target agent whether to continue running a trace if the
connection is lost unexpectedly.
+ ** Trace files
+
+ GDB now has the ability to save the trace buffer into a file, and
+ then use that file as a target, similarly to you can do with
+ corefiles. You can select trace frames, print data that was
+ collected in them, and use tstatus to display the state of the
+ tracing run at the moment that it was saved. To create a trace
+ file, use "tsave <filename>", and to use it, do "target tfile
+ <name>".
+
* Changed commands
disassemble
diff --git a/gdb/ax-gdb.c b/gdb/ax-gdb.c
index 3483a41..5776bb0 100644
--- a/gdb/ax-gdb.c
+++ b/gdb/ax-gdb.c
@@ -37,6 +37,7 @@
#include "user-regs.h"
#include "language.h"
#include "dictionary.h"
+#include "breakpoint.h"
#include "tracepoint.h"
/* To make sense of this file, you should read doc/agentexpr.texi.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 0dc8474..058995f 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9847,27 +9847,61 @@ ftrace_command (char *arg, int from_tty)
set_tracepoint_count (breakpoint_count);
}
-extern void create_tracepoint_from_upload (int num, enum bptype type,
- ULONGEST addr);
-
-void
-create_tracepoint_from_upload (int num, enum bptype type, ULONGEST addr)
+/* Given information about a tracepoint as recorded on a target (which
+ can be either a live system or a trace file), attempt to create an
+ equivalent GDB tracepoint. This is not a reliable process, since
+ the target does not necessarily have all the information used when
+ the tracepoint was originally defined. */
+
+struct breakpoint *
+create_tracepoint_from_upload (struct uploaded_tp *utp)
{
char buf[100];
struct breakpoint *tp;
+
+ /* In the absence of a source location, fall back to raw address. */
+ sprintf (buf, "*%s", paddress (get_current_arch(), utp->addr));
- sprintf (buf, "*0x%s", paddress (get_current_arch (), addr));
- if (type == bp_fast_tracepoint)
- ftrace_command (buf, 0);
- else
- trace_command (buf, 0);
+ break_command_really (get_current_arch (),
+ buf,
+ NULL, 0, 1 /* parse arg */,
+ 0 /* tempflag */,
+ (utp->type == bp_fast_tracepoint) /* hardwareflag */,
+ 1 /* traceflag */,
+ 0 /* Ignore count */,
+ pending_break_support,
+ NULL,
+ 0 /* from_tty */,
+ utp->enabled /* enabled */);
+ set_tracepoint_count (breakpoint_count);
+
+ tp = get_tracepoint (tracepoint_count);
- /* Record that this tracepoint is numbered differently on host and
- target. */
- tp = get_tracepoint (tracepoint_count);
- tp->number_on_target = num;
-}
+ if (utp->pass > 0)
+ {
+ sprintf (buf, "%d %d", utp->pass, tp->number);
+
+ trace_pass_command (buf, 0);
+ }
+
+ if (utp->cond)
+ {
+ printf_filtered ("Want to restore a condition\n");
+ }
+
+ if (utp->numactions > 0)
+ {
+ printf_filtered ("Want to restore action list\n");
+ }
+ if (utp->num_step_actions > 0)
+ {
+ printf_filtered ("Want to restore action list\n");
+ }
+
+ return tp;
+ }
+
/* Print information on tracepoint number TPNUM_EXP, or all if
omitted. */
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index 8b5d162..b07d720 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,9 @@
+2010-01-15 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.texinfo (Trace Files): New section.
+ (Tracepoint Packets): Document QTSave and qTBuffer.
+ (Trace File Format): New appendix.
+
2010-01-13 Phil Muldoon <pmuldoon@redhat.com>
* gdb.texinfo (Values From Inferior): Document lazy_string value
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 253e251..7cf1bb4 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -174,6 +174,7 @@ software in general. We will miss him.
@value{GDBN}
* Operating System Information:: Getting additional information from
the operating system
+* Trace File Format:: GDB trace file format
* Copying:: GNU General Public License says
how you can copy and share GDB
* GNU Free Documentation License:: The license for this documentation
@@ -9292,12 +9293,17 @@ support tracepoints as of this writing. The format of the remote
packets used to implement tracepoints are described in @ref{Tracepoint
Packets}.
+It is also possible to get trace data from a file, in a manner reminiscent
+of corefiles; you specify the filename, and use @code{tfind} to search
+through the file. @xref{Trace Files}, for more details.
+
This chapter describes the tracepoint commands and features.
@menu
* Set Tracepoints::
* Analyze Collected Data::
* Tracepoint Variables::
+* Trace Files::
@end menu
@node Set Tracepoints
@@ -10081,6 +10087,41 @@ which are managed by the target.
> end
@end smallexample
+@node Trace Files
+@section Using Trace Files
+@cindex trace files
+
+In some situations, the target running a trace experiment may no
+longer be available; perhaps it crashed, or the hardware was needed
+for a different activity. To handle these cases, you can arrange to
+dump the trace data into a file, and later use that file as a source
+of trace data, via the @code{target tfile} command.
+
+@table @code
+
+@kindex tsave
+@item tsave [ -r ] @var{filename}
+Save the trace data to @var{filename}. By default, this command
+assumes that @var{filename} refers to the host filesystem, so if
+necessary @value{GDBN} will copy raw trace data up from the target and
+then save it. If the target supports it, you can also supply the
+optional argument @code{-r} (``remote'') to direct the target to save
+the data directly into @var{filename} in its own filesystem, which may be
+more efficient if the trace buffer is very large. (Note, however, that
+@code{target tfile} can only read from files accessible to the host.)
+
+@kindex target tfile
+@kindex tfile
+@item target tfile @var{filename}
+Use the file named @var{filename} as a source of trace data. Commands
+that examine data work as they do with a live target, but it is not
+possible to run any new trace experiments. @code{tstatus} will report
+the state of the trace run at the moment the data was saved, as well
+as the current trace frame you are examining. @var{filename} must be
+on a filesystem accessible to the host.
+
+@end table
+
@node Overlays
@chapter Debugging Programs That Use Overlays
@cindex overlays
@@ -29964,10 +30005,12 @@ encoded). @value{GDBN} will continue to supply the values of symbols
(if available), until the target ceases to request them.
@end table
+@item qTBuffer
@item QTDisconnected
@itemx QTDP
@itemx QTDV
-@itemx QTfP
+@itemx qTfP
+@itemx qTfV
@itemx QTFrame
@xref{Tracepoint Packets}.
@@ -29996,7 +30039,9 @@ the command by a @samp{,}, not a @samp{:}, contrary to the naming
conventions above. Please don't use this packet as a model for new
packets.)
-@item QTsP
+@item QTSave
+@item qTsP
+@item qTsV
@itemx QTStart
@itemx QTStop
@itemx QTinit
@@ -30455,6 +30500,29 @@ of data, and multiple @code{qTsP} to get additional pieces. Replies
to these packets generally take the form of the @code{QTDP} packets
that define tracepoints. (FIXME add detailed syntax)
+@item qTfV
+@itemx qTsV
+These packets request data about trace state variables that are on the
+target. @value{GDBN} sends @code{qTfV} to get the first vari of data,
+and multiple @code{qTsV} to get additional variables. Replies to
+these packets follow the syntax of the @code{QTDV} packets that define
+trace state variables.
+
+@item QTSave:@var{filename}
+This packet directs the target to save trace data to the file name
+@var{filename} in the target's filesystem. @var{filename} is encoded
+as a hex string; the interpretation of the file name (relative vs
+absolute, wild cards, etc) is up to the target.
+
+@item qTBuffer:@var{offset},@var{len}
+Return up to @var{len} bytes of the current contents of trace buffer,
+starting at @var{offset}. The trace buffer is treated as if it were
+a contiguous collection of traceframes, as per the trace file format.
+The reply consists as many hex-encoded bytes as the target can deliver
+in a packet; it is not an error to return fewer than were asked for.
+A reply consisting of just @code{l} indicates that no bytes are
+available.
+
@end table
@node Host I/O Packets
@@ -32146,6 +32214,59 @@ element is interpreted as human-readable auxilliary information.
@include agentexpr.texi
+@node Trace File Format
+@appendix Trace File Format
+@cindex trace file format
+
+The trace file comes in three parts: a header, a textual description
+section, and a trace frame section with binary data.
+
+The header has the form @code{\x7fTRACE0\n}. The first byte is
+@code{0x7f} so as to indicate that the file contains binary data,
+while the @code{0} is a version number that may have different values
+in the future.
+
+The description section consists of multiple lines of @sc{ascii} text
+separated by newline characters (@code{0xa}). The lines may include a
+variety of optional descriptive or context-setting information, such
+as tracepoint definitions or register set size. @value{GDBN} will
+ignore any line that it does not recognize. An empty line marks the end
+of this section.
+
+@c FIXME add some specific types of data
+
+The trace frame section consists of a number of consecutive frames.
+Each frame begins with a two-byte tracepoint number, followed by a
+four-byte size giving the amount of data in the frame. The data in
+the frame consists of a number of blocks, each introduced by a
+character indicating its type (at least register, memory, and trace
+state variable). The data in this section is raw binary, not a
+hexadecimal or other encoding; its endianness matches the target's
+endianness.
+
+@c FIXME bi-arch may require endianness/arch info in description section
+
+@table @code
+@item R @var{bytes}
+Register block. The number and ordering of bytes matches that of a
+@code{g} packet in the remote protocol. Note that these are the
+actual bytes, in target order and @value{GDBN} register order, not a
+hexadecimal encoding.
+
+@item M @var{address} @var{length} @var{bytes}...
+Memory block. This is a contiguous block of memory, at the 8-byte
+address @var{address}, with a 2-byte length @var{length}, followed by
+@var{length} bytes.
+
+@item V @var{number} @var{value}
+Trace state variable block. This records the 8-byte signed value
+@var{value} of trace state variable numbered @var{number}.
+
+@end table
+
+Future enhancements of the trace file format may include additional types
+of blocks.
+
@node Target Descriptions
@appendix Target Descriptions
@cindex target descriptions
diff --git a/gdb/remote.c b/gdb/remote.c
index f492082..2cee1b0 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -189,9 +189,9 @@ static void record_currthread (ptid_t currthread);
static int fromhex (int a);
-static int hex2bin (const char *hex, gdb_byte *bin, int count);
+extern int hex2bin (const char *hex, gdb_byte *bin, int count);
-static int bin2hex (const gdb_byte *bin, char *hex, int count);
+extern int bin2hex (const gdb_byte *bin, char *hex, int count);
static int putpkt_binary (char *buf, int cnt);
@@ -215,8 +215,12 @@ static char *write_ptid (char *buf, const char *endbuf, ptid_t ptid);
static ptid_t read_ptid (char *buf, char **obuf);
struct remote_state;
-static void remote_get_tracing_state (struct remote_state *);
+static int remote_get_trace_status (struct trace_status *ts);
+static int remote_upload_tracepoints (struct uploaded_tp **utpp);
+
+static int remote_upload_trace_state_variables (struct uploaded_tsv **utsvp);
+
static void remote_query_supported (void);
static void remote_check_symbols (struct objfile *objfile);
@@ -3158,7 +3162,23 @@ remote_start_remote (struct ui_out *uiout, void *opaque)
previously; find out where things are at. */
if (rs->disconnected_tracing)
{
- remote_get_tracing_state (rs);
+ struct uploaded_tp *uploaded_tps = NULL;
+ struct uploaded_tsv *uploaded_tsvs = NULL;
+
+ remote_get_trace_status (current_trace_status ());
+ if (current_trace_status ()->running)
+ printf_filtered (_("Trace is already running on the target.\n"));
+
+ /* Get trace state variables first, they may be checked when
+ parsing uploaded commands. */
+
+ remote_upload_trace_state_variables (&uploaded_tsvs);
+
+ merge_uploaded_trace_state_variables (&uploaded_tsvs);
+
+ remote_upload_tracepoints (&uploaded_tps);
+
+ merge_uploaded_tracepoints (&uploaded_tps);
}
/* If breakpoints are global, insert them now. */
@@ -3948,7 +3968,7 @@ fromhex (int a)
error (_("Reply contains invalid hex digit %d"), a);
}
-static int
+int
hex2bin (const char *hex, gdb_byte *bin, int count)
{
int i;
@@ -3978,7 +3998,7 @@ tohex (int nib)
return 'a' + nib - 10;
}
-static int
+int
bin2hex (const gdb_byte *bin, char *hex, int count)
{
int i;
@@ -8034,7 +8054,7 @@ remote_rcmd (char *command,
{
char *buf;
- /* XXX - see also tracepoint.c:remote_get_noisy_reply(). */
+ /* XXX - see also remote_get_noisy_reply(). */
rs->buf[0] = '\0';
getpkt (&rs->buf, &rs->buf_size, 0);
buf = rs->buf;
@@ -9342,9 +9362,15 @@ static void
remote_download_trace_state_variable (struct trace_state_variable *tsv)
{
struct remote_state *rs = get_remote_state ();
+ char *p;
- sprintf (rs->buf, "QTDV:%x:%s",
- tsv->number, phex ((ULONGEST) tsv->initial_value, 8));
+ sprintf (rs->buf, "QTDV:%x:%s:%x:",
+ tsv->number, phex ((ULONGEST) tsv->initial_value, 8), tsv->builtin);
+ p = rs->buf + strlen (rs->buf);
+ if ((p - rs->buf) + strlen (tsv->name) * 2 >= get_remote_packet_size ())
+ error (_("Trace state variable name too long for tsv definition packet"));
+ p += 2 * bin2hex ((gdb_byte *) (tsv->name), p, 0);
+ *p++ = '\0';
putpkt (rs->buf);
remote_get_noisy_reply (&target_buf, &target_buf_size);
}
@@ -9395,16 +9421,39 @@ remote_trace_start ()
}
static int
-remote_get_trace_status (int *stop_reason)
+remote_get_trace_status (struct trace_status *ts)
{
+ char *p, *p1, *p_temp;
+ ULONGEST val;
+ /* FIXME we need to get register block size some other way */
+ extern int trace_regblock_size;
+ trace_regblock_size = get_remote_arch_state ()->sizeof_g_packet;
+
putpkt ("qTStatus");
- remote_get_noisy_reply (&target_buf, &target_buf_size);
+ getpkt (&target_buf, &target_buf_size, 0);
+ /* FIXME should handle more variety of replies */
+
+ p = target_buf;
+
+ /* If the remote target doesn't do tracing, flag it. */
+ if (*p == '\0')
+ return -1;
- if (target_buf[0] != 'T' ||
- (target_buf[1] != '0' && target_buf[1] != '1'))
+ /* We're working with a live target. */
+ ts->from_file = 0;
+
+ /* Set some defaults. */
+ ts->running_known = 0;
+ ts->stop_reason = trace_stop_reason_unknown;
+ ts->traceframe_count = -1;
+ ts->buffer_free = 0;
+
+ if (*p++ != 'T')
error (_("Bogus trace status reply from target: %s"), target_buf);
- return (target_buf[1] == '1');
+ parse_trace_status (p, ts);
+
+ return ts->running;
}
static void
@@ -9434,16 +9483,16 @@ remote_trace_find (enum trace_find_type type, int num,
sprintf (p, "%x", num);
break;
case tfind_pc:
- sprintf (p, "pc:%s", paddress (target_gdbarch, addr1));
+ sprintf (p, "pc:%s", phex_nz (addr1, 0));
break;
case tfind_tp:
sprintf (p, "tdp:%x", num);
break;
case tfind_range:
- sprintf (p, "range:%s:%s", paddress (target_gdbarch, addr1), paddress (target_gdbarch, addr2));
+ sprintf (p, "range:%s:%s", phex_nz (addr1, 0), phex_nz (addr2, 0));
break;
case tfind_outside:
- sprintf (p, "outside:%s:%s", paddress (target_gdbarch, addr1), paddress (target_gdbarch, addr2));
+ sprintf (p, "outside:%s:%s", phex_nz (addr1, 0), phex_nz (addr2, 0));
break;
default:
error ("Unknown trace find type %d", type);
@@ -9499,6 +9548,67 @@ remote_get_trace_state_variable_value (int tsvnum, LONGEST *val)
return 0;
}
+static int
+remote_save_trace_data (char *filename)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *p, *reply;
+
+ p = rs->buf;
+ strcpy (p, "QTSave:");
+ p += strlen (p);
+ if ((p - rs->buf) + strlen (filename) * 2 >= get_remote_packet_size ())
+ error (_("Remote file name too long for trace save packet"));
+ p += 2 * bin2hex ((gdb_byte *) filename, p, 0);
+ *p++ = '\0';
+ putpkt (rs->buf);
+ remote_get_noisy_reply (&target_buf, &target_buf_size);
+ return 0;
+}
+
+/* This is basically a memory transfer, but needs to be its own packet
+ because we don't know how the target actually organizes its trace
+ memory, plus we want to be able to ask for as much as possible, but
+ not be unhappy if we don't get as much as we ask for. */
+
+static LONGEST
+remote_get_raw_trace_data (gdb_byte *buf, ULONGEST offset, LONGEST len)
+{
+ struct remote_state *rs = get_remote_state ();
+ char *reply;
+ char *p;
+ int rslt;
+
+ p = rs->buf;
+ strcpy (p, "qTBuffer:");
+ p += strlen (p);
+ p += hexnumstr (p, offset);
+ *p++ = ',';
+ p += hexnumstr (p, len);
+ *p++ = '\0';
+
+ putpkt (rs->buf);
+ reply = remote_get_noisy_reply (&target_buf, &target_buf_size);
+ if (reply && *reply)
+ {
+ /* 'l' by itself means we're at the end of the buffer and
+ there is nothing more to get. */
+ if (*reply == 'l')
+ return 0;
+
+ /* Convert the reply into binary. Limit the number of bytes to
+ convert according to our passed-in buffer size, rather than
+ what was returned in the packet; if the target is
+ unexpectedly generous and gives us a bigger reply than we
+ asked for, we don't want to crash. */
+ rslt = hex2bin (target_buf, buf, len);
+ return rslt;
+ }
+
+ /* Something went wrong, flag as an error. */
+ return -1;
+}
+
static void
remote_set_disconnected_tracing (int val)
{
@@ -9592,6 +9702,10 @@ Specify the serial device it is connected to\n\
remote_ops.to_trace_stop = remote_trace_stop;
remote_ops.to_trace_find = remote_trace_find;
remote_ops.to_get_trace_state_variable_value = remote_get_trace_state_variable_value;
+ remote_ops.to_save_trace_data = remote_save_trace_data;
+ remote_ops.to_upload_tracepoints = remote_upload_tracepoints;
+ remote_ops.to_upload_trace_state_variables = remote_upload_trace_state_variables;
+ remote_ops.to_get_raw_trace_data = remote_get_raw_trace_data;
remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing;
remote_ops.to_core_of_thread = remote_core_of_thread;
}
@@ -9744,181 +9858,51 @@ remote_new_objfile (struct objfile *objfile)
remote_check_symbols (objfile);
}
-/* Struct to collect random info about tracepoints on the target. */
-
-struct uploaded_tp {
- int number;
- enum bptype type;
- ULONGEST addr;
- int enabled;
- int step;
- int pass;
- int orig_size;
- char *cond;
- int cond_len;
- struct uploaded_tp *next;
-};
-
-struct uploaded_tp *uploaded_tps;
-
-struct uploaded_tp *
-get_uploaded_tp (int num)
-{
- struct uploaded_tp *utp;
-
- for (utp = uploaded_tps; utp; utp = utp->next)
- if (utp->number == num)
- return utp;
- utp = (struct uploaded_tp *) xmalloc (sizeof (struct uploaded_tp));
- utp->number = num;
- utp->next = uploaded_tps;
- uploaded_tps = utp;
- return utp;
-}
-
-/* Look for an existing tracepoint that seems similar enough to the
- uploaded one. Enablement isn't checked, because the user can
- toggle that freely, and may have done so in anticipation of the
- next trace run. */
-
-struct breakpoint *
-find_matching_tracepoint (struct uploaded_tp *utp)
+/* Pull all the tracepoints defined on the target and create local
+ data structures representing them. We don't want to create real
+ tracepoints yet, we don't want to mess up the user's existing
+ collection. */
+
+static int
+remote_upload_tracepoints (struct uploaded_tp **utpp)
{
- VEC(breakpoint_p) *tp_vec = all_tracepoints ();
- int ix;
- struct breakpoint *t;
+ struct remote_state *rs = get_remote_state ();
+ char *p;
- for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+ /* Ask for a first packet of tracepoint definition. */
+ putpkt ("qTfP");
+ getpkt (&rs->buf, &rs->buf_size, 0);
+ p = rs->buf;
+ while (*p && *p != 'l')
{
- if (t->type == utp->type
- && (t->loc && t->loc->address == utp->addr)
- && t->step_count == utp->step
- && t->pass_count == utp->pass
- /* FIXME also test conditionals and actions */
- )
- return t;
+ parse_tracepoint_definition (p, utpp);
+ /* Ask for another packet of tracepoint definition. */
+ putpkt ("qTsP");
+ getpkt (&rs->buf, &rs->buf_size, 0);
+ p = rs->buf;
}
- return NULL;
+ return 0;
}
-/* Find out everything we can about the trace run that was already
- happening on the target. This includes both running/stopped, and
- the tracepoints that were in use. */
-
-static void
-remote_get_tracing_state (struct remote_state *rs)
+static int
+remote_upload_trace_state_variables (struct uploaded_tsv **utsvp)
{
+ struct remote_state *rs = get_remote_state ();
char *p;
- ULONGEST num, addr, step, pass, orig_size, xlen;
- int enabled, i;
- enum bptype type;
- char *cond;
- struct uploaded_tp *utp;
- struct breakpoint *t;
- extern void get_trace_status ();
-
- get_trace_status ();
- if (trace_running_p)
- printf_filtered (_("Trace is running on the target.\n"));
- putpkt ("qTfP");
+ /* Ask for a first packet of variable definition. */
+ putpkt ("qTfV");
getpkt (&rs->buf, &rs->buf_size, 0);
p = rs->buf;
- while (*p != '\0')
+ while (*p && *p != 'l')
{
- if (*p == 'T')
- {
- p++;
- p = unpack_varlen_hex (p, &num);
- p++;
- p = unpack_varlen_hex (p, &addr);
- p++;
- enabled = (*p++ == 'E');
- p++;
- p = unpack_varlen_hex (p, &step);
- p++;
- p = unpack_varlen_hex (p, &pass);
- p++;
- type = bp_tracepoint;
- cond = NULL;
- while (*p)
- {
- if (*p == 'F')
- {
- type = bp_fast_tracepoint;
- p++;
- p = unpack_varlen_hex (p, &orig_size);
- }
- else if (*p == 'X')
- {
- p++;
- p = unpack_varlen_hex (p, &xlen);
- p++; /* skip the comma */
- cond = (char *) xmalloc (xlen);
- hex2bin (p, cond, xlen);
- p += 2 * xlen;
- }
- else
- /* Silently skip over anything else. */
- p++;
- }
- utp = get_uploaded_tp (num);
- utp->type = type;
- utp->addr = addr;
- utp->enabled = enabled;
- utp->step = step;
- utp->pass = pass;
- utp->cond = cond;
- utp->cond_len = xlen;
- }
- else if (*p == 'A')
- {
- p++;
- p = unpack_varlen_hex (p, &num);
- p++;
- p = unpack_varlen_hex (p, &addr);
- p++;
- utp = get_uploaded_tp (num);
- /* FIXME save the action */
- }
- else if (*p == 'S')
- {
- p++;
- p = unpack_varlen_hex (p, &num);
- p++;
- p = unpack_varlen_hex (p, &addr);
- p++;
- utp = get_uploaded_tp (num);
- /* FIXME save the action */
- }
- else if (*p == 'l')
- {
- /* No more tracepoint info, get out of the loop. */
- break;
- }
- putpkt ("qTsP");
+ parse_tsv_definition (p, utsvp);
+ /* Ask for another packet of variable definition. */
+ putpkt ("qTsV");
getpkt (&rs->buf, &rs->buf_size, 0);
p = rs->buf;
}
- /* Got all the tracepoint info, now look for matches among what we
- already have in GDB. */
- for (utp = uploaded_tps; utp; utp = utp->next)
- {
- t = find_matching_tracepoint (utp);
- if (t)
- {
- printf_filtered (_("Assuming tracepoint %d is same as target's tracepoint %d.\n"),
- t->number, utp->number);
- t->number_on_target = utp->number;
- }
- else
- {
- extern void create_tracepoint_from_upload (int num, ULONGEST addr);
- create_tracepoint_from_upload (utp->number, utp->addr);
- }
- }
- /* FIXME free all the space */
- uploaded_tps = NULL;
+ return 0;
}
void
diff --git a/gdb/target.c b/gdb/target.c
index edf8697..1f0a426 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -694,6 +694,10 @@ update_current_target (void)
INHERIT (to_trace_stop, t);
INHERIT (to_trace_find, t);
INHERIT (to_get_trace_state_variable_value, t);
+ INHERIT (to_save_trace_data, t);
+ INHERIT (to_upload_tracepoints, t);
+ INHERIT (to_upload_trace_state_variables, t);
+ INHERIT (to_get_raw_trace_data, t);
INHERIT (to_set_disconnected_tracing, t);
INHERIT (to_magic, t);
/* Do not inherit to_memory_map. */
@@ -859,7 +863,7 @@ update_current_target (void)
(void (*) (void))
tcomplain);
de_fault (to_get_trace_status,
- (int (*) (int *))
+ (int (*) (struct trace_status *))
return_minus_one);
de_fault (to_trace_stop,
(void (*) (void))
@@ -870,6 +874,18 @@ update_current_target (void)
de_fault (to_get_trace_state_variable_value,
(int (*) (int, LONGEST *))
return_zero);
+ de_fault (to_save_trace_data,
+ (int (*) (char *))
+ tcomplain);
+ de_fault (to_upload_tracepoints,
+ (int (*) (struct uploaded_tp **))
+ return_zero);
+ de_fault (to_upload_trace_state_variables,
+ (int (*) (struct uploaded_tsv **))
+ return_zero);
+ de_fault (to_get_raw_trace_data,
+ (LONGEST (*) (gdb_byte *, ULONGEST, LONGEST))
+ tcomplain);
de_fault (to_set_disconnected_tracing,
(void (*) (int))
tcomplain);
diff --git a/gdb/target.h b/gdb/target.h
index a020bf7..7103ab2 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -32,6 +32,9 @@ struct bp_target_info;
struct regcache;
struct target_section_table;
struct trace_state_variable;
+struct trace_status;
+struct uploaded_tsv;
+struct uploaded_tp;
/* This include file defines the interface between the main part
of the debugger, and the part which is target-specific, or
@@ -632,7 +635,7 @@ struct target_ops
void (*to_trace_start) (void);
/* Get the current status of a tracing run. */
- int (*to_get_trace_status) (int *stop_reason);
+ int (*to_get_trace_status) (struct trace_status *ts);
/* Stop a trace run. */
void (*to_trace_stop) (void);
@@ -649,6 +652,15 @@ struct target_ops
location pointed to by VAL, else returning 0. */
int (*to_get_trace_state_variable_value) (int tsv, LONGEST *val);
+ int (*to_save_trace_data) (char *filename);
+
+ int (*to_upload_tracepoints) (struct uploaded_tp **utpp);
+
+ int (*to_upload_trace_state_variables) (struct uploaded_tsv **utsvp);
+
+ LONGEST (*to_get_raw_trace_data) (gdb_byte *buf,
+ ULONGEST offset, LONGEST len);
+
/* Set the target's tracing behavior in response to unexpected
disconnection - set VAL to 1 to keep tracing, 0 to stop. */
void (*to_set_disconnected_tracing) (int val);
@@ -1319,8 +1331,8 @@ extern int target_search_memory (CORE_ADDR start_addr,
#define target_trace_set_readonly_regions() \
(*current_target.to_trace_set_readonly_regions) ()
-#define target_get_trace_status(stop_reason) \
- (*current_target.to_get_trace_status) (stop_reason)
+#define target_get_trace_status(ts) \
+ (*current_target.to_get_trace_status) (ts)
#define target_trace_stop() \
(*current_target.to_trace_stop) ()
@@ -1331,6 +1343,18 @@ extern int target_search_memory (CORE_ADDR start_addr,
#define target_get_trace_state_variable_value(tsv,val) \
(*current_target.to_get_trace_state_variable_value) ((tsv), (val))
+#define target_save_trace_data(filename) \
+ (*current_target.to_save_trace_data) (filename)
+
+#define target_upload_tracepoints(utpp) \
+ (*current_target.to_upload_tracepoints) (utpp)
+
+#define target_upload_trace_state_variables(utsvp) \
+ (*current_target.to_upload_trace_state_variables) (utsvp)
+
+#define target_get_raw_trace_data(buf,offset,len) \
+ (*current_target.to_get_raw_trace_data) ((buf), (offset), (len))
+
#define target_set_disconnected_tracing(val) \
(*current_target.to_set_disconnected_tracing) (val)
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index e81c4e9..79b5759 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2010-01-15 Stan Shebs <stan@codesourcery.com>
+
+ * gdb.trace/tfile.c: New file.
+ * gdb.trace/tfile.exp: New file.
+
2010-01-14 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.base/pie-support.exp, gdb.base/pie-support.c: Remove.
diff --git a/gdb/testsuite/gdb.trace/tfile.c b/gdb/testsuite/gdb.trace/tfile.c
new file mode 100644
index 0000000..9ffc371
--- /dev/null
+++ b/gdb/testsuite/gdb.trace/tfile.c
@@ -0,0 +1,116 @@
+/* This program does two things; it generates valid trace files, and
+ it can also be traced so as to test trace file creation from
+ GDB. */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+char spbuf[200];
+
+char trbuf[1000];
+char *trptr;
+char *tfsizeptr;
+
+int testglob = 31415;
+
+int
+start_trace_file (char *filename)
+{
+ int fd;
+
+ fd = open (filename, O_WRONLY|O_CREAT|O_APPEND,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+
+ if (fd < 0)
+ return fd;
+
+ /* 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. */
+ write (fd, "\x7fTRACE0\n", 8);
+
+ return fd;
+}
+
+void
+finish_trace_file (int fd)
+{
+ close (fd);
+}
+
+void
+write_basic_trace_file ()
+{
+ int fd;
+
+ fd = start_trace_file ("basic.tf");
+
+ /* The next part of the file consists of newline-separated lines
+ defining status, tracepoints, etc. The section is terminated by
+ an empty line. */
+
+ /* Dump the size of the R (register) blocks in traceframes. */
+ snprintf (spbuf, sizeof spbuf, "R %x\n", 500 /* FIXME get from arch */);
+ write (fd, spbuf, strlen (spbuf));
+
+ /* Dump trace status, in the general form of the qTstatus reply. */
+ snprintf (spbuf, sizeof spbuf, "status 0;tstop:0;tframes:1;tcreated:1;tfree:100;tsize:1000\n");
+ write (fd, spbuf, strlen (spbuf));
+
+ /* Dump tracepoint definitions, in syntax similar to that used
+ for reconnection uploads. */
+ snprintf (spbuf, sizeof spbuf, "tp T1:%lx:E:0:0\n",
+ (long) &write_basic_trace_file);
+ write (fd, spbuf, strlen (spbuf));
+ /* (Note that we would only need actions defined if we wanted to
+ test tdump.) */
+
+ /* Empty line marks the end of the definition section. */
+ write (fd, "\n", 1);
+
+ /* Make up a simulated trace buffer. */
+ /* (Encapsulate better if we're going to do lots of this.) */
+ trptr = trbuf;
+ *((short *) trptr) = 1;
+ trptr += sizeof (short);
+ tfsizeptr = trptr;
+ trptr += sizeof (int);
+ *((char *) trptr) = 'M';
+ trptr += 1;
+ *((long long *) trptr) = (long) &testglob;
+ trptr += sizeof (long long);
+ *((short *) trptr) = sizeof (testglob);
+ trptr += sizeof (short);
+ *((int *) trptr) = testglob;
+ trptr += sizeof (testglob);
+ /* Go back and patch in the frame size. */
+ *((int *) tfsizeptr) = trptr - tfsizeptr - sizeof (int);
+
+ /* Write end of tracebuffer marker. */
+ *((short *) trptr) = 0;
+ trptr += sizeof (short);
+ *((int *) trptr) = 0;
+ trptr += sizeof (int);
+
+ write (fd, trbuf, trptr - trbuf);
+
+ finish_trace_file (fd);
+}
+
+void
+done_making_trace_files (void)
+{
+}
+
+int
+main (int argc, char **argv, char **envp)
+{
+ write_basic_trace_file ();
+
+ done_making_trace_files ();
+
+ return 0;
+}
+
diff --git a/gdb/testsuite/gdb.trace/tfile.exp b/gdb/testsuite/gdb.trace/tfile.exp
new file mode 100644
index 0000000..398b5ef
--- /dev/null
+++ b/gdb/testsuite/gdb.trace/tfile.exp
@@ -0,0 +1,89 @@
+# Copyright 2010 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test of trace file support.
+
+# Note that unlike most of the tracing tests, this can be run on
+# targets lacking tracepoint support; the program tfile.c has the
+# ability to generate synthetic trace files directly, and the tfile
+# target is available to all GDB configs.
+
+load_lib "trace-support.exp";
+
+if [target_info exists gdb,nofileio] {
+ verbose "Skipping tfile.exp because of no fileio capabilities."
+ continue
+}
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+set prms_id 0
+set bug_id 0
+
+gdb_exit
+gdb_start
+set testfile "tfile"
+set srcfile ${testfile}.c
+set binfile $objdir/$subdir/$testfile
+if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \
+ executable {debug nowarnings}] != "" } {
+ untested file.exp
+ return -1
+}
+gdb_reinitialize_dir $srcdir/$subdir
+
+# Make sure we are starting fresh.
+remote_exec build {sh -xc rm\ -f\ basic.tf}
+
+gdb_load $binfile
+
+runto_main
+
+gdb_test "break done_making_trace_files" "" ""
+
+gdb_test "continue" "" ""
+
+# tsave command would be tested here...
+
+gdb_test "continue" "" ""
+
+# Program has presumably exited, now target a trace file it created.
+
+gdb_test "target tfile basic.tf" "Created tracepoint.*" "target tfile"
+
+gdb_test "info trace" ".*tracepoint.*in write_basic_trace_file.*" \
+ "info tracepoints on trace file"
+
+gdb_test "tfind 0" "Found traceframe 0.*" "tfind 0 on trace file"
+
+gdb_test "print testglob" " = 31415" "print testglob on trace file"
+
+gdb_test "tfind" "Target failed to find requested trace frame." \
+ "tfind does not find a second frame in trace file"
+
+gdb_test "tstatus" \
+ "Using a trace file.*
+Trace stopped by a tstop command.*
+Collected 1 trace frames.*
+Trace buffer has 256 bytes free.*
+Looking at trace frame 0, tracepoint .*" \
+ "tstatus on trace file"
+
+
+
+
+
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 0dfe23c..550a880 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -43,6 +43,7 @@
#include "gdbcore.h"
#include "objfiles.h"
#include "filenames.h"
+#include "gdbthread.h"
#include "ax.h"
#include "ax-gdb.h"
@@ -58,6 +59,13 @@
#include <unistd.h>
#endif
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+extern int hex2bin (const char *hex, gdb_byte *bin, int count);
+extern int bin2hex (const gdb_byte *bin, char *hex, int count);
+
extern void stop_tracing ();
/* Maximum length of an agent aexpression.
@@ -170,8 +178,29 @@ static struct cleanup *make_cleanup_free_actions (struct breakpoint *t);
extern void send_disconnected_tracing_value (int value);
+static void free_uploaded_tps (struct uploaded_tp **utpp);
+static void free_uploaded_tsvs (struct uploaded_tsv **utsvp);
+
+
extern void _initialize_tracepoint (void);
+static struct trace_status trace_status;
+
+char *stop_reason_names[] = {
+ "tunknown",
+ "tnotrun",
+ "tstop",
+ "tfull",
+ "tdisconnected",
+ "tpasscount"
+};
+
+struct trace_status *
+current_trace_status ()
+{
+ return &trace_status;
+}
+
/* Set traceframe number to NUM. */
static void
set_traceframe_num (int num)
@@ -405,7 +434,7 @@ tvariables_info (char *args, int from_tty)
print_spaces_filtered (11 - strlen (plongest (tsv->initial_value)), gdb_stdout);
if (tsv->value_known)
printf_filtered (" %s", plongest (tsv->value));
- else if (trace_running_p || traceframe_number >= 0)
+ else if (current_trace_status ()->running || traceframe_number >= 0)
/* The value is/was defined, but we don't have it. */
printf_filtered (_(" <unknown>"));
else
@@ -1513,11 +1542,10 @@ trace_start_command (char *args, int from_tty)
if (!any_downloaded)
error ("No tracepoints downloaded, not starting trace");
- /* Init any trace state variables that start with nonzero values. */
+ /* Send down all the trace state variables too. */
for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
{
- if (tsv->initial_value != 0)
- target_download_trace_state_variable (tsv);
+ target_download_trace_state_variable (tsv);
}
/* Tell target to treat text-like sections as transparent. */
@@ -1530,7 +1558,7 @@ trace_start_command (char *args, int from_tty)
set_traceframe_num (-1);
set_tracepoint_num (-1);
set_traceframe_context (NULL);
- trace_running_p = 1;
+ current_trace_status()->running = 1;
}
/* tstop command */
@@ -1544,31 +1572,35 @@ void
stop_tracing ()
{
target_trace_stop ();
- trace_running_p = 0;
-}
-
-unsigned long trace_running_p;
-
-int
-get_trace_status ()
-{
- int status = target_get_trace_status (NULL);
-
- /* exported for use by the GUI */
- trace_running_p = (status > 0);
-
- return status;
+ /* should change in response to reply? */
+ current_trace_status ()->running = 0;
}
/* tstatus command */
static void
trace_status_command (char *args, int from_tty)
{
- int status = get_trace_status ();
+ struct trace_status *ts = current_trace_status ();
+ int status;
- if (status < 0)
- printf_filtered (_("Trace can not be run on the target.\n"));
- else if (trace_running_p)
+ status = target_get_trace_status (ts);
+
+ if (status == -1)
+ {
+ if (ts->from_file)
+ printf_filtered (_("Using a trace file.\n"));
+ else
+ {
+ printf_filtered (_("Trace can not be run on this target.\n"));
+ return;
+ }
+ }
+
+ if (!ts->running_known)
+ {
+ printf_filtered (_("Run/stop status is unknown.\n"));
+ }
+ else if (ts->running)
{
printf_filtered (_("Trace is running on the target.\n"));
if (disconnected_tracing)
@@ -1577,8 +1609,49 @@ trace_status_command (char *args, int from_tty)
printf_filtered (_("Trace will stop if GDB disconnects.\n"));
}
else
- printf_filtered (_("Trace is not running on the target.\n"));
+ {
+ switch (ts->stop_reason)
+ {
+ case trace_never_run:
+ printf_filtered (_("No trace has been run on the target.\n"));
+ break;
+ case tstop_command:
+ printf_filtered (_("Trace stopped by a tstop command.\n"));
+ break;
+ case trace_buffer_full:
+ printf_filtered (_("Trace stopped because the buffer was full.\n"));
+ break;
+ case trace_disconnected:
+ printf_filtered (_("Trace stopped because of disconnection.\n"));
+ break;
+ case tracepoint_passcount:
+ /* FIXME account for number on target */
+ printf_filtered (_("Trace stopped by tracepoint %d.\n"),
+ ts->stopping_tracepoint);
+ break;
+ case trace_stop_reason_unknown:
+ printf_filtered (_("Trace stopped for an unknown reason.\n"));
+ break;
+ default:
+ printf_filtered (_("Trace stopped for some other reason (%d).\n"),
+ ts->stop_reason);
+ break;
+ }
+ }
+
+ if (ts->traceframe_count >= 0)
+ {
+ printf_filtered (_("Collected %d trace frames.\n"),
+ ts->traceframe_count);
+ }
+
+ if (ts->buffer_free)
+ {
+ printf_filtered (_("Trace buffer has %d bytes free.\n"),
+ ts->buffer_free);
+ }
+ /* Now report on what we're doing with tfind. */
if (traceframe_number >= 0)
printf_filtered (_("Looking at trace frame %d, tracepoint %d.\n"),
traceframe_number, tracepoint_number);
@@ -1589,7 +1662,14 @@ trace_status_command (char *args, int from_tty)
void
disconnect_or_stop_tracing (int from_tty)
{
- if (trace_running_p && from_tty)
+ /* It can happen that the target that was tracing went away on its
+ own, and we didn't notice. Get a status update, and if the
+ current target doesn't even do tracing, then assume it's not
+ running anymore. */
+ if (target_get_trace_status (current_trace_status ()) < 0)
+ current_trace_status ()->running = 0;
+
+ if (current_trace_status ()->running && from_tty)
{
int cont = query (_("Trace is running. Continue tracing after detach? "));
/* Note that we send the query result without affecting the
@@ -1674,7 +1754,11 @@ finish_tfind_command (enum trace_find_type type, int num,
else
set_traceframe_context (get_current_frame ());
- if (from_tty)
+ /* If we're in nonstop mode and getting out of looking at trace
+ frames, there won't be any current frame to go back to and
+ display. */
+ if (from_tty
+ && (has_stack_frames () || traceframe_number >= 0))
{
enum print_what print_what;
@@ -1715,7 +1799,7 @@ trace_find_command (char *args, int from_tty)
{ /* this should only be called with a numeric argument */
int frameno = -1;
- if (trace_running_p)
+ if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
if (args == 0 || *args == 0)
@@ -1774,7 +1858,7 @@ trace_find_pc_command (char *args, int from_tty)
CORE_ADDR pc;
char tmp[40];
- if (trace_running_p)
+ if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
if (args == 0 || *args == 0)
@@ -1792,7 +1876,7 @@ trace_find_tracepoint_command (char *args, int from_tty)
int tdp;
struct breakpoint *tp;
- if (trace_running_p)
+ if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
if (args == 0 || *args == 0)
@@ -1832,7 +1916,7 @@ trace_find_line_command (char *args, int from_tty)
struct cleanup *old_chain;
char startpc_str[40], endpc_str[40];
- if (trace_running_p)
+ if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
if (args == 0 || *args == 0)
@@ -1914,7 +1998,7 @@ trace_find_range_command (char *args, int from_tty)
char start_str[40], stop_str[40];
char *tmp;
- if (trace_running_p)
+ if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
if (args == 0 || *args == 0)
@@ -1948,7 +2032,7 @@ trace_find_outside_command (char *args, int from_tty)
char start_str[40], stop_str[40];
char *tmp;
- if (trace_running_p)
+ if (current_trace_status ()->running && !current_trace_status ()->from_file)
error ("May not look at trace frames while trace is running.");
if (args == 0 || *args == 0)
@@ -2239,6 +2323,168 @@ trace_dump_command (char *args, int from_tty)
discard_cleanups (old_cleanups);
}
+extern int trace_regblock_size;
+
+static void
+trace_save_command (char *args, int from_tty)
+{
+ char **argv;
+ char *filename = NULL, *pathname;
+ int target_does_save = 0;
+ struct cleanup *cleanup;
+ struct trace_status *ts = current_trace_status ();
+ int err, status;
+ FILE *fp;
+ struct uploaded_tp *uploaded_tps = NULL, *utp;
+ struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
+ int a;
+ LONGEST gotten = 0;
+ ULONGEST offset = 0;
+#define MAX_TRACE_UPLOAD 2000
+ gdb_byte buf[MAX_TRACE_UPLOAD];
+
+ if (args == NULL)
+ error_no_arg (_("file in which to save trace data"));
+
+ argv = gdb_buildargv (args);
+ make_cleanup_freeargv (argv);
+
+ for (; *argv; ++argv)
+ {
+ if (strcmp (*argv, "-r") == 0)
+ target_does_save = 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 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)
+ {
+ err = target_save_trace_data (filename);
+ if (err < 0)
+ error (_("Target failed to save trace data to '%s'."),
+ filename);
+ return;
+ }
+
+ /* 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);
+
+ pathname = tilde_expand (args);
+ cleanup = make_cleanup (xfree, pathname);
+
+ fp = fopen (pathname, "w");
+ if (!fp)
+ error (_("Unable to open file '%s' for saving trace data (%s)"),
+ args, safe_strerror (errno));
+ make_cleanup_fclose (fp);
+
+ /* 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. */
+ fwrite ("\x7fTRACE0\n", 8, 1, fp);
+
+ /* Write descriptive info. */
+
+ /* Write out the size of a register block. */
+ fprintf (fp, "R %x\n", trace_regblock_size);
+
+ /* Write out status of the tracing run (aka "tstatus" info). */
+ fprintf (fp, "status %c;%s:%x;tframes:%x;tfree:%x\n",
+ (ts->running ? '1' : '0'),
+ stop_reason_names[ts->stop_reason], ts->stopping_tracepoint,
+ ts->traceframe_count, (unsigned int) ts->buffer_free);
+
+ /* 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. */
+
+ /* Get trace state variables first, they may be checked when parsing
+ uploaded commands. */
+
+ target_upload_trace_state_variables (&uploaded_tsvs);
+
+ for (utsv = uploaded_tsvs; utsv; utsv = utsv->next)
+ {
+ char *buf = "";
+
+ if (utsv->name)
+ {
+ buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
+ bin2hex ((gdb_byte *) (utsv->name), buf, 0);
+ }
+
+ fprintf (fp, "tsv %x:%s:%x:%s\n",
+ utsv->number, phex_nz (utsv->initial_value, 8),
+ utsv->builtin, buf);
+
+ if (utsv->name)
+ xfree (buf);
+ }
+
+ free_uploaded_tsvs (&uploaded_tsvs);
+
+ target_upload_tracepoints (&uploaded_tps);
+
+ for (utp = uploaded_tps; utp; utp = utp->next)
+ {
+ fprintf (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 (fp, ":F%x", utp->orig_size);
+ if (utp->cond)
+ fprintf (fp, ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
+ utp->cond);
+ fprintf (fp, "\n");
+ for (a = 0; a < utp->numactions; ++a)
+ fprintf (fp, "tp A%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ utp->actions[a]);
+ for (a = 0; a < utp->num_step_actions; ++a)
+ fprintf (fp, "tp S%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ utp->step_actions[a]);
+ }
+
+ free_uploaded_tps (&uploaded_tps);
+
+ /* Mark the end of the definition section. */
+ fprintf (fp, "\n");
+
+ /* Get and write the trace data proper. We ask for big blocks, in
+ the hopes of efficiency, but will take less if the target has
+ packet size limitations or some such. */
+ while (1)
+ {
+ 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;
+ fwrite (buf, gotten, 1, fp);
+ offset += gotten;
+ }
+
+ /* Mark the end of trace data. */
+ fwrite (&gotten, 4, 1, fp);
+
+ do_cleanups (cleanup);
+ if (from_tty)
+ printf_filtered (_("Trace data saved to file '%s'.\n"), args);
+}
+
/* Tell the target what to do with an ongoing tracing run if GDB
disconnects for some reason. */
@@ -2286,6 +2532,927 @@ get_traceframe_number (void)
return 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->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;
+ }
+}
+
+/* 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. */
+
+struct breakpoint *
+find_matching_tracepoint (struct uploaded_tp *utp)
+{
+ VEC(breakpoint_p) *tp_vec = all_tracepoints ();
+ int ix;
+ struct breakpoint *t;
+ struct bp_location *loc;
+
+ for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+ {
+ if (t->type == utp->type
+ && t->step_count == utp->step
+ && t->pass_count == utp->pass
+ /* FIXME also test conditionals and actions */
+ )
+ {
+ /* Scan the locations for an address match. */
+ for (loc = t->loc; loc; loc = loc->next)
+ {
+ if (loc->address == utp->addr)
+ return t;
+ }
+ }
+ }
+ 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;
+ struct breakpoint *t;
+
+ /* Look for GDB tracepoints that match up with our uploaded versions. */
+ for (utp = *uploaded_tps; utp; utp = utp->next)
+ {
+ t = find_matching_tracepoint (utp);
+ if (t)
+ printf_filtered (_("Assuming tracepoint %d is same as target's tracepoint %d at %s.\n"),
+ t->number, utp->number, paddress (get_current_arch (), utp->addr));
+ else
+ {
+ t = create_tracepoint_from_upload (utp);
+ if (t)
+ printf_filtered (_("Created tracepoint %d for target's tracepoint %d at %s.\n"),
+ t->number, utp->number, paddress (get_current_arch (), utp->addr));
+ 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. */
+
+struct trace_state_variable *
+find_matching_tsv (struct uploaded_tsv *utsv)
+{
+ if (!utsv->name)
+ return NULL;
+
+ return find_trace_state_variable (utsv->name);
+}
+
+struct trace_state_variable *
+create_tsv_from_upload (struct uploaded_tsv *utsv)
+{
+ const char *namebase;
+ char buf[20];
+ int try_num = 0;
+ struct trace_state_variable *tsv;
+
+ if (utsv->name)
+ {
+ namebase = utsv->name;
+ sprintf (buf, "%s", namebase);
+ }
+ else
+ {
+ namebase = "__tsv";
+ sprintf (buf, "%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))
+ sprintf (buf, "%s_%d", namebase, try_num++);
+
+ /* We have an available name, create the variable. */
+ tsv = create_trace_state_variable (xstrdup (buf));
+ tsv->initial_value = utsv->initial_value;
+ tsv->builtin = utsv->builtin;
+
+ 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)
+ printf_filtered (_("Assuming trace state variable $%s is same as target's variable %d.\n"),
+ tsv->name, utsv->number);
+ else
+ {
+ tsv = create_tsv_from_upload (utsv);
+ printf_filtered (_("Created trace state variable $%s for target's variable %d.\n"),
+ tsv->name, utsv->number);
+ }
+ /* 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 */
+
+struct target_ops tfile_ops;
+
+/* Fill in tfile_ops with its defined operations and properties. */
+
+#define TRACE_HEADER_SIZE 8
+
+int trace_fd = -1;
+off_t trace_frames_offset;
+off_t cur_offset;
+int cur_data_size;
+int trace_regblock_size;
+
+static void tfile_interp_line (char *line,
+ struct uploaded_tp **utpp,
+ struct uploaded_tsv **utsvp);
+
+static void
+tfile_open (char *filename, int from_tty)
+{
+ 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);
+
+ push_target (&tfile_ops);
+ discard_cleanups (old_chain);
+
+ trace_fd = scratch_chan;
+
+ bytes = 0;
+ /* Read the file header and test for validity. */
+ read (trace_fd, &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."));
+
+ 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;
+
+ /* Read through a section of newline-terminated lines that
+ define things like tracepoints. */
+ i = 0;
+ while (1)
+ {
+ read (trace_fd, &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"));
+ }
+
+ /* Add the file's tracepoints and variables into the current mix. */
+
+ merge_uploaded_tracepoints (&uploaded_tps);
+
+ merge_uploaded_trace_state_variables (&uploaded_tsvs);
+
+ /* Record the starting offset of the binary trace data. */
+ trace_frames_offset = bytes;
+
+ /* If we don't have a blocksize, we can't interpret the
+ traceframes. */
+ if (trace_regblock_size == 0)
+ error (_("No register block size recorded in trace file"));
+ if (ts->traceframe_count <= 0)
+ {
+ warning ("No traceframes present in this file.");
+ return;
+ }
+
+#define TFILE_PID (1)
+ inferior_appeared (current_inferior (), TFILE_PID);
+ inferior_ptid = pid_to_ptid (TFILE_PID);
+ add_thread_silent (inferior_ptid);
+
+ post_create_inferior (&tfile_ops, from_tty);
+
+#if 0
+ /* FIXME this will get defined in MI patch submission */
+ tfind_1 (tfind_number, 0, 0, 0, 0);
+#endif
+}
+
+/* Interpret the given line from the definitions part of the trace
+ 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. */
+
+extern char *unpack_varlen_hex (char *buff, ULONGEST *result);
+
+void
+parse_trace_status (char *line, struct trace_status *ts)
+{
+ char *p = line, *p1, *p_temp;
+ ULONGEST val;
+
+ ts->running_known = 1;
+ ts->running = (*p++ == '1');
+ ts->stop_reason = trace_stop_reason_unknown;
+ while (*p++)
+ {
+ p1 = strchr (p, ':');
+ if (p1 == NULL)
+ error (_("Malformed trace status, at %s\n\
+Status line: '%s'\n"), p, line);
+ 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)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->stop_reason = tstop_command;
+ }
+ if (strncmp (p, "tframes", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->traceframe_count = val;
+ }
+ if (strncmp (p, "tfree", p1 - p) == 0)
+ {
+ p = unpack_varlen_hex (++p1, &val);
+ ts->buffer_free = val;
+ }
+ else
+ {
+ /* Silently skip unknown optional info. */
+ p_temp = strchr (p1 + 1, ';');
+ if (p_temp)
+ p = p_temp;
+ else
+ /* Must be at the end. */
+ break;
+ }
+ }
+}
+
+/* Given a line of text defining a tracepoint or tracepoint action, 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;
+ int enabled, i;
+ enum bptype type;
+ char *cond;
+ 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 == '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);
+ utp->actions[utp->numactions++] = xstrdup (p);
+ }
+ else if (piece == 'S')
+ {
+ utp = get_uploaded_tp (num, addr, utpp);
+ utp->step_actions[utp->num_step_actions++] = xstrdup (p);
+ }
+ else
+ {
+ error ("Invalid tracepoint 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;
+}
+
+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;
+}
+
+/* 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 breakpoint *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);
+ read (trace_fd, &tpnum, 2);
+ tp = get_tracepoint_by_number_on_target (tpnum);
+ if (tp && tp->loc)
+ addr = tp->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;
+ int data_size;
+ struct breakpoint *tp;
+ off_t offset, tframe_offset;
+ ULONGEST tfaddr;
+
+ lseek (trace_fd, trace_frames_offset, SEEK_SET);
+ offset = trace_frames_offset;
+ while (1)
+ {
+ tframe_offset = offset;
+ read (trace_fd, &tpnum, 2);
+ offset += 2;
+ if (tpnum == 0)
+ break;
+ read (trace_fd, &data_size, 4);
+ 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)
+ {
+ printf_filtered ("Found traceframe %d.\n", tfnum);
+ 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;
+}
+
+/* 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);
+ char block_type;
+ int i, pos, offset, regn, regsize;
+ unsigned short mlen;
+ 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);
+
+ lseek (trace_fd, cur_offset, SEEK_SET);
+ pos = 0;
+ while (pos < cur_data_size)
+ {
+ read (trace_fd, &block_type, 1);
+ ++pos;
+ switch (block_type)
+ {
+ case 'R':
+ read (trace_fd, 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_valid_p (regcache, regn))
+ {
+ 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;
+ case 'M':
+ lseek (trace_fd, 8, SEEK_CUR);
+ read (trace_fd, &mlen, 2);
+ lseek (trace_fd, mlen, SEEK_CUR);
+ pos += (8 + 2 + mlen);
+ break;
+ case 'V':
+ lseek (trace_fd, 4 + 8, SEEK_CUR);
+ pos += (4 + 8);
+ break;
+ default:
+ error ("Unknown block type '%c' (0x%x) in trace frame",
+ block_type, block_type);
+ break;
+ }
+ }
+}
+
+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)
+{
+ char block_type;
+ int pos;
+ ULONGEST maddr;
+ unsigned short mlen;
+
+ /* We're only doing regular memory for now. */
+ if (object != TARGET_OBJECT_MEMORY)
+ return -1;
+
+ if (readbuf == NULL)
+ error ("tfile_xfer_partial: trace file is read-only");
+
+ lseek (trace_fd, cur_offset, SEEK_SET);
+ pos = 0;
+ while (pos < cur_data_size)
+ {
+ read (trace_fd, &block_type, 1);
+ ++pos;
+ switch (block_type)
+ {
+ case 'R':
+ lseek (trace_fd, trace_regblock_size, SEEK_CUR);
+ pos += trace_regblock_size;
+ break;
+ case 'M':
+ read (trace_fd, &maddr, 8);
+ read (trace_fd, &mlen, 2);
+ if (maddr <= offset && (offset + len) <= (maddr + mlen))
+ {
+ read (trace_fd, readbuf, mlen);
+ return mlen;
+ }
+ lseek (trace_fd, mlen, SEEK_CUR);
+ pos += (8 + 2 + mlen);
+ break;
+ case 'V':
+ lseek (trace_fd, 4 + 8, SEEK_CUR);
+ pos += (4 + 8);
+ break;
+ default:
+ error ("Unknown block type '%c' (0x%x) in traceframe",
+ block_type, block_type);
+ break;
+ }
+ }
+ /* 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)
+{
+ char block_type;
+ int pos, vnum;
+ unsigned short mlen;
+
+ lseek (trace_fd, cur_offset, SEEK_SET);
+ pos = 0;
+ while (pos < cur_data_size)
+ {
+ read (trace_fd, &block_type, 1);
+ ++pos;
+ switch (block_type)
+ {
+ case 'R':
+ lseek (trace_fd, trace_regblock_size, SEEK_CUR);
+ pos += trace_regblock_size;
+ break;
+ case 'M':
+ lseek (trace_fd, 8, SEEK_CUR);
+ read (trace_fd, &mlen, 2);
+ lseek (trace_fd, mlen, SEEK_CUR);
+ pos += (8 + 2 + mlen);
+ break;
+ case 'V':
+ read (trace_fd, &vnum, 4);
+ if (tsvnum == vnum)
+ {
+ read (trace_fd, val, 8);
+ return 1;
+ }
+ lseek (trace_fd, 8, SEEK_CUR);
+ pos += (4 + 8);
+ break;
+ default:
+ error ("Unknown block type '%c' (0x%x) in traceframe",
+ block_type, block_type);
+ break;
+ }
+ }
+ /* Didn't find anything. */
+ return 0;
+}
+
+static int
+tfile_has_memory (struct target_ops *ops)
+{
+ return 1;
+}
+
+static int
+tfile_has_stack (struct target_ops *ops)
+{
+ return 1;
+}
+
+static int
+tfile_has_registers (struct target_ops *ops)
+{
+ return 1;
+}
+
+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_trace_find = tfile_trace_find;
+ tfile_ops.to_get_trace_state_variable_value = tfile_get_trace_state_variable_value;
+ /* core_stratum might seem more logical, but GDB doesn't like having
+ more than one core_stratum vector. */
+ tfile_ops.to_stratum = process_stratum;
+ 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_magic = OPS_MAGIC;
+}
+
/* module initialization */
void
_initialize_tracepoint (void)
@@ -2332,6 +3499,11 @@ _initialize_tracepoint (void)
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 '-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\
@@ -2462,4 +3634,8 @@ trace data collected in the meantime."),
NULL,
&setlist,
&showlist);
+
+ init_tfile_ops ();
+
+ add_target (&tfile_ops);
}
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index 5d2862a..b50266a 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -61,12 +61,79 @@ struct trace_state_variable
/* The value of a variable is a 64-bit signed integer. */
LONGEST value;
+
+ /* This is true for variables that are predefined and built into
+ the target. */
+ int builtin;
+ };
+
+/* The trace status encompasses various info about the general state
+ of the tracing run. */
+
+enum trace_stop_reason
+ {
+ trace_stop_reason_unknown,
+ trace_never_run,
+ tstop_command,
+ trace_buffer_full,
+ trace_disconnected,
+ tracepoint_passcount
};
-extern unsigned long trace_running_p;
+struct trace_status
+{
+ /* This is true if the status is coming from a file rather
+ than a live target. */
+ int from_file;
+
+ /* This is true if the value of the running field is known. */
+ int running_known;
+
+ int running;
+
+ enum trace_stop_reason stop_reason;
+
+ int stopping_tracepoint;
+
+ int traceframe_count;
+
+ size_t buffer_size;
+
+ size_t buffer_free;
+};
+
+struct trace_status *current_trace_status (void);
extern char *default_collect;
+/* Struct to collect random info about tracepoints on the target. */
+
+struct uploaded_tp {
+ int number;
+ enum bptype type;
+ ULONGEST addr;
+ int enabled;
+ int step;
+ int pass;
+ int orig_size;
+ char *cond;
+ int numactions;
+ char *actions[100];
+ int num_step_actions;
+ char *step_actions[100];
+ struct uploaded_tp *next;
+};
+
+/* Struct recording info about trace state variables on the target. */
+
+struct uploaded_tsv {
+ const char *name;
+ int number;
+ LONGEST initial_value;
+ int builtin;
+ struct uploaded_tsv *next;
+};
+
/* A hook used to notify the UI of tracepoint operations. */
extern void (*deprecated_trace_find_hook) (char *arg, int from_tty);
@@ -81,4 +148,15 @@ extern void while_stepping_pseudocommand (char *args, int from_tty);
extern struct trace_state_variable *find_trace_state_variable (const char *name);
+extern void parse_trace_status (char *line, struct trace_status *ts);
+
+extern void parse_tracepoint_definition (char *line, struct uploaded_tp **utpp);
+extern void parse_tsv_definition (char *line, struct uploaded_tsv **utsvp);
+
+extern struct uploaded_tp *get_uploaded_tp (int num, ULONGEST addr,
+ struct uploaded_tp **utpp);
+extern struct breakpoint *create_tracepoint_from_upload (struct uploaded_tp *utp);
+extern void merge_uploaded_tracepoints (struct uploaded_tp **utpp);
+extern void merge_uploaded_trace_state_variables (struct uploaded_tsv **utsvp);
+
#endif /* TRACEPOINT_H */