[PATCH 2/5] Save trace into CTF format

Yao Qi
2013-02-27T02:19:10Z

This patch is to teach GDB to save trace data into CTF format. In
this patch, struct trace_frame_write_ops and struct
trace_file_write_ops are implemented, so that GDB is able to write
trace data in CTF format.

Note that we don't use ui-file to write trace file, because
ui_file_write doesn't handle error well. We can migrate to ui-file
once ui_file_write starts to handle error.

gdb:

2013-02-27 Hui Zhu <hui_zhu@mentor.com>
Yao Qi <yao@codesourcery.com>

* Makefile.in (REMOTE_OBS): Add ctf.o.
(SFILES): Add ctf.c.
(HFILES_NO_SRCDIR): Add ctf.h.
* ctf.c, ctf.h: New files.
* tracepoint.c : Include 'ctf.h'.
(collect_pseudocommand): Remove static.
(trace_save_command): Parse option "-ctf".
Produce different trace file writers per option.
Adjust output message.
(trace_save_tfile, trace_save_ctf): New.
* tracepoint.h (trace_save_tfile, trace_save_ctf): Declare.
* mi/mi-main.c: Include 'ctf.h'.
(mi_cmd_trace_save): Handle option '-ctf'. Call either
trace_save_tfile or trace_save_ctf.
---
gdb/Makefile.in | 6 +-
gdb/ctf.c | 680 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/ctf.h | 24 ++
gdb/mi/mi-main.c | 11 +-
gdb/tracepoint.c | 36 +++-
gdb/tracepoint.h | 2 +
6 files changed, 750 insertions(+), 9 deletions(-)
create mode 100644 gdb/ctf.c
create mode 100644 gdb/ctf.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ed30db5..5be6c77 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -510,7 +510,7 @@ SER_HARDWIRE = @SER_HARDWIRE@
# The `remote' debugging target is supported for most architectures,
# but not all (e.g. 960)
REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o \
- remote-notif.o
+ remote-notif.o ctf.o

# This is remote-sim.o if a simulator is to be linked in.
SIM_OBS = @SIM_OBS@
@@ -759,7 +759,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
regset.c sol-thread.c windows-termcap.c \
common/gdb_vecs.c common/common-utils.c common/xml-utils.c \
common/ptid.c common/buffer.c gdb-dlfcn.c common/agent.c \
- common/format.c
+ common/format.c ctf.c

LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c

@@ -835,7 +835,7 @@ gnulib/import/stddef.in.h gnulib/import/inttypes.in.h inline-frame.h skip.h \
common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \
common/format.h common/host-defs.h utils.h common/queue.h \
common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h \
-gdb_bfd.h sparc-ravenscar-thread.h ppc-ravenscar-thread.h
+gdb_bfd.h sparc-ravenscar-thread.h ppc-ravenscar-thread.h ctf.h

# Header files that already have srcdir in them, or which are in objdir.

