diff options
Diffstat (limited to 'gdb/tracefile.c')
-rw-r--r-- | gdb/tracefile.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/gdb/tracefile.c b/gdb/tracefile.c new file mode 100644 index 0000000..9945ae5 --- /dev/null +++ b/gdb/tracefile.c @@ -0,0 +1,389 @@ +/* Trace file support in GDB. + + Copyright (C) 1997-2014 Free Software Foundation, Inc. + + This file is part of GDB. + + 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/>. */ + +#include "defs.h" +#include "tracefile.h" +#include "ctf.h" + +/* Helper macros. */ + +#define TRACE_WRITE_R_BLOCK(writer, buf, size) \ + writer->ops->frame_ops->write_r_block ((writer), (buf), (size)) +#define TRACE_WRITE_M_BLOCK_HEADER(writer, addr, size) \ + writer->ops->frame_ops->write_m_block_header ((writer), (addr), \ + (size)) +#define TRACE_WRITE_M_BLOCK_MEMORY(writer, buf, size) \ + writer->ops->frame_ops->write_m_block_memory ((writer), (buf), \ + (size)) +#define TRACE_WRITE_V_BLOCK(writer, num, val) \ + writer->ops->frame_ops->write_v_block ((writer), (num), (val)) + +/* Free trace file writer. */ + +static void +trace_file_writer_xfree (void *arg) +{ + struct trace_file_writer *writer = arg; + + writer->ops->dtor (writer); + xfree (writer); +} + +/* Save tracepoint data to file named FILENAME through WRITER. WRITER + determines the trace file format. If TARGET_DOES_SAVE is non-zero, + the save is performed on the target, otherwise GDB obtains all trace + data and saves it locally. */ + +static void +trace_save (const char *filename, struct trace_file_writer *writer, + int target_does_save) +{ + struct trace_status *ts = current_trace_status (); + int status; + struct uploaded_tp *uploaded_tps = NULL, *utp; + struct uploaded_tsv *uploaded_tsvs = NULL, *utsv; + + ULONGEST offset = 0; +#define MAX_TRACE_UPLOAD 2000 + gdb_byte buf[MAX_TRACE_UPLOAD]; + int written; + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); + + /* If the target is to save the data to a file on its own, then just + send the command and be done with it. */ + if (target_does_save) + { + if (!writer->ops->target_save (writer, filename)) + 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); + + writer->ops->start (writer, filename); + + writer->ops->write_header (writer); + + /* Write descriptive info. */ + + /* Write out the size of a register block. */ + writer->ops->write_regblock_type (writer, trace_regblock_size); + + /* Write out status of the tracing run (aka "tstatus" info). */ + writer->ops->write_status (writer, ts); + + /* 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) + writer->ops->write_uploaded_tsv (writer, utsv); + + free_uploaded_tsvs (&uploaded_tsvs); + + target_upload_tracepoints (&uploaded_tps); + + for (utp = uploaded_tps; utp; utp = utp->next) + target_get_tracepoint_status (NULL, utp); + + for (utp = uploaded_tps; utp; utp = utp->next) + writer->ops->write_uploaded_tp (writer, utp); + + free_uploaded_tps (&uploaded_tps); + + /* Mark the end of the definition section. */ + writer->ops->write_definition_end (writer); + + /* Get and write the trace data proper. */ + while (1) + { + LONGEST gotten = 0; + + /* The writer supports writing the contents of trace buffer + directly to trace file. Don't parse the contents of trace + buffer. */ + if (writer->ops->write_trace_buffer != NULL) + { + /* We ask for big blocks, in the hopes of efficiency, but + will take less if the target has packet size limitations + or some such. */ + gotten = target_get_raw_trace_data (buf, offset, + MAX_TRACE_UPLOAD); + if (gotten < 0) + error (_("Failure to get requested trace buffer data")); + /* No more data is forthcoming, we're done. */ + if (gotten == 0) + break; + + writer->ops->write_trace_buffer (writer, buf, gotten); + + offset += gotten; + } + else + { + uint16_t tp_num; + uint32_t tf_size; + /* Parse the trace buffers according to how data are stored + in trace buffer in GDBserver. */ + + gotten = target_get_raw_trace_data (buf, offset, 6); + + if (gotten == 0) + break; + + /* Read the first six bytes in, which is the tracepoint + number and trace frame size. */ + tp_num = (uint16_t) + extract_unsigned_integer (&buf[0], 2, byte_order); + + tf_size = (uint32_t) + extract_unsigned_integer (&buf[2], 4, byte_order); + + writer->ops->frame_ops->start (writer, tp_num); + gotten = 6; + + if (tf_size > 0) + { + unsigned int block; + + offset += 6; + + for (block = 0; block < tf_size; ) + { + gdb_byte block_type; + + /* We'll fetch one block each time, in order to + handle the extremely large 'M' block. We first + fetch one byte to get the type of the block. */ + gotten = target_get_raw_trace_data (buf, offset, 1); + if (gotten < 1) + error (_("Failure to get requested trace buffer data")); + + gotten = 1; + block += 1; + offset += 1; + + block_type = buf[0]; + switch (block_type) + { + case 'R': + gotten + = target_get_raw_trace_data (buf, offset, + trace_regblock_size); + if (gotten < trace_regblock_size) + error (_("Failure to get requested trace" + " buffer data")); + + TRACE_WRITE_R_BLOCK (writer, buf, + trace_regblock_size); + break; + case 'M': + { + unsigned short mlen; + ULONGEST addr; + LONGEST t; + int j; + + t = target_get_raw_trace_data (buf,offset, 10); + if (t < 10) + error (_("Failure to get requested trace" + " buffer data")); + + offset += 10; + block += 10; + + gotten = 0; + addr = (ULONGEST) + extract_unsigned_integer (buf, 8, + byte_order); + mlen = (unsigned short) + extract_unsigned_integer (&buf[8], 2, + byte_order); + + TRACE_WRITE_M_BLOCK_HEADER (writer, addr, + mlen); + + /* The memory contents in 'M' block may be + very large. Fetch the data from the target + and write them into file one by one. */ + for (j = 0; j < mlen; ) + { + unsigned int read_length; + + if (mlen - j > MAX_TRACE_UPLOAD) + read_length = MAX_TRACE_UPLOAD; + else + read_length = mlen - j; + + t = target_get_raw_trace_data (buf, + offset + j, + read_length); + if (t < read_length) + error (_("Failure to get requested" + " trace buffer data")); + + TRACE_WRITE_M_BLOCK_MEMORY (writer, buf, + read_length); + + j += read_length; + gotten += read_length; + } + + break; + } + case 'V': + { + int vnum; + LONGEST val; + + gotten + = target_get_raw_trace_data (buf, offset, + 12); + if (gotten < 12) + error (_("Failure to get requested" + " trace buffer data")); + + vnum = (int) extract_signed_integer (buf, + 4, + byte_order); + val + = extract_signed_integer (&buf[4], 8, + byte_order); + + TRACE_WRITE_V_BLOCK (writer, vnum, val); + } + break; + default: + error (_("Unknown block type '%c' (0x%x) in" + " trace frame"), + block_type, block_type); + } + + block += gotten; + offset += gotten; + } + } + else + offset += gotten; + + writer->ops->frame_ops->end (writer); + } + } + + writer->ops->end (writer); +} + +static void +trace_save_command (char *args, int from_tty) +{ + int target_does_save = 0; + char **argv; + char *filename = NULL; + struct cleanup *back_to; + int generate_ctf = 0; + struct trace_file_writer *writer = NULL; + + if (args == NULL) + error_no_arg (_("file in which to save trace data")); + + argv = gdb_buildargv (args); + back_to = make_cleanup_freeargv (argv); + + for (; *argv; ++argv) + { + if (strcmp (*argv, "-r") == 0) + target_does_save = 1; + if (strcmp (*argv, "-ctf") == 0) + generate_ctf = 1; + else if (**argv == '-') + error (_("unknown option `%s'"), *argv); + else + filename = *argv; + } + + if (!filename) + error_no_arg (_("file in which to save trace data")); + + if (generate_ctf) + writer = ctf_trace_file_writer_new (); + else + writer = tfile_trace_file_writer_new (); + + make_cleanup (trace_file_writer_xfree, writer); + + trace_save (filename, writer, target_does_save); + + if (from_tty) + printf_filtered (_("Trace data saved to %s '%s'.\n"), + generate_ctf ? "directory" : "file", filename); + + do_cleanups (back_to); +} + +/* Save the trace data to file FILENAME of tfile format. */ + +void +trace_save_tfile (const char *filename, int target_does_save) +{ + struct trace_file_writer *writer; + struct cleanup *back_to; + + writer = tfile_trace_file_writer_new (); + back_to = make_cleanup (trace_file_writer_xfree, writer); + trace_save (filename, writer, target_does_save); + do_cleanups (back_to); +} + +/* Save the trace data to dir DIRNAME of ctf format. */ + +void +trace_save_ctf (const char *dirname, int target_does_save) +{ + struct trace_file_writer *writer; + struct cleanup *back_to; + + writer = ctf_trace_file_writer_new (); + back_to = make_cleanup (trace_file_writer_xfree, writer); + + trace_save (dirname, writer, target_does_save); + do_cleanups (back_to); +} + +extern initialize_file_ftype _initialize_tracefile; + +void +_initialize_tracefile (void) +{ + add_com ("tsave", class_trace, trace_save_command, _("\ +Save the trace data to a file.\n\ +Use the '-ctf' option to save the data to CTF format.\n\ +Use the '-r' option to direct the target to save directly to the file,\n\ +using its own filesystem.")); +} |