diff --git a/gdb/ctf.c b/gdb/ctf.c
new file mode 100644
index 0000000..c4809d8
--- /dev/null
+++ b/gdb/ctf.c
@@ -0,0 +1,680 @@
+/* CTF format support.
+
+ Copyright (C) 2012-2013 Free Software Foundation, Inc.
+ Contributed by Hui Zhu <hui_zhu@mentor.com>
+ Contributed by Yao Qi <yao@codesourcery.com>
+
+ 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 "ctf.h"
+#include "tracepoint.h"
+#include "regcache.h"
+
+#include <ctype.h>
+
+/* GDB saves trace buffers and other information (such as trace
+ status) got from the remote target into Common Trace Format (CTF).
+ The following types of information are expected to save in CTF:
+
+ 1. The length (in bytes) of register cache. Event "register" will
+ be defined in metadata, which includes the length.
+
+ 2. Trace status. Not implemented yet in CTF writer.
+
+ 3. Uploaded trace variables and tracepoints. Not implemented yet
+ in CTF writer.
+
+ 4. Trace frames. Each trace frame is composed by several blocks
+ of different types ('R', 'M', 'V'). One trace frame is saved in
+ one CTF packet and the blocks of this frame are saved as events.
+ 4.1: The trace frame related information (such as the number of
+ tracepoint associated with this frame) is saved in the packet
+ context.
+ 4.2: The block 'R' and 'V' are saved in event "register" and "tsv"
+ respectively. They are fixed in length, so there is only one event
+ type for each of them.
+ 4.3: The block 'M' is variable on length. Each event type is
+ defined for a specific length of block 'M', so there are multiple
+ event types "memory_$LENGTH" in metadata file. $LENGTH is a
+ decimal.
+ 4.4: When iterating over events, babeltrace can't tell iterator
+ goes to a new packet, so we need a marker or anchor to tell GDB
+ that iterator goes into a new packet or frame. We define event
+ "frame". */
+
+#define CTF_MAGIC 0xC1FC1FC1
+#define CTF_SAVE_MAJOR 1
+#define CTF_SAVE_MINOR 8
+
+#define CTF_METADATA_NAME "metadata"
+#define CTF_DATASTREAM_NAME "datastream"
+
+/* Reserved event id. */
+
+#define CTF_EVENT_ID_REGISTER 0
+#define CTF_EVENT_ID_TSV 1
+#define CTF_EVENT_ID_FRAME 2
+#define CTF_EVENT_ID_LAST 3
+
+/* Handler of writing trace of CTF format. */
+
+struct trace_write_handler
+{
+ /* File descriptor of metadata. */
+ FILE *metadata_fd;
+ /* File descriptor of traceframes. */
+ FILE *datastream_fd;
+
+ /* This is the content size of the current packet. */
+ size_t content_size;
+
+ /* This is the start offset of current packet. */
+ long packet_start;
+};
+
+/* Write contents in BUF to file FD. SIZE is the size of BUF. */
+
+static void
+ctf_save_fwrite (FILE *fd, const gdb_byte *buf, size_t size)
+{
+ if (fwrite (buf, size, 1, fd) != 1)
+ error (_("Unable to write file for saving trace data (%s)"),
+ safe_strerror (errno));
+}
+
+static void
+ctf_save_fwrite_format_1 (FILE *fd, const char *format, va_list args)
+{
+ char *linebuffer;
+ struct cleanup *old_cleanups;
+
+ linebuffer = xstrvprintf (format, args);
+ old_cleanups = make_cleanup (xfree, linebuffer);
+ ctf_save_fwrite (fd, linebuffer, strlen (linebuffer));
+ do_cleanups (old_cleanups);
+}
+
+/* Write data in FORMAT to FD. */
+
+static void
+ctf_save_fwrite_format (FILE *fd, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ ctf_save_fwrite_format_1 (fd, format, args);
+ va_end (args);
+}
+
+/* Write BUF of length SIZE to datastream file represented by
+ HANDLER. */
+
+static int
+ctf_save_write (struct trace_write_handler *handler,
+ const gdb_byte *buf, size_t size)
+{
+ ctf_save_fwrite (handler->datastream_fd, buf, size);
+
+ handler->content_size += size;
+
+ return 0;
+}
+
+/* Set datastream file position. Update HANDLER->content_size
+ if WHENCE is SEEK_CUR. */
+
+static int
+ctf_save_fseek (struct trace_write_handler *handler, long offset,
+ int whence)
+{
+ if (fseek (handler->datastream_fd, offset, whence))
+ error (_("Unable to seek file for saving trace data (%s)"),
+ safe_strerror (errno));
+
+ if (whence == SEEK_CUR)
+ handler->content_size += offset;
+
+ return 0;
+}
+
+/* Change the datastream file position to align on ALIGN_SIZE,
+ and Write BUF to datastream file. The size of BUF is SIZE. */
+
+static int
+ctf_save_align_write (struct trace_write_handler *handler,
+ const gdb_byte *buf,
+ size_t size, size_t align_size)
+{
+ long offset
+ = (align_up (handler->content_size, align_size)
+ - handler->content_size);
+
+ if (ctf_save_fseek (handler, offset, SEEK_CUR))
+ return -1;
+
+ if (ctf_save_write (handler, buf, size))
+ return -1;
+
+ return 0;
+}
+
+/* Move to next packet. */
+
+static void
+ctf_save_next_packet (struct trace_write_handler *handler)
+{
+ handler->packet_start += (handler->content_size + 4);
+ ctf_save_fseek (handler, handler->packet_start, SEEK_SET);
+ handler->content_size = 0;
+}
+
+/* Write the CTF metadata header. */
+
+static void
+ctf_save_metadata_header (struct trace_write_handler *handler)
+{
+ const char metadata_fmt[] =
+ "\ntrace {\n"
+ " major = %u;\n"
+ " minor = %u;\n"
+ " byte_order = %s;\n" /* be or le */
+ " packet.header := struct {\n"
+ " uint32_t magic;\n"
+ " };\n"
+ "};\n"
+ "\n"
+ "stream {\n"
+ " packet.context := struct {\n"
+ " uint32_t content_size;\n"
+ " uint32_t packet_size;\n"
+ " uint16_t tpnum;\n"
+ " };\n"
+ " event.header := struct {\n"
+ " uint32_t id;\n"
+ " };\n"
+ "};\n";
+
+ ctf_save_fwrite_format (handler->metadata_fd, "/* CTF %d.%d */\n",
+ CTF_SAVE_MAJOR, CTF_SAVE_MINOR);
+ ctf_save_fwrite_format (handler->metadata_fd,
+ "typealias integer { size = 8; align = 8; "
+ "signed = false; encoding = ascii; }"
+ " := uint8_t;\n");
+ ctf_save_fwrite_format (handler->metadata_fd,
+ "typealias integer { size = 16; align = 16;"
+ "signed = false; } := uint16_t;\n");
+ ctf_save_fwrite_format (handler->metadata_fd,
+ "typealias integer { size = 32; align = 32;"
+ "signed = false; } := uint32_t;\n");
+ ctf_save_fwrite_format (handler->metadata_fd,
+ "typealias integer { size = 64; align = 64;"
+ "signed = false; base = hex;}"
+ " := uint64_t;\n");
+ ctf_save_fwrite_format (handler->metadata_fd, "\n");
+
+ ctf_save_fwrite_format (handler->metadata_fd, metadata_fmt,
+ CTF_SAVE_MAJOR, CTF_SAVE_MINOR,
+ BYTE_ORDER == LITTLE_ENDIAN ? "le" : "be");
+ ctf_save_fwrite_format (handler->metadata_fd, "\n");
+}
+
+/* Cleanup function. */
+
+static void
+ctf_save_cleanup (void *p)
+{
+ struct trace_write_handler *handler = p;
+
+ if (handler->metadata_fd)
+ fclose (handler->metadata_fd);
+
+ if (handler->datastream_fd)
+ fclose (handler->datastream_fd);
+}
+
+/* Data specific to CTF trace writer. */
+
+struct ctf_writer_data
+{
+ struct trace_write_handler tcs;
+
+ /* Event id 0 1 and 2 are reserved:
+ - 0 for register block
+ - 1 for trace variable block.
+ - 2 for frame start marker. */
+ unsigned int event_id;
+
+ /* The value of element of index I is the size of 'M' block, whose
+ event id is I. */
+ unsigned int *m_block_size;
+ /* Length of M_BLOCK_SIZE. */
+ unsigned int length;
+
+ struct cleanup *cleanup;
+};
+
+/* This is the implementation of trace_file_write_ops method
+ can_target_save. */
+
+static int
+ctf_can_target_save (struct trace_file_writer *self,
+ const char *filename)
+{
+ /* Don't support save trace file to CTF format in the target. */
+ return 0;
+}
+
+/* This is the implementation of trace_file_write_ops method
+ start. It creates the directory DIRNAME, metadata and datastream
+ in the directory. */
+
+static void
+ctf_start (struct trace_file_writer *self, const char *dirname)
+{
+ char *file_name;
+ struct cleanup *old_chain;
+ struct ctf_writer_data *data = self->data;
+ int i;
+
+ data->event_id = CTF_EVENT_ID_LAST;
+ data->length = 8;
+ data->m_block_size = xmalloc (data->length * sizeof (int));
+ memset (data->m_block_size, 0, data->length * sizeof (int));
+
+ /* Create DIRNAME. */
+ file_name = alloca (strlen (dirname) + 1
+ + strlen (CTF_DATASTREAM_NAME) + 1);
+ if (mkdir (dirname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+ && errno != EEXIST)
+ error (_("Unable to open directory '%s' for saving trace data (%s)"),
+ dirname, safe_strerror (errno));
+
+ memset (&data->tcs, '\0', sizeof (data->tcs));
+
+ file_name = xstrprintf ("%s/%s", dirname, CTF_METADATA_NAME);
+ old_chain = make_cleanup (xfree, file_name);
+
+ data->tcs.metadata_fd = fopen (file_name, "w");
+ if (!data->tcs.metadata_fd)
+ error (_("Unable to open file '%s' for saving trace data (%s)"),
+ file_name, safe_strerror (errno));
+ do_cleanups (old_chain);
+
+ ctf_save_metadata_header (&data->tcs);
+
+ file_name = xstrprintf ("%s/%s", dirname, CTF_DATASTREAM_NAME);
+ old_chain = make_cleanup (xfree, file_name);
+ data->tcs.datastream_fd = fopen (file_name, "w");
+ if (!data->tcs.datastream_fd)
+ error (_("Unable to open file '%s' for saving trace data (%s)"),
+ file_name, safe_strerror (errno));
+ do_cleanups (old_chain);
+
+ data->cleanup = make_cleanup (ctf_save_cleanup, &data->tcs);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_header. Write the types of events on trace variable and
+ frame. */
+
+static void
+ctf_write_header (struct trace_file_writer *self)
+{
+ struct ctf_writer_data *data = self->data;
+
+ ctf_save_fwrite_format (data->tcs.metadata_fd, "\n");
+ ctf_save_fwrite_format (data->tcs.metadata_fd,
+ "event {\n\tname = \"tsv\";\n\tid = %u;\n"
+ "\tfields := struct { \n"
+ "\t\tuint64_t val;\n"
+ "\t\tuint32_t num;\n"
+ "\t};\n"
+ "};\n", CTF_EVENT_ID_TSV);
+
+ ctf_save_fwrite_format (data->tcs.metadata_fd, "\n");
+ ctf_save_fwrite_format (data->tcs.metadata_fd,
+ "event {\n\tname = \"frame\";\n\tid = %u;\n"
+ "\tfields := struct { \n"
+ "\t};\n"
+ "};\n", CTF_EVENT_ID_FRAME);
+
+ gdb_assert (data->tcs.content_size == 0);
+ gdb_assert (data->tcs.packet_start == 0);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_regblock_type. Write the type of register event in
+ metadata. */
+
+static void
+ctf_write_regblock_type (struct trace_file_writer *self, int size)
+{
+ struct ctf_writer_data *data = self->data;
+
+ ctf_save_fwrite_format (data->tcs.metadata_fd, "\n");
+
+ ctf_save_fwrite_format (data->tcs.metadata_fd,
+ "event {\n\tname = \"register\";\n\tid = %u;\n"
+ "\tfields := struct { \n"
+ "\t\tuint8_t contents[%d];\n"
+ "\t};\n"
+ "};\n",
+ CTF_EVENT_ID_REGISTER, size);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_status. */
+
+static void
+ctf_write_status (struct trace_file_writer *self,
+ struct trace_status *ts)
+{
+ /* It is not supported yet to write trace status into CTF trace
+ data. */
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tsv. */
+
+static void
+ctf_write_uploaded_tsv (struct trace_file_writer *self,
+ struct uploaded_tsv *tsv)
+{
+ /* It is not supported yet to write uploaded trace variables
+ into CTF trace data. */
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tp. */
+
+static void
+ctf_write_uploaded_tp (struct trace_file_writer *self,
+ struct uploaded_tp *tp)
+{
+ /* It is not supported yet to write uploaded tracepoints
+ into CTF trace data. */
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_definition_end. */
+
+static void
+ctf_write_definition_end (struct trace_file_writer *self)
+{
+ /* Nothing to do for CTF. */
+}
+
+/* The minimal file size of data stream. It is required by
+ babeltrace. */
+
+#define CTF_FILE_MIN_SIZE 4096
+
+/* This is the implementation of trace_file_write_ops method
+ end. */
+
+static void
+ctf_end (struct trace_file_writer *self)
+{
+ struct ctf_writer_data *data = self->data;
+
+ gdb_assert (data->tcs.content_size == 0);
+ /* The babeltrace requires or assumes that the datastream file
+ is larger than 4096. If we don't generate enough packets and
+ events, create a fake packet which has zero event, to use up
+ the space. */
+ if (data->tcs.packet_start < CTF_FILE_MIN_SIZE)
+ {
+ uint32_t u32;
+
+ /* magic. */
+ u32 = CTF_MAGIC;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+
+ /* content_size. */
+ u32 = 0;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+
+ /* packet_size. */
+ u32 = 12;
+ if (data->tcs.packet_start + u32 < 4096)
+ u32 = 4096 - data->tcs.packet_start;
+
+ u32 *= TARGET_CHAR_BIT;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+
+ /* tpnum. */
+ u32 = 0;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 2);
+
+ /* Enlarge the file to CTF_FILE_MIN_SIZE is it is still less
+ than that. */
+ if (CTF_FILE_MIN_SIZE
+ > (data->tcs.packet_start + data->tcs.content_size))
+ {
+ gdb_byte b = 0;
+
+ ctf_save_fseek (&data->tcs, CTF_FILE_MIN_SIZE - 1,
+ SEEK_SET);
+ ctf_save_write (&data->tcs, &b, 1);
+ }
+ }
+
+ do_cleanups (data->cleanup);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ start. */
+
+static void
+ctf_write_frame_start (struct trace_file_writer *self, uint16_t tpnum)
+{
+ struct ctf_writer_data *data = self->data;
+ int two = 2;
+ uint32_t u32;
+
+ /* Step 1: Write packet context. */
+ /* magic. */
+ u32 = CTF_MAGIC;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+ /* content_size and packet_size.. We still don't know the value,
+ write it later. */
+ ctf_save_fseek (&data->tcs, 4, SEEK_CUR);
+ ctf_save_fseek (&data->tcs, 4, SEEK_CUR);
+ /* Tracepoint number. */
+ ctf_save_write (&data->tcs, (gdb_byte *) &tpnum, 2);
+
+ /* Step 2: Write event "frame". */
+ /* Event Id. */
+ ctf_save_align_write (&data->tcs, (void *) &two, 4, 4);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ write_r_block. */
+
+static void
+ctf_write_frame_r_block (struct trace_file_writer *self,
+ gdb_byte *buf, int size)
+{
+ struct ctf_writer_data *data = self->data;
+ int zero = 0;
+
+ /* Event Id. */
+ ctf_save_align_write (&data->tcs, (void *) &zero, 4, 4);
+
+ /* array contents. */
+ ctf_save_align_write (&data->tcs, buf, size, 1);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ write_m_block. */
+
+static void
+ctf_write_frame_m_block (struct trace_file_writer *self,
+ ULONGEST addr, gdb_byte *buf,
+ uint16_t length)
+{
+ struct ctf_writer_data *data = self->data;
+ int i;
+ int event_id = 0;
+
+ /* Check we've seen the 'M' block of length LENGTH before. */
+ for (i = 0; i < data->length; i++)
+ {
+ if (length == data->m_block_size[i])
+ {
+ event_id = i + CTF_EVENT_ID_LAST;
+ break;
+ }
+ }
+
+ /* Record it. */
+ if (event_id == 0)
+ {
+ event_id = data->event_id++;
+
+ if (event_id - CTF_EVENT_ID_LAST >= data->length)
+ {
+ data->m_block_size
+ = xrealloc (data->m_block_size,
+ sizeof (int) * data->length * 2);
+ memset (&data->m_block_size[data->length], 0,
+ data->length * sizeof (int));
+ data->length *= 2;
+ }
+
+ data->m_block_size[event_id - CTF_EVENT_ID_LAST] = length;
+
+ /* Save the type of 'M' block into metadata. */
+ ctf_save_fwrite_format (data->tcs.metadata_fd, "\n");
+ ctf_save_fwrite_format (data->tcs.metadata_fd,
+ "event {\n\tname = \"memory_%d\";\n\tid = %u;\n"
+ "\tfields := struct { \n"
+ "\t\tuint64_t address;\n"
+ "\t\tuint16_t length;\n"
+ "\t\tuint8_t contents[%d];\n"
+ "\t};\n"
+ "};\n",
+ length, event_id, length);
+ }
+
+ /* Save 'M' block into datastream. */
+ /* Event Id. */
+ ctf_save_align_write (&data->tcs, (void *) &event_id, 4, 4);
+
+ /* Address. */
+ ctf_save_align_write (&data->tcs, (void *) &addr, 8, 8);
+
+ /* Length. */
+ ctf_save_align_write (&data->tcs, (void *) &length, 2, 2);
+
+ /* Contents. */
+ ctf_save_align_write (&data->tcs, (void *) buf, length, 1);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ write_v_block. */
+
+static void
+ctf_write_frame_v_block (struct trace_file_writer *self,
+ int num, LONGEST val)
+{
+ struct ctf_writer_data *data = self->data;
+ int one = 1;
+
+ /* Event Id. */
+ ctf_save_align_write (&data->tcs, (void *) &one, 4, 4);
+
+ /* val. */
+ ctf_save_align_write (&data->tcs, (void *) &val, 8, 8);
+ /* num. */
+ ctf_save_align_write (&data->tcs, (void *) &num, 4, 4);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ end. */
+
+static void
+ctf_write_frame_end (struct trace_file_writer *self)
+{
+ struct ctf_writer_data *data = self->data;
+ uint32_t u32;
+ uint32_t t;
+
+ /* Write the content size to packet header. */
+ ctf_save_fseek (&data->tcs, data->tcs.packet_start + 4, SEEK_SET);
+ u32 = data->tcs.content_size * TARGET_CHAR_BIT;
+
+ t = data->tcs.content_size;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+
+ /* Write the packet size. */
+ u32 += 4 * TARGET_CHAR_BIT;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+
+ data->tcs.content_size = t;
+
+ /* Write zero at the end of the packet. */
+ ctf_save_fseek (&data->tcs, data->tcs.packet_start + t, SEEK_SET);
+ u32 = 0;
+ ctf_save_write (&data->tcs, (gdb_byte *) &u32, 4);
+ data->tcs.content_size = t;
+
+ ctf_save_next_packet (&data->tcs);
+}
+
+/* Operations to write various types of trace frames into CTF
+ format. */
+
+static struct trace_frame_write_ops ctf_write_frame_ops =
+{
+ ctf_write_frame_start,
+ ctf_write_frame_r_block,
+ ctf_write_frame_m_block,
+ ctf_write_frame_v_block,
+ ctf_write_frame_end,
+};
+
+/* Operations to write trace buffers into CTF format. */
+
+static struct trace_file_write_ops ctf_write_ops =
+{
+ ctf_can_target_save,
+ ctf_start,
+ ctf_write_header,
+ ctf_write_regblock_type,
+ ctf_write_status,
+ ctf_write_uploaded_tsv,
+ ctf_write_uploaded_tp,
+ ctf_write_definition_end,
+ NULL,
+ &ctf_write_frame_ops,
+ ctf_end,
+};
+
+/* Return a trace writer for CTF format. */
+
+struct trace_file_writer *
+ctf_trace_file_writer (void)
+{
+ struct trace_file_writer *writer
+ = xmalloc (sizeof (struct trace_file_writer));
+
+ writer->ops = &ctf_write_ops;
+ writer->data = xmalloc (sizeof (struct ctf_writer_data));
+
+ return writer;
+}
diff --git a/gdb/ctf.h b/gdb/ctf.h
new file mode 100644
index 0000000..d2fccaf
--- /dev/null
+++ b/gdb/ctf.h
@@ -0,0 +1,24 @@
+/* CTF format support.
+
+ Copyright (C) 2012-2013 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/>. */
+
+#ifndef CTF_H
+#define CTF_H
+
+extern struct trace_file_writer *ctf_trace_file_writer (void);
+#endif
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 206b626..a950b34 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -49,6 +49,7 @@
#include "osdata.h"
#include "splay-tree.h"
#include "tracepoint.h"
+#include "ctf.h"
#include "ada-lang.h"
#include "linespec.h"

@@ -2477,16 +2478,19 @@ void
mi_cmd_trace_save (char *command, char **argv, int argc)
{
int target_saves = 0;
+ int generate_ctf = 0;
char *filename;

if (argc != 1 && argc != 2)
- error (_("Usage: -trace-save [-r] filename"));
+ error (_("Usage: -trace-save [-r] [-ctf] filename"));

if (argc == 2)
{
filename = argv[1];
if (strcmp (argv[0], "-r") == 0)
target_saves = 1;
+ if (strcmp (argv[0], "-ctf") == 0)
+ generate_ctf = 1;
else
error (_("Invalid option: %s"), argv[0]);
}
@@ -2495,7 +2499,10 @@ mi_cmd_trace_save (char *command, char **argv, int argc)
filename = argv[0];
}

- trace_save_tfile (filename, target_saves);
+ if (generate_ctf)
+ trace_save_ctf (filename, target_saves);
+ else
+ trace_save_tfile (filename, target_saves);
}

void
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 43bd80f..7434df7 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -53,6 +53,7 @@
#include "exceptions.h"
#include "cli/cli-utils.h"
#include "probe.h"
+#include "ctf.h"

/* readline include files */
#include "readline/readline.h"
@@ -3451,6 +3452,7 @@ static void
trace_save_command (char *args, int from_tty)
{
int target_does_save = 0;
+ int generate_ctf = 0;
char **argv;
char *filename = NULL;
struct cleanup *back_to;
@@ -3466,6 +3468,8 @@ trace_save_command (char *args, int from_tty)
{
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
@@ -3475,15 +3479,22 @@ trace_save_command (char *args, int from_tty)
if (!filename)
error_no_arg (_("file in which to save trace data"));

- writer = xmalloc (sizeof (struct trace_file_writer));
- writer->ops = &tfile_write_ops;
- writer->data = xmalloc (sizeof (struct tfile_writer_data));
+ if (generate_ctf)
+ writer = ctf_trace_file_writer ();
+ else
+ {
+ writer = xmalloc (sizeof (struct trace_file_writer));
+ writer->ops = &tfile_write_ops;
+ writer->data = xmalloc (sizeof (struct tfile_writer_data));
+ }
+
make_cleanup (trace_file_writer_xfree, writer);

trace_save (filename, writer, target_does_save);

if (from_tty)
- printf_filtered (_("Trace data saved to file '%s'.\n"), filename);
+ printf_filtered (_("Trace data saved to %s '%s'.\n"),
+ generate_ctf ? "directory" : "file", filename);

do_cleanups (back_to);
}
@@ -3499,6 +3510,22 @@ trace_save_tfile (const char *filename, int target_does_save)
writer->ops = &tfile_write_ops;
writer->data = xmalloc (sizeof (struct tfile_writer_data));

+ trace_save (filename, writer, target_does_save);
+
+ back_to = make_cleanup (trace_file_writer_xfree, writer);
+ do_cleanups (back_to);
+}
+
+/* Save the trace data to dir DIRENAME 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 ();
+ trace_save (dirname, writer, target_does_save);
back_to = make_cleanup (trace_file_writer_xfree, writer);
do_cleanups (back_to);
}
@@ -5553,6 +5580,7 @@ _initialize_tracepoint (void)
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\
+Use the '-ctf' option to save the data to CTF format,\n\
using its own filesystem."));

c = add_com ("tvariable", class_trace, trace_variable_command,_("\
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index 1db6b69..44ff2de 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -372,6 +372,8 @@ extern void tfind_1 (enum trace_find_type type, int num,

extern void trace_save_tfile (const char *filename,
int target_does_save);
+extern void trace_save_ctf (const char *dirname,
+ int target_does_save);

extern struct traceframe_info *parse_traceframe_info (const char *tframe_info);

--
1.7.7.6