* [PATCH 1/5] Refactor 'tsave'
2013-02-27 2:18 [PATCH 0/5, 2nd try] CTF Support Yao Qi
@ 2013-02-27 2:18 ` Yao Qi
2013-02-28 16:49 ` Pedro Alves
2013-02-27 2:19 ` [PATCH 5/5] ctf test: report.exp Yao Qi
` (3 subsequent siblings)
4 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-02-27 2:18 UTC (permalink / raw)
To: gdb-patches
Hello,
The existing GDB code on saving trace data to trace file is highly
related to TFILE format, which makes some difficulties on supporting a
new trace file format, such as CTF. It is idea to de-couple trace
saving with actual trace file format. This is what this patch does.
With this patch applied, "tsave" command uses some operations vectors
to write data and the logic in "tsave" is not related to any specific
trace format.
gdb:
2013-02-27 Yao Qi <yao@codesourcery.com>
* tracepoint.c (trace_file_writer_xfree): New.
(struct tfile_writer_data): New.
(tfile_can_target_save, tfile_start): New.
(tfile_write_header, tfile_write_regblock_type): New.
(tfile_write_status, tfile_write_uploaded_tsv): New.
(tfile_write_uploaded_tp, tfile_write_definition_end): New.
(tfile_write_raw_data, (tfile_end): New.
(tfile_write_ops): New global variable.
(TRACE_WRITE_R_BLOCK, TRACE_WRITE_M_BLOCK): New macros.
(TRACE_WRITE_V_BLOCK): New macro.
(trace_save): Add extra one parameter WRITER. Make it static.
Use WRITER to writer trace.
(trace_save_command): Caller update.
(trace_save_tfile): Write trace data in TFILE format.
* tracepoint.h (struct trace_frame_write_ops): New.
(struct trace_file_write_ops): New.
(struct trace_file_writer): New.
(trace_save): Remove its declaration.
(trace_save_tfile): Declare it.
* mi/mi-main.c (mi_cmd_trace_save): Call trace_save_tfile
instead of trace_save.
---
gdb/mi/mi-main.c | 2 +-
gdb/tracepoint.c | 534 +++++++++++++++++++++++++++++++++++++++++-------------
gdb/tracepoint.h | 92 +++++++++-
3 files changed, 504 insertions(+), 124 deletions(-)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 37294e0..206b626 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2495,7 +2495,7 @@ mi_cmd_trace_save (char *command, char **argv, int argc)
filename = argv[0];
}
- trace_save (filename, target_saves);
+ trace_save_tfile (filename, target_saves);
}
void
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index f7a3650..43bd80f 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -2983,90 +2983,317 @@ encode_source_string (int tpnum, ULONGEST addr,
return -1;
}
-extern int trace_regblock_size;
+/* Free trace file writer and its data. */
-/* Save tracepoint data to file named FILENAME. 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_file_writer_xfree (void *arg)
+{
+ struct trace_file_writer *writer = (struct trace_file_writer *) arg;
-void
-trace_save (const char *filename, int target_does_save)
+ if (writer != NULL)
+ xfree (writer->data);
+
+ xfree (writer);
+}
+
+/* TFILE writer specific data. */
+
+struct tfile_writer_data
{
- struct cleanup *cleanup;
- char *pathname;
- struct trace_status *ts = current_trace_status ();
- int err, status;
+ /* File pointer to tfile trace file. */
FILE *fp;
- struct uploaded_tp *uploaded_tps = NULL, *utp;
- struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
- int a;
- char *act;
- LONGEST gotten = 0;
- ULONGEST offset = 0;
-#define MAX_TRACE_UPLOAD 2000
- gdb_byte buf[MAX_TRACE_UPLOAD];
- int written;
+ /* Path name of the tfile trace file. */
+ char *pathname;
- /* 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;
- }
+ struct cleanup *cleanup;
+};
- /* 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);
+/* This is the implementation of trace_file_write_ops method
+ can_target_save. We just call the generic target
+ target_save_trace_data to decide about target-side saving. */
+
+static int
+tfile_can_target_save (struct trace_file_writer *self,
+ const char *filename)
+{
+ int err = target_save_trace_data (filename);
+
+ return (err >= 0);
+}
- pathname = tilde_expand (filename);
- cleanup = make_cleanup (xfree, pathname);
+/* This is the implementation of trace_file_write_ops method
+ start. It creates the trace file FILENAME and register some
+ cleanups. */
+
+static void
+tfile_start (struct trace_file_writer *self, const char *filename)
+{
+ struct tfile_writer_data *data = self->data;
- fp = fopen (pathname, "wb");
- if (!fp)
+ data->pathname = tilde_expand (filename);
+ data->cleanup = make_cleanup (xfree, data->pathname);
+ data->fp = fopen (data->pathname, "wb");
+ if (data->fp == NULL)
error (_("Unable to open file '%s' for saving trace data (%s)"),
filename, safe_strerror (errno));
- make_cleanup_fclose (fp);
+ make_cleanup_fclose (data->fp);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_header. Write the TFILE header. */
+
+static void
+tfile_write_header (struct trace_file_writer *self)
+{
+ struct tfile_writer_data *data = self->data;
+ int written;
/* Write a file header, with a high-bit-set char to indicate a
binary file, plus a hint as what this file is, and a version
number in case of future needs. */
- written = fwrite ("\x7fTRACE0\n", 8, 1, fp);
+ written = fwrite ("\x7fTRACE0\n", 8, 1, data->fp);
if (written < 1)
- perror_with_name (pathname);
+ perror_with_name (data->pathname);
+}
- /* Write descriptive info. */
+/* This is the implementation of trace_file_write_ops method
+ write_regblock_type. Write the size of register block. */
- /* Write out the size of a register block. */
- fprintf (fp, "R %x\n", trace_regblock_size);
+static void
+tfile_write_regblock_type (struct trace_file_writer *self, int size)
+{
+ struct tfile_writer_data *data = self->data;
- /* Write out status of the tracing run (aka "tstatus" info). */
- fprintf (fp, "status %c;%s",
+ fprintf (data->fp, "R %x\n", size);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_status. */
+
+static void
+tfile_write_status (struct trace_file_writer *self,
+ struct trace_status *ts)
+{
+ struct tfile_writer_data *data = self->data;
+
+ fprintf (data->fp, "status %c;%s",
(ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
if (ts->stop_reason == tracepoint_error)
{
char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
bin2hex ((gdb_byte *) ts->stop_desc, buf, 0);
- fprintf (fp, ":%s", buf);
+ fprintf (data->fp, ":%s", buf);
}
- fprintf (fp, ":%x", ts->stopping_tracepoint);
+ fprintf (data->fp, ":%x", ts->stopping_tracepoint);
if (ts->traceframe_count >= 0)
- fprintf (fp, ";tframes:%x", ts->traceframe_count);
+ fprintf (data->fp, ";tframes:%x", ts->traceframe_count);
if (ts->traceframes_created >= 0)
- fprintf (fp, ";tcreated:%x", ts->traceframes_created);
+ fprintf (data->fp, ";tcreated:%x", ts->traceframes_created);
if (ts->buffer_free >= 0)
- fprintf (fp, ";tfree:%x", ts->buffer_free);
+ fprintf (data->fp, ";tfree:%x", ts->buffer_free);
if (ts->buffer_size >= 0)
- fprintf (fp, ";tsize:%x", ts->buffer_size);
+ fprintf (data->fp, ";tsize:%x", ts->buffer_size);
if (ts->disconnected_tracing)
- fprintf (fp, ";disconn:%x", ts->disconnected_tracing);
+ fprintf (data->fp, ";disconn:%x", ts->disconnected_tracing);
if (ts->circular_buffer)
- fprintf (fp, ";circular:%x", ts->circular_buffer);
- fprintf (fp, "\n");
+ fprintf (data->fp, ";circular:%x", ts->circular_buffer);
+ fprintf (data->fp, "\n");
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tsv. */
+
+static void
+tfile_write_uploaded_tsv (struct trace_file_writer *self,
+ struct uploaded_tsv *utsv)
+{
+ char *buf = "";
+ struct tfile_writer_data *data = self->data;
+
+ if (utsv->name)
+ {
+ buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
+ bin2hex ((gdb_byte *) (utsv->name), buf, 0);
+ }
+
+ fprintf (data->fp, "tsv %x:%s:%x:%s\n",
+ utsv->number, phex_nz (utsv->initial_value, 8),
+ utsv->builtin, buf);
+
+ if (utsv->name)
+ xfree (buf);
+}
+
+#define MAX_TRACE_UPLOAD 2000
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tp. */
+
+static void
+tfile_write_uploaded_tp (struct trace_file_writer *self,
+ struct uploaded_tp *utp)
+{
+ struct tfile_writer_data *data = self->data;
+ int a;
+ char *act;
+ gdb_byte buf[MAX_TRACE_UPLOAD];
+
+ fprintf (data->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 (data->fp, ":F%x", utp->orig_size);
+ if (utp->cond)
+ fprintf (data->fp,
+ ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
+ utp->cond);
+ fprintf (data->fp, "\n");
+ for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
+ fprintf (data->fp, "tp A%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
+ fprintf (data->fp, "tp S%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ if (utp->at_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
+ fprintf (data->fp, "tp Z%s\n", buf);
+ }
+ if (utp->cond_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "cond", utp->cond_string,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (data->fp, "tp Z%s\n", buf);
+ }
+ for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
+ {
+ encode_source_string (utp->number, utp->addr, "cmd", act,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (data->fp, "tp Z%s\n", buf);
+ }
+ fprintf (data->fp, "tp V%x:%s:%x:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ utp->hit_count,
+ phex_nz (utp->traceframe_usage,
+ sizeof (utp->traceframe_usage)));
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_definition_end. */
+
+static void
+tfile_write_definition_end (struct trace_file_writer *self)
+{
+ struct tfile_writer_data *data = self->data;
+
+ fprintf (data->fp, "\n");
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_raw_data. */
+
+static void
+tfile_write_raw_data (struct trace_file_writer *self, gdb_byte *buf,
+ LONGEST len)
+{
+ struct tfile_writer_data *data = self->data;
+
+ if (fwrite (buf, len, 1, data->fp) < 1)
+ perror_with_name (data->pathname);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ end. */
+
+static void
+tfile_end (struct trace_file_writer *self)
+{
+ struct tfile_writer_data *data = self->data;
+ uint32_t gotten = 0;
+
+ /* Mark the end of trace data. */
+ if (fwrite (&gotten, 4, 1, data->fp) < 1)
+ perror_with_name (data->pathname);
+
+ do_cleanups (data->cleanup);
+}
+
+/* Operations to write trace buffers into TFILE format. */
+
+static struct trace_file_write_ops tfile_write_ops =
+{
+ tfile_can_target_save,
+ tfile_start,
+ tfile_write_header,
+ tfile_write_regblock_type,
+ tfile_write_status,
+ tfile_write_uploaded_tsv,
+ tfile_write_uploaded_tp,
+ tfile_write_definition_end,
+ tfile_write_raw_data,
+ NULL,
+ tfile_end,
+};
+
+/* 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(writer, addr, buf, size) \
+ writer->ops->frame_ops->write_m_block ((writer), (addr), (buf), \
+ (size))
+#define TRACE_WRITE_V_BLOCK(writer, num, val) \
+ writer->ops->frame_ops->write_v_block ((writer), (num), (val))
+
+extern int trace_regblock_size;
+
+/* 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;
+ 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->can_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
@@ -3081,22 +3308,7 @@ trace_save (const char *filename, int target_does_save)
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);
- }
+ writer->ops->write_uploaded_tsv (writer, utsv);
free_uploaded_tsvs (&uploaded_tsvs);
@@ -3106,76 +3318,133 @@ trace_save (const char *filename, int target_does_save)
target_get_tracepoint_status (NULL, utp);
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; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
- fprintf (fp, "tp A%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
- fprintf (fp, "tp S%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- if (utp->at_string)
- {
- encode_source_string (utp->number, utp->addr,
- "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- if (utp->cond_string)
- {
- encode_source_string (utp->number, utp->addr,
- "cond", utp->cond_string,
- buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
- {
- encode_source_string (utp->number, utp->addr, "cmd", act,
- buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- fprintf (fp, "tp V%x:%s:%x:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->hit_count,
- phex_nz (utp->traceframe_usage,
- sizeof (utp->traceframe_usage)));
- }
+ writer->ops->write_uploaded_tp (writer, utp);
free_uploaded_tps (&uploaded_tps);
/* Mark the end of the definition section. */
- fprintf (fp, "\n");
+ writer->ops->write_definition_end (writer);
/* 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);
+ LONGEST gotten = 0;
+
+ if (writer->ops->write_raw_data != NULL)
+ {
+ gotten = target_get_raw_trace_data (buf, offset,
+ MAX_TRACE_UPLOAD);
+ if (gotten == 0)
+ break;
+ writer->ops->write_raw_data (writer, buf, gotten);
+ }
+ else
+ {
+ uint16_t tp_num;
+ uint32_t tf_size;
+ unsigned int read_length;
+ unsigned int block;
+
+ /* Read the first six bytes in, which is the tracepoint
+ number and trace frame size. */
+ gotten = target_get_raw_trace_data (buf, offset, 6);
+
+ if (gotten == 0)
+ break;
+ 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 <= MAX_TRACE_UPLOAD)
+ read_length = tf_size;
+ else
+ {
+ read_length = MAX_TRACE_UPLOAD;
+ gdb_assert (0);
+ }
+
+ if (tf_size > 0)
+ {
+ offset += 6;
+ gotten = target_get_raw_trace_data (buf, offset,
+ read_length);
+ gdb_assert (gotten >= read_length);
+
+ gotten = read_length;
+
+ for (block = 0; block < read_length; )
+ {
+ gdb_byte block_type = buf[block++];
+
+ switch (block_type)
+ {
+ case 'R':
+ TRACE_WRITE_R_BLOCK (writer, &buf[block],
+ trace_regblock_size);
+ block += trace_regblock_size;
+ break;
+ case 'M':
+ {
+ unsigned short mlen;
+ ULONGEST addr;
+
+ addr = (ULONGEST)
+ extract_unsigned_integer (&buf[block], 8,
+ byte_order);
+ block += 8;
+ mlen = (unsigned short)
+ extract_unsigned_integer (&buf[block], 2,
+ byte_order);
+
+ block += 2;
+ TRACE_WRITE_M_BLOCK (writer, addr,
+ &buf[block], mlen);
+
+ block += mlen;
+ break;
+ }
+ case 'V':
+ {
+ int vnum
+ = (int) extract_signed_integer (&buf[block],
+ 4,
+ byte_order);
+ LONGEST val
+ = extract_signed_integer (&buf[block + 4],
+ 8,
+ byte_order);
+
+ block += (4 + 8);
+ TRACE_WRITE_V_BLOCK (writer, vnum, val);
+ }
+ break;
+ default:
+ error (_("Unknown block type '%c' (0x%x) in"
+ " trace frame"),
+ block_type, block_type);
+ }
+ }
+ }
+
+ writer->ops->frame_ops->end (writer);
+ }
+
if (gotten < 0)
error (_("Failure to get requested trace buffer data"));
/* No more data is forthcoming, we're done. */
if (gotten == 0)
break;
- written = fwrite (buf, gotten, 1, fp);
- if (written < 1)
- perror_with_name (pathname);
offset += gotten;
}
- /* Mark the end of trace data. (We know that gotten is 0 at this point.) */
- written = fwrite (&gotten, 4, 1, fp);
- if (written < 1)
- perror_with_name (pathname);
-
- do_cleanups (cleanup);
+ writer->ops->end (writer);
}
static void
@@ -3185,6 +3454,7 @@ trace_save_command (char *args, int from_tty)
char **argv;
char *filename = NULL;
struct cleanup *back_to;
+ struct trace_file_writer *writer = NULL;
if (args == NULL)
error_no_arg (_("file in which to save trace data"));
@@ -3205,7 +3475,12 @@ trace_save_command (char *args, int from_tty)
if (!filename)
error_no_arg (_("file in which to save trace data"));
- trace_save (filename, target_does_save);
+ 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);
@@ -3213,6 +3488,21 @@ trace_save_command (char *args, int from_tty)
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 = xmalloc (sizeof (*writer));
+ struct cleanup *back_to;
+
+ writer->ops = &tfile_write_ops;
+ writer->data = xmalloc (sizeof (struct tfile_writer_data));
+
+ back_to = make_cleanup (trace_file_writer_xfree, writer);
+ do_cleanups (back_to);
+}
+
/* Tell the target what to do with an ongoing tracing run if GDB
disconnects for some reason. */
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index b2b9d7c..1db6b69 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -205,6 +205,95 @@ struct static_tracepoint_marker
char *extra;
};
+struct trace_file_writer;
+
+/* Operations to write trace frames to a specific trace format. */
+
+struct trace_frame_write_ops
+{
+ /* Write a new trace frame. The tracepoint number of this trace
+ frame is TPNUM. */
+ void (*start) (struct trace_file_writer *self, uint16_t tpnum);
+
+ /* Write a 'R' block. Buffer BUF contains its content and SIZE is
+ its size. */
+ void (*write_r_block) (struct trace_file_writer *self,
+ gdb_byte *buf, int size);
+
+ /* Write a 'M' block. Buffer BUF contains its content and LENGTH is
+ the length of it. ADDR is the start address of collected
+ memory. */
+ void (*write_m_block) (struct trace_file_writer *self,
+ ULONGEST addr, gdb_byte *buf,
+ uint16_t length);
+
+ /* Write a 'V' block. NUM is the trace variable number and VAL is
+ the value of the trace variable. */
+ void (*write_v_block) (struct trace_file_writer *self, int num,
+ LONGEST val);
+
+ /* The end of the trace frame. */
+ void (*end) (struct trace_file_writer *self);
+};
+
+/* Operations to write trace buggers to a specific trace format. */
+
+struct trace_file_write_ops
+{
+ /* Return true if the target is able to save the data to file
+ or directory NAME of desired format. */
+ int (*can_target_save) (struct trace_file_writer *self,
+ const char *ame);
+
+ /* Write the trace buggers to file or directory NAME. */
+ void (*start) (struct trace_file_writer *self,
+ const char *name);
+
+ /* Write the trace header. */
+ void (*write_header) (struct trace_file_writer *self);
+
+ /* Write the type of block about registers. SIZE is the size of
+ all registers on the target. */
+ void (*write_regblock_type) (struct trace_file_writer *self,
+ int size);
+
+ /* Write trace status TS. */
+ void (*write_status) (struct trace_file_writer *self,
+ struct trace_status *ts);
+
+ /* Write the uploaded TSV. */
+ void (*write_uploaded_tsv) (struct trace_file_writer *self,
+ struct uploaded_tsv *tsv);
+
+ /* Write the uploaded tracepoint TP. */
+ void (*write_uploaded_tp) (struct trace_file_writer *self,
+ struct uploaded_tp *tp);
+
+ /* Write to mark the definition part is end. */
+ void (*write_definition_end) (struct trace_file_writer *self);
+
+ /* Write the raw data of trace bugger. The content is in BUF and
+ length is LEN. */
+ void (*write_raw_data) (struct trace_file_writer *self,
+ gdb_byte *buf, LONGEST len);
+
+ /* Operations to write trace frames. */
+ struct trace_frame_write_ops *frame_ops;
+
+ /* The end of writing trace buggers. */
+ void (*end) (struct trace_file_writer *self);
+};
+
+/* Trace file writer for a given format. */
+
+struct trace_file_writer
+{
+ struct trace_file_write_ops *ops;
+
+ /* Writer specific data. */
+ void *data;
+};
+
extern void parse_static_tracepoint_marker_definition
(char *line, char **pp,
struct static_tracepoint_marker *marker);
@@ -281,7 +370,8 @@ extern void tfind_1 (enum trace_find_type type, int num,
ULONGEST addr1, ULONGEST addr2,
int from_tty);
-extern void trace_save (const char *filename, int target_does_save);
+extern void trace_save_tfile (const char *filename,
+ int target_does_save);
extern struct traceframe_info *parse_traceframe_info (const char *tframe_info);
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-02-27 2:18 ` [PATCH 1/5] Refactor 'tsave' Yao Qi
@ 2013-02-28 16:49 ` Pedro Alves
2013-03-01 8:58 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2013-02-28 16:49 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 02/27/2013 02:17 AM, Yao Qi wrote:
> Hello,
> The existing GDB code on saving trace data to trace file is highly
> related to TFILE format, which makes some difficulties on supporting a
> new trace file format, such as CTF. It is idea to de-couple trace
> saving with actual trace file format. This is what this patch does.
> With this patch applied, "tsave" command uses some operations vectors
> to write data and the logic in "tsave" is not related to any specific
> trace format.
>
> gdb:
>
> 2013-02-27 Yao Qi <yao@codesourcery.com>
>
> * tracepoint.c (trace_file_writer_xfree): New.
> (struct tfile_writer_data): New.
> (tfile_can_target_save, tfile_start): New.
> (tfile_write_header, tfile_write_regblock_type): New.
> (tfile_write_status, tfile_write_uploaded_tsv): New.
> (tfile_write_uploaded_tp, tfile_write_definition_end): New.
> (tfile_write_raw_data, (tfile_end): New.
> (tfile_write_ops): New global variable.
> (TRACE_WRITE_R_BLOCK, TRACE_WRITE_M_BLOCK): New macros.
> (TRACE_WRITE_V_BLOCK): New macro.
> (trace_save): Add extra one parameter WRITER. Make it static.
> Use WRITER to writer trace.
> (trace_save_command): Caller update.
> (trace_save_tfile): Write trace data in TFILE format.
> * tracepoint.h (struct trace_frame_write_ops): New.
> (struct trace_file_write_ops): New.
> (struct trace_file_writer): New.
> (trace_save): Remove its declaration.
> (trace_save_tfile): Declare it.
> * mi/mi-main.c (mi_cmd_trace_save): Call trace_save_tfile
> instead of trace_save.
>
> ---
> gdb/mi/mi-main.c | 2 +-
> gdb/tracepoint.c | 534 +++++++++++++++++++++++++++++++++++++++++-------------
> gdb/tracepoint.h | 92 +++++++++-
> 3 files changed, 504 insertions(+), 124 deletions(-)
>
> diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
> index 37294e0..206b626 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -2495,7 +2495,7 @@ mi_cmd_trace_save (char *command, char **argv, int argc)
> filename = argv[0];
> }
>
> - trace_save (filename, target_saves);
> + trace_save_tfile (filename, target_saves);
> }
>
> void
> diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
> index f7a3650..43bd80f 100644
> --- a/gdb/tracepoint.c
> +++ b/gdb/tracepoint.c
> @@ -2983,90 +2983,317 @@ encode_source_string (int tpnum, ULONGEST addr,
> return -1;
> }
>
> -extern int trace_regblock_size;
> +/* Free trace file writer and its data. */
>
> -/* Save tracepoint data to file named FILENAME. 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_file_writer_xfree (void *arg)
> +{
> + struct trace_file_writer *writer = (struct trace_file_writer *) arg;
>
> -void
> -trace_save (const char *filename, int target_does_save)
> + if (writer != NULL)
> + xfree (writer->data);
> +
> + xfree (writer);
As long as you need a NULL check, move both calls under it:
if (writer != NULL)
{
xfree (writer->data);
xfree (writer);
}
> +}
> +
> +/* TFILE writer specific data. */
> +
> +struct tfile_writer_data
> {
> - struct cleanup *cleanup;
> - char *pathname;
> - struct trace_status *ts = current_trace_status ();
> - int err, status;
> + /* File pointer to tfile trace file. */
> FILE *fp;
> - struct uploaded_tp *uploaded_tps = NULL, *utp;
> - struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
> - int a;
> - char *act;
> - LONGEST gotten = 0;
> - ULONGEST offset = 0;
> -#define MAX_TRACE_UPLOAD 2000
> - gdb_byte buf[MAX_TRACE_UPLOAD];
> - int written;
> + /* Path name of the tfile trace file. */
> + char *pathname;
>
> - /* 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;
> - }
> + struct cleanup *cleanup;
> +};
>
> - /* 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);
> +/* This is the implementation of trace_file_write_ops method
> + can_target_save. We just call the generic target
> + target_save_trace_data to decide about target-side saving. */
This looks odd to me. The query to check whether the writer
_can_ save, actually saves? What's the point of querying then?
This might just be a naming issue.
> +
> +static int
> +tfile_can_target_save (struct trace_file_writer *self,
> + const char *filename)
> +{
> + int err = target_save_trace_data (filename);
> +
> + return (err >= 0);
> +}
>
> - pathname = tilde_expand (filename);
> - cleanup = make_cleanup (xfree, pathname);
> +/* This is the implementation of trace_file_write_ops method
> + start. It creates the trace file FILENAME and register some
> + cleanups. */
"it (...) registers"
> +
> +static void
> +tfile_start (struct trace_file_writer *self, const char *filename)
> +{
> + struct tfile_writer_data *data = self->data;
>
> - fp = fopen (pathname, "wb");
> - if (!fp)
> + data->pathname = tilde_expand (filename);
> + data->cleanup = make_cleanup (xfree, data->pathname);
> + data->fp = fopen (data->pathname, "wb");
> + if (data->fp == NULL)
> error (_("Unable to open file '%s' for saving trace data (%s)"),
> filename, safe_strerror (errno));
> - make_cleanup_fclose (fp);
> + make_cleanup_fclose (data->fp);
> +}
> +
> +/* This is the implementation of trace_file_write_ops method
> + write_header. Write the TFILE header. */
> +
> +static void
> +tfile_write_header (struct trace_file_writer *self)
> +{
> + struct tfile_writer_data *data = self->data;
> + int written;
>
> /* Write a file header, with a high-bit-set char to indicate a
> binary file, plus a hint as what this file is, and a version
> number in case of future needs. */
> - written = fwrite ("\x7fTRACE0\n", 8, 1, fp);
> + written = fwrite ("\x7fTRACE0\n", 8, 1, data->fp);
> if (written < 1)
> - perror_with_name (pathname);
> + perror_with_name (data->pathname);
> +}
>
> - /* Write descriptive info. */
> +/* This is the implementation of trace_file_write_ops method
> + write_regblock_type. Write the size of register block. */
>
> - /* Write out the size of a register block. */
> - fprintf (fp, "R %x\n", trace_regblock_size);
> +static void
> +tfile_write_regblock_type (struct trace_file_writer *self, int size)
> +{
> + struct tfile_writer_data *data = self->data;
>
> - /* Write out status of the tracing run (aka "tstatus" info). */
> - fprintf (fp, "status %c;%s",
> + fprintf (data->fp, "R %x\n", size);
> +}
> +
> +/* This is the implementation of trace_file_write_ops method
> + write_status. */
> +
> +static void
> +tfile_write_status (struct trace_file_writer *self,
> + struct trace_status *ts)
> +{
> + struct tfile_writer_data *data = self->data;
> +
> + fprintf (data->fp, "status %c;%s",
> (ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
> if (ts->stop_reason == tracepoint_error)
> {
> char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
>
> bin2hex ((gdb_byte *) ts->stop_desc, buf, 0);
> - fprintf (fp, ":%s", buf);
> + fprintf (data->fp, ":%s", buf);
> }
> - fprintf (fp, ":%x", ts->stopping_tracepoint);
> + fprintf (data->fp, ":%x", ts->stopping_tracepoint);
> if (ts->traceframe_count >= 0)
> - fprintf (fp, ";tframes:%x", ts->traceframe_count);
> + fprintf (data->fp, ";tframes:%x", ts->traceframe_count);
> if (ts->traceframes_created >= 0)
> - fprintf (fp, ";tcreated:%x", ts->traceframes_created);
> + fprintf (data->fp, ";tcreated:%x", ts->traceframes_created);
> if (ts->buffer_free >= 0)
> - fprintf (fp, ";tfree:%x", ts->buffer_free);
> + fprintf (data->fp, ";tfree:%x", ts->buffer_free);
> if (ts->buffer_size >= 0)
> - fprintf (fp, ";tsize:%x", ts->buffer_size);
> + fprintf (data->fp, ";tsize:%x", ts->buffer_size);
> if (ts->disconnected_tracing)
> - fprintf (fp, ";disconn:%x", ts->disconnected_tracing);
> + fprintf (data->fp, ";disconn:%x", ts->disconnected_tracing);
> if (ts->circular_buffer)
> - fprintf (fp, ";circular:%x", ts->circular_buffer);
> - fprintf (fp, "\n");
> + fprintf (data->fp, ";circular:%x", ts->circular_buffer);
> + fprintf (data->fp, "\n");
> +}
> +
> +/* This is the implementation of trace_file_write_ops method
> + write_uploaded_tsv. */
> +
> +static void
> +tfile_write_uploaded_tsv (struct trace_file_writer *self,
> + struct uploaded_tsv *utsv)
> +{
> + char *buf = "";
> + struct tfile_writer_data *data = self->data;
> +
> + if (utsv->name)
> + {
> + buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
> + bin2hex ((gdb_byte *) (utsv->name), buf, 0);
> + }
> +
> + fprintf (data->fp, "tsv %x:%s:%x:%s\n",
> + utsv->number, phex_nz (utsv->initial_value, 8),
> + utsv->builtin, buf);
> +
> + if (utsv->name)
> + xfree (buf);
> +}
> +
> +#define MAX_TRACE_UPLOAD 2000
> +
> +/* This is the implementation of trace_file_write_ops method
> + write_uploaded_tp. */
> +
> +static void
> +tfile_write_uploaded_tp (struct trace_file_writer *self,
> + struct uploaded_tp *utp)
> +{
> + struct tfile_writer_data *data = self->data;
> + int a;
> + char *act;
> + gdb_byte buf[MAX_TRACE_UPLOAD];
> +
> + fprintf (data->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 (data->fp, ":F%x", utp->orig_size);
> + if (utp->cond)
> + fprintf (data->fp,
> + ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
> + utp->cond);
> + fprintf (data->fp, "\n");
> + for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
> + fprintf (data->fp, "tp A%x:%s:%s\n",
> + utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
> + for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
> + fprintf (data->fp, "tp S%x:%s:%s\n",
> + utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
> + if (utp->at_string)
> + {
> + encode_source_string (utp->number, utp->addr,
> + "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
> + fprintf (data->fp, "tp Z%s\n", buf);
> + }
> + if (utp->cond_string)
> + {
> + encode_source_string (utp->number, utp->addr,
> + "cond", utp->cond_string,
> + buf, MAX_TRACE_UPLOAD);
> + fprintf (data->fp, "tp Z%s\n", buf);
> + }
> + for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
> + {
> + encode_source_string (utp->number, utp->addr, "cmd", act,
> + buf, MAX_TRACE_UPLOAD);
> + fprintf (data->fp, "tp Z%s\n", buf);
> + }
> + fprintf (data->fp, "tp V%x:%s:%x:%s\n",
> + utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
> + utp->hit_count,
> + phex_nz (utp->traceframe_usage,
> + sizeof (utp->traceframe_usage)));
> +}
> +
> +/* This is the implementation of trace_file_write_ops method
> + write_definition_end. */
> +
> +static void
> +tfile_write_definition_end (struct trace_file_writer *self)
> +{
> + struct tfile_writer_data *data = self->data;
> +
> + fprintf (data->fp, "\n");
> +}
> +
> +/* This is the implementation of trace_file_write_ops method
> + write_raw_data. */
> +
> +static void
> +tfile_write_raw_data (struct trace_file_writer *self, gdb_byte *buf,
> + LONGEST len)
> +{
> + struct tfile_writer_data *data = self->data;
> +
> + if (fwrite (buf, len, 1, data->fp) < 1)
> + perror_with_name (data->pathname);
> +}
> +
> +/* This is the implementation of trace_file_write_ops method
> + end. */
> +
> +static void
> +tfile_end (struct trace_file_writer *self)
> +{
> + struct tfile_writer_data *data = self->data;
> + uint32_t gotten = 0;
> +
> + /* Mark the end of trace data. */
> + if (fwrite (&gotten, 4, 1, data->fp) < 1)
> + perror_with_name (data->pathname);
> +
> + do_cleanups (data->cleanup);
> +}
> +
> +/* Operations to write trace buffers into TFILE format. */
> +
> +static struct trace_file_write_ops tfile_write_ops =
> +{
> + tfile_can_target_save,
> + tfile_start,
> + tfile_write_header,
> + tfile_write_regblock_type,
> + tfile_write_status,
> + tfile_write_uploaded_tsv,
> + tfile_write_uploaded_tp,
> + tfile_write_definition_end,
> + tfile_write_raw_data,
> + NULL,
> + tfile_end,
> +};
> +
> +/* 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(writer, addr, buf, size) \
> + writer->ops->frame_ops->write_m_block ((writer), (addr), (buf), \
> + (size))
> +#define TRACE_WRITE_V_BLOCK(writer, num, val) \
> + writer->ops->frame_ops->write_v_block ((writer), (num), (val))
> +
> +extern int trace_regblock_size;
> +
> +/* 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;
> + 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->can_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
> @@ -3081,22 +3308,7 @@ trace_save (const char *filename, int target_does_save)
> 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);
> - }
> + writer->ops->write_uploaded_tsv (writer, utsv);
>
> free_uploaded_tsvs (&uploaded_tsvs);
>
> @@ -3106,76 +3318,133 @@ trace_save (const char *filename, int target_does_save)
> target_get_tracepoint_status (NULL, utp);
>
> 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; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
> - fprintf (fp, "tp A%x:%s:%s\n",
> - utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
> - for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
> - fprintf (fp, "tp S%x:%s:%s\n",
> - utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
> - if (utp->at_string)
> - {
> - encode_source_string (utp->number, utp->addr,
> - "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
> - fprintf (fp, "tp Z%s\n", buf);
> - }
> - if (utp->cond_string)
> - {
> - encode_source_string (utp->number, utp->addr,
> - "cond", utp->cond_string,
> - buf, MAX_TRACE_UPLOAD);
> - fprintf (fp, "tp Z%s\n", buf);
> - }
> - for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
> - {
> - encode_source_string (utp->number, utp->addr, "cmd", act,
> - buf, MAX_TRACE_UPLOAD);
> - fprintf (fp, "tp Z%s\n", buf);
> - }
> - fprintf (fp, "tp V%x:%s:%x:%s\n",
> - utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
> - utp->hit_count,
> - phex_nz (utp->traceframe_usage,
> - sizeof (utp->traceframe_usage)));
> - }
> + writer->ops->write_uploaded_tp (writer, utp);
>
> free_uploaded_tps (&uploaded_tps);
>
> /* Mark the end of the definition section. */
> - fprintf (fp, "\n");
> + writer->ops->write_definition_end (writer);
>
> /* 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);
> + LONGEST gotten = 0;
> +
> + if (writer->ops->write_raw_data != NULL)
I got confused while reading this. As it stands, why do we
need this check?
> + {
> + gotten = target_get_raw_trace_data (buf, offset,
> + MAX_TRACE_UPLOAD);
> + if (gotten == 0)
> + break;
> + writer->ops->write_raw_data (writer, buf, gotten);
> + }
> + else
and this whole else block? This didn't seem to exist
before the patch. At least a comment is missing here.
(time passes)
Ok, I think I got it.
I think an important missing clue here is that "raw" in
"write_raw_data" and target_get_raw_trace_data actually means
frames in "tfile" format, right? "ops->write_raw_data" being
NULL means that the writer can't handle parsing the "tfile"
frames itself.
"raw" here is almost meaningless to the reader -- surely "raw" in
the ctf context could mean raw ctf "something".
Maybe "raw" should really be s/raw/tfile/ or s/raw/raw_tfile/ ?
> + {
> + uint16_t tp_num;
> + uint32_t tf_size;
> + unsigned int read_length;
> + unsigned int block;
> +
> + /* Read the first six bytes in, which is the tracepoint
> + number and trace frame size. */
> + gotten = target_get_raw_trace_data (buf, offset, 6);
> +
> + if (gotten == 0)
> + break;
> + 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 <= MAX_TRACE_UPLOAD)
> + read_length = tf_size;
> + else
> + {
> + read_length = MAX_TRACE_UPLOAD;
> + gdb_assert (0);
Leftover? I guess this means you didn't try with a big trace
frame?
> + }
> +
> + if (tf_size > 0)
> + {
> + offset += 6;
> + gotten = target_get_raw_trace_data (buf, offset,
> + read_length);
> + gdb_assert (gotten >= read_length);
An assertion here doesn't seem appropriate, as this is handling input.
Moreover we have:
/* 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)
{
> +
> + gotten = read_length;
> +
> + for (block = 0; block < read_length; )
> + {
> + gdb_byte block_type = buf[block++];
> +
> + switch (block_type)
> + {
> + case 'R':
> + TRACE_WRITE_R_BLOCK (writer, &buf[block],
> + trace_regblock_size);
> + block += trace_regblock_size;
> + break;
> + case 'M':
> + {
> + unsigned short mlen;
> + ULONGEST addr;
> +
> + addr = (ULONGEST)
> + extract_unsigned_integer (&buf[block], 8,
> + byte_order);
> + block += 8;
> + mlen = (unsigned short)
> + extract_unsigned_integer (&buf[block], 2,
> + byte_order);
> +
> + block += 2;
> + TRACE_WRITE_M_BLOCK (writer, addr,
> + &buf[block], mlen);
> +
> + block += mlen;
> + break;
> + }
> + case 'V':
> + {
> + int vnum
> + = (int) extract_signed_integer (&buf[block],
> + 4,
> + byte_order);
> + LONGEST val
> + = extract_signed_integer (&buf[block + 4],
> + 8,
> + byte_order);
> +
> + block += (4 + 8);
> + TRACE_WRITE_V_BLOCK (writer, vnum, val);
> + }
> + break;
> + default:
> + error (_("Unknown block type '%c' (0x%x) in"
> + " trace frame"),
> + block_type, block_type);
> + }
> + }
> + }
> +
> + writer->ops->frame_ops->end (writer);
> + }
> +
> if (gotten < 0)
> error (_("Failure to get requested trace buffer data"));
> /* No more data is forthcoming, we're done. */
> if (gotten == 0)
> break;
> - written = fwrite (buf, gotten, 1, fp);
> - if (written < 1)
> - perror_with_name (pathname);
> offset += gotten;
> }
>
> - /* Mark the end of trace data. (We know that gotten is 0 at this point.) */
> - written = fwrite (&gotten, 4, 1, fp);
> - if (written < 1)
> - perror_with_name (pathname);
> -
> - do_cleanups (cleanup);
> + writer->ops->end (writer);
> }
>
> static void
> @@ -3185,6 +3454,7 @@ trace_save_command (char *args, int from_tty)
> char **argv;
> char *filename = NULL;
> struct cleanup *back_to;
> + struct trace_file_writer *writer = NULL;
>
> if (args == NULL)
> error_no_arg (_("file in which to save trace data"));
> @@ -3205,7 +3475,12 @@ trace_save_command (char *args, int from_tty)
> if (!filename)
> error_no_arg (_("file in which to save trace data"));
>
> - trace_save (filename, target_does_save);
> + writer = xmalloc (sizeof (struct trace_file_writer));
> + writer->ops = &tfile_write_ops;
> + writer->data = xmalloc (sizeof (struct tfile_writer_data));
I suggest moving these three lines to a new "tfile_writer_data_new"
function that returns a new writer...
> + 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);
> @@ -3213,6 +3488,21 @@ trace_save_command (char *args, int from_tty)
> 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 = xmalloc (sizeof (*writer));
> + struct cleanup *back_to;
> +
> + writer->ops = &tfile_write_ops;
> + writer->data = xmalloc (sizeof (struct tfile_writer_data));
... and call it here too.
> +
> + back_to = make_cleanup (trace_file_writer_xfree, writer);
> + do_cleanups (back_to);
> +}
> +
> /* Tell the target what to do with an ongoing tracing run if GDB
> disconnects for some reason. */
>
> diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
> index b2b9d7c..1db6b69 100644
> --- a/gdb/tracepoint.h
> +++ b/gdb/tracepoint.h
> @@ -205,6 +205,95 @@ struct static_tracepoint_marker
> char *extra;
> };
>
> +struct trace_file_writer;
> +
> +/* Operations to write trace frames to a specific trace format. */
> +
> +struct trace_frame_write_ops
> +{
> + /* Write a new trace frame. The tracepoint number of this trace
> + frame is TPNUM. */
> + void (*start) (struct trace_file_writer *self, uint16_t tpnum);
> +
> + /* Write a 'R' block. Buffer BUF contains its content and SIZE is
> + its size. */
"an 'R'". I believe "contents" is more appropriate.
> + void (*write_r_block) (struct trace_file_writer *self,
> + gdb_byte *buf, int size);
> +
> + /* Write a 'M' block. Buffer BUF contains its content and LENGTH is
> + the length of it. ADDR is the start address of collected
> + memory. */
an 'M'.
> + void (*write_m_block) (struct trace_file_writer *self,
> + ULONGEST addr, gdb_byte *buf,
> + uint16_t length);
> +
> + /* Write a 'V' block. NUM is the trace variable number and VAL is
> + the value of the trace variable. */
> + void (*write_v_block) (struct trace_file_writer *self, int num,
> + LONGEST val);
Though, talking about 'R', 'M' and 'V' is tfile specific, right?
> +
> + /* The end of the trace frame. */
> + void (*end) (struct trace_file_writer *self);
> +};
> +
> +/* Operations to write trace buggers to a specific trace format. */
s/buggers/buffers/ I believe. More instances of the typo below.
> +
> +struct trace_file_write_ops
> +{
> + /* Return true if the target is able to save the data to file
> + or directory NAME of desired format. */
> + int (*can_target_save) (struct trace_file_writer *self,
> + const char *ame);
> +
> + /* Write the trace buggers to file or directory NAME. */
> + void (*start) (struct trace_file_writer *self,
> + const char *name);
> +
> + /* Write the trace header. */
> + void (*write_header) (struct trace_file_writer *self);
> +
> + /* Write the type of block about registers. SIZE is the size of
> + all registers on the target. */
> + void (*write_regblock_type) (struct trace_file_writer *self,
> + int size);
> +
> + /* Write trace status TS. */
> + void (*write_status) (struct trace_file_writer *self,
> + struct trace_status *ts);
> +
> + /* Write the uploaded TSV. */
> + void (*write_uploaded_tsv) (struct trace_file_writer *self,
> + struct uploaded_tsv *tsv);
> +
> + /* Write the uploaded tracepoint TP. */
> + void (*write_uploaded_tp) (struct trace_file_writer *self,
> + struct uploaded_tp *tp);
> +
> + /* Write to mark the definition part is end. */
This doesn't parse correctly. Something like:
/* Called to signal the end of the definition part. */
perhaps.
> + void (*write_definition_end) (struct trace_file_writer *self);
> +
> + /* Write the raw data of trace bugger. The content is in BUF and
> + length is LEN. */
> + void (*write_raw_data) (struct trace_file_writer *self,
> + gdb_byte *buf, LONGEST len);
> +
> + /* Operations to write trace frames. */
> + struct trace_frame_write_ops *frame_ops;
> +
> + /* The end of writing trace buggers. */
> + void (*end) (struct trace_file_writer *self);
> +};
> +
> +/* Trace file writer for a given format. */
> +
> +struct trace_file_writer
> +{
> + struct trace_file_write_ops *ops;
> +
> + /* Writer specific data. */
> + void *data;
> +};
I think it's better design to get rid of this
"data" pointer, which as is needs to be xmalloc'ed
separately. Get rid of "struct tfile_writer_data", and do
instead:
struct trace_file_writer
{
struct trace_file_write_ops *ops;
};
struct tfile_writer
{
struct trace_file_writer base;
/* Path name of the tfile trace file. */
char *pathname;
struct cleanup *cleanup;
};
struct whatever_writer
{
struct trace_file_writer base;
/* whatever fields. */
};
And then:
- struct tfile_writer_data *data = self->data;
+ struct tfile_writer *writer = (struct tfile_writer *) self;
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-02-28 16:49 ` Pedro Alves
@ 2013-03-01 8:58 ` Yao Qi
2013-03-01 10:05 ` Pedro Alves
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-03-01 8:58 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 4075 bytes --]
Hi Pedro,
thanks for the quick review. The updated patch address most of your
comments except some I'd like to discuss with you below,
On 03/01/2013 12:33 AM, Pedro Alves wrote:
> Ok, I think I got it.
>
> I think an important missing clue here is that "raw" in
> "write_raw_data" and target_get_raw_trace_data actually means
> frames in "tfile" format, right? "ops->write_raw_data" being
> NULL means that the writer can't handle parsing the "tfile"
> frames itself.
>
> "raw" here is almost meaningless to the reader -- surely "raw" in
> the ctf context could mean raw ctf "something".
> Maybe "raw" should really be s/raw/tfile/ or s/raw/raw_tfile/ ?
>
"raw" here means the data in the trace buffers, and "raw data" has few
to do with the file format. It is only about how data are stored in
trace buffers. See the comments on write_raw_data,
/* Write the raw data of trace buffer. The content is in BUF and
length is LEN. */
void (*write_raw_data) (struct trace_file_writer *self,
gdb_byte *buf, LONGEST len);
I'd like to differentiate the "raw data" stored in trace buffers and the
data finally saved into files.
TFILE is a format that composed by ascii definition part and trace
frames dumped from the raw data directly. There could be another trace
file format FOO that stores raw data as well.
>> >+ {
>> >+ uint16_t tp_num;
>> >+ uint32_t tf_size;
>> >+ unsigned int read_length;
>> >+ unsigned int block;
>> >+
>> >+ /* Read the first six bytes in, which is the tracepoint
>> >+ number and trace frame size. */
>> >+ gotten = target_get_raw_trace_data (buf, offset, 6);
>> >+
>> >+ if (gotten == 0)
>> >+ break;
>> >+ 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 <= MAX_TRACE_UPLOAD)
>> >+ read_length = tf_size;
>> >+ else
>> >+ {
>> >+ read_length = MAX_TRACE_UPLOAD;
>> >+ gdb_assert (0);
> Leftover? I guess this means you didn't try with a big trace
> frame?
>
I change this part that GDB will skip the frame if it is too big, and
print a warning, like this:
warning: Skip this trace frame because its size (708) is greater than
the size of GDB internal buffer (200)
I set MAX_TRACE_UPLOAD to 200, and run the testsuite. The CTF file is
still correct, but some frames are lost.
>> >+ }
>> >+
>> >+ if (tf_size > 0)
>> >+ {
>> >+ offset += 6;
>> >+ gotten = target_get_raw_trace_data (buf, offset,
>> >+ read_length);
>> >+ gdb_assert (gotten >= read_length);
> An assertion here doesn't seem appropriate, as this is handling input.
> Moreover we have:
>
> /* 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)
> {
>
The first sentence of header comment is "because we don't know how the
target actually organizes its trace memory", however, we know it here,
because we've got the size of the traceframe. The 'gotten' should be
greater than or equal to the size of the traceframe.
Considering that the remote target may return something wrong, it is too
aggressive to use an assert here. In the new version, I change it to
throwing an error.
>> >+ /* Write to mark the definition part is end. */
> This doesn't parse correctly. Something like:
>
> /* Called to signal the end of the definition part. */
>
> perhaps.
>
Is it incorrect? The basic sentence pattern is "write (an intransitive
verb) to do sth.", and "[that] the definition part is end" is a noun clause.
--
Yao (é½å°§)
[-- Attachment #2: 0001-Refactor-tsave.patch --]
[-- Type: text/x-patch, Size: 24918 bytes --]
gdb:
2013-03-01 Yao Qi <yao@codesourcery.com>
* tracepoint.c (trace_file_writer_xfree): New.
(struct tfile_writer_data): New.
(tfile_can_target_save, tfile_start): New.
(tfile_write_header, tfile_write_regblock_type): New.
(tfile_write_status, tfile_write_uploaded_tsv): New.
(tfile_write_uploaded_tp, tfile_write_definition_end): New.
(tfile_write_raw_data, (tfile_end): New.
(tfile_write_ops): New global variable.
(TRACE_WRITE_R_BLOCK, TRACE_WRITE_M_BLOCK): New macros.
(TRACE_WRITE_V_BLOCK): New macro.
(trace_save): Add extra one parameter WRITER. Make it static.
Use WRITER to writer trace.
(tfile_trace_file_writer_new): New.
(trace_save_command): Caller update.
(trace_save_tfile): Write trace data in TFILE format.
* tracepoint.h (struct trace_frame_write_ops): New.
(struct trace_file_write_ops): New.
(struct trace_file_writer): New.
(trace_save): Remove its declaration.
(trace_save_tfile): Declare it.
* mi/mi-main.c (mi_cmd_trace_save): Call trace_save_tfile
instead of trace_save.
---
gdb/mi/mi-main.c | 2 +-
gdb/tracepoint.c | 559 ++++++++++++++++++++++++++++++++++++++++++------------
gdb/tracepoint.h | 90 +++++++++-
3 files changed, 528 insertions(+), 123 deletions(-)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 37294e0..206b626 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2495,7 +2495,7 @@ mi_cmd_trace_save (char *command, char **argv, int argc)
filename = argv[0];
}
- trace_save (filename, target_saves);
+ trace_save_tfile (filename, target_saves);
}
void
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 9a80aa3..cc1d742 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -2983,90 +2983,323 @@ encode_source_string (int tpnum, ULONGEST addr,
return -1;
}
-extern int trace_regblock_size;
+/* Free trace file writer. */
-/* Save tracepoint data to file named FILENAME. 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_file_writer_xfree (void *arg)
+{
+ xfree (arg);
+}
-void
-trace_save (const char *filename, int target_does_save)
+/* TFILE trace writer. */
+
+struct tfile_trace_file_writer
{
- struct cleanup *cleanup;
- char *pathname;
- struct trace_status *ts = current_trace_status ();
- int err, status;
+ struct trace_file_writer base;
+
+ /* File pointer to tfile trace file. */
FILE *fp;
- struct uploaded_tp *uploaded_tps = NULL, *utp;
- struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
- int a;
- char *act;
- LONGEST gotten = 0;
- ULONGEST offset = 0;
-#define MAX_TRACE_UPLOAD 2000
- gdb_byte buf[MAX_TRACE_UPLOAD];
- int written;
+ /* Path name of the tfile trace file. */
+ char *pathname;
- /* 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;
- }
+ struct cleanup *cleanup;
+};
- /* 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);
+/* This is the implementation of trace_file_write_ops method
+ target_save. We just call the generic target
+ target_save_trace_data to do target-side saving. */
+
+static int
+tfile_target_save (struct trace_file_writer *self,
+ const char *filename)
+{
+ int err = target_save_trace_data (filename);
+
+ return (err >= 0);
+}
- pathname = tilde_expand (filename);
- cleanup = make_cleanup (xfree, pathname);
+/* This is the implementation of trace_file_write_ops method
+ start. It creates the trace file FILENAME and registers some
+ cleanups. */
- fp = fopen (pathname, "wb");
- if (!fp)
+static void
+tfile_start (struct trace_file_writer *self, const char *filename)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ writer->pathname = tilde_expand (filename);
+ writer->cleanup = make_cleanup (xfree, writer->pathname);
+ writer->fp = fopen (writer->pathname, "wb");
+ if (writer->fp == NULL)
error (_("Unable to open file '%s' for saving trace data (%s)"),
filename, safe_strerror (errno));
- make_cleanup_fclose (fp);
+ make_cleanup_fclose (writer->fp);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_header. Write the TFILE header. */
+
+static void
+tfile_write_header (struct trace_file_writer *self)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+ int written;
/* Write a file header, with a high-bit-set char to indicate a
binary file, plus a hint as what this file is, and a version
number in case of future needs. */
- written = fwrite ("\x7fTRACE0\n", 8, 1, fp);
+ written = fwrite ("\x7fTRACE0\n", 8, 1, writer->fp);
if (written < 1)
- perror_with_name (pathname);
+ perror_with_name (writer->pathname);
+}
- /* Write descriptive info. */
+/* This is the implementation of trace_file_write_ops method
+ write_regblock_type. Write the size of register block. */
- /* Write out the size of a register block. */
- fprintf (fp, "R %x\n", trace_regblock_size);
+static void
+tfile_write_regblock_type (struct trace_file_writer *self, int size)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
- /* Write out status of the tracing run (aka "tstatus" info). */
- fprintf (fp, "status %c;%s",
+ fprintf (writer->fp, "R %x\n", size);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_status. */
+
+static void
+tfile_write_status (struct trace_file_writer *self,
+ struct trace_status *ts)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ fprintf (writer->fp, "status %c;%s",
(ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
if (ts->stop_reason == tracepoint_error)
{
char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
bin2hex ((gdb_byte *) ts->stop_desc, buf, 0);
- fprintf (fp, ":%s", buf);
+ fprintf (writer->fp, ":%s", buf);
}
- fprintf (fp, ":%x", ts->stopping_tracepoint);
+ fprintf (writer->fp, ":%x", ts->stopping_tracepoint);
if (ts->traceframe_count >= 0)
- fprintf (fp, ";tframes:%x", ts->traceframe_count);
+ fprintf (writer->fp, ";tframes:%x", ts->traceframe_count);
if (ts->traceframes_created >= 0)
- fprintf (fp, ";tcreated:%x", ts->traceframes_created);
+ fprintf (writer->fp, ";tcreated:%x", ts->traceframes_created);
if (ts->buffer_free >= 0)
- fprintf (fp, ";tfree:%x", ts->buffer_free);
+ fprintf (writer->fp, ";tfree:%x", ts->buffer_free);
if (ts->buffer_size >= 0)
- fprintf (fp, ";tsize:%x", ts->buffer_size);
+ fprintf (writer->fp, ";tsize:%x", ts->buffer_size);
if (ts->disconnected_tracing)
- fprintf (fp, ";disconn:%x", ts->disconnected_tracing);
+ fprintf (writer->fp, ";disconn:%x", ts->disconnected_tracing);
if (ts->circular_buffer)
- fprintf (fp, ";circular:%x", ts->circular_buffer);
- fprintf (fp, "\n");
+ fprintf (writer->fp, ";circular:%x", ts->circular_buffer);
+ fprintf (writer->fp, "\n");
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tsv. */
+
+static void
+tfile_write_uploaded_tsv (struct trace_file_writer *self,
+ struct uploaded_tsv *utsv)
+{
+ char *buf = "";
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ if (utsv->name)
+ {
+ buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
+ bin2hex ((gdb_byte *) (utsv->name), buf, 0);
+ }
+
+ fprintf (writer->fp, "tsv %x:%s:%x:%s\n",
+ utsv->number, phex_nz (utsv->initial_value, 8),
+ utsv->builtin, buf);
+
+ if (utsv->name)
+ xfree (buf);
+}
+
+#define MAX_TRACE_UPLOAD 2000
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tp. */
+
+static void
+tfile_write_uploaded_tp (struct trace_file_writer *self,
+ struct uploaded_tp *utp)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+ int a;
+ char *act;
+ gdb_byte buf[MAX_TRACE_UPLOAD];
+
+ fprintf (writer->fp, "tp T%x:%s:%c:%x:%x",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ (utp->enabled ? 'E' : 'D'), utp->step, utp->pass);
+ if (utp->type == bp_fast_tracepoint)
+ fprintf (writer->fp, ":F%x", utp->orig_size);
+ if (utp->cond)
+ fprintf (writer->fp,
+ ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
+ utp->cond);
+ fprintf (writer->fp, "\n");
+ for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
+ fprintf (writer->fp, "tp A%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
+ fprintf (writer->fp, "tp S%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ if (utp->at_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
+ fprintf (writer->fp, "tp Z%s\n", buf);
+ }
+ if (utp->cond_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "cond", utp->cond_string,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (writer->fp, "tp Z%s\n", buf);
+ }
+ for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
+ {
+ encode_source_string (utp->number, utp->addr, "cmd", act,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (writer->fp, "tp Z%s\n", buf);
+ }
+ fprintf (writer->fp, "tp V%x:%s:%x:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ utp->hit_count,
+ phex_nz (utp->traceframe_usage,
+ sizeof (utp->traceframe_usage)));
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_definition_end. */
+
+static void
+tfile_write_definition_end (struct trace_file_writer *self)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ fprintf (writer->fp, "\n");
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_raw_data. */
+
+static void
+tfile_write_raw_data (struct trace_file_writer *self, gdb_byte *buf,
+ LONGEST len)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ if (fwrite (buf, len, 1, writer->fp) < 1)
+ perror_with_name (writer->pathname);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ end. */
+
+static void
+tfile_end (struct trace_file_writer *self)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+ uint32_t gotten = 0;
+
+ /* Mark the end of trace data. */
+ if (fwrite (&gotten, 4, 1, writer->fp) < 1)
+ perror_with_name (writer->pathname);
+
+ do_cleanups (writer->cleanup);
+}
+
+/* Operations to write trace buffers into TFILE format. */
+
+static struct trace_file_write_ops tfile_write_ops =
+{
+ tfile_target_save,
+ tfile_start,
+ tfile_write_header,
+ tfile_write_regblock_type,
+ tfile_write_status,
+ tfile_write_uploaded_tsv,
+ tfile_write_uploaded_tp,
+ tfile_write_definition_end,
+ tfile_write_raw_data,
+ NULL,
+ tfile_end,
+};
+
+/* 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(writer, addr, buf, size) \
+ writer->ops->frame_ops->write_m_block ((writer), (addr), (buf), \
+ (size))
+#define TRACE_WRITE_V_BLOCK(writer, num, val) \
+ writer->ops->frame_ops->write_v_block ((writer), (num), (val))
+
+extern int trace_regblock_size;
+
+/* 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;
+ 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
@@ -3081,22 +3314,7 @@ trace_save (const char *filename, int target_does_save)
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);
- }
+ writer->ops->write_uploaded_tsv (writer, utsv);
free_uploaded_tsvs (&uploaded_tsvs);
@@ -3106,76 +3324,155 @@ trace_save (const char *filename, int target_does_save)
target_get_tracepoint_status (NULL, utp);
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; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
- fprintf (fp, "tp A%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
- fprintf (fp, "tp S%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- if (utp->at_string)
- {
- encode_source_string (utp->number, utp->addr,
- "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- if (utp->cond_string)
- {
- encode_source_string (utp->number, utp->addr,
- "cond", utp->cond_string,
- buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
- {
- encode_source_string (utp->number, utp->addr, "cmd", act,
- buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- fprintf (fp, "tp V%x:%s:%x:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->hit_count,
- phex_nz (utp->traceframe_usage,
- sizeof (utp->traceframe_usage)));
- }
+ writer->ops->write_uploaded_tp (writer, utp);
free_uploaded_tps (&uploaded_tps);
/* Mark the end of the definition section. */
- fprintf (fp, "\n");
+ writer->ops->write_definition_end (writer);
/* 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);
+ LONGEST gotten = 0;
+
+ if (writer->ops->write_raw_data != NULL)
+ {
+ gotten = target_get_raw_trace_data (buf, offset,
+ MAX_TRACE_UPLOAD);
+ if (gotten == 0)
+ break;
+ writer->ops->write_raw_data (writer, buf, gotten);
+ }
+ else
+ {
+ uint16_t tp_num;
+ uint32_t tf_size;
+ unsigned int block;
+
+ /* Read the first six bytes in, which is the tracepoint
+ number and trace frame size. */
+ gotten = target_get_raw_trace_data (buf, offset, 6);
+
+ if (gotten == 0)
+ break;
+ tp_num = (uint16_t)
+ extract_unsigned_integer (&buf[0], 2, byte_order);
+
+ tf_size = (uint32_t)
+ extract_unsigned_integer (&buf[2], 4, byte_order);
+
+ /* Skip this trace frame if it is too big. */
+ if (tf_size > MAX_TRACE_UPLOAD)
+ {
+ offset += 6 + tf_size;
+ warning (_("Skip this trace frame because its size (%d)"
+ " is greater than the size of GDB internal"
+ " buffer (%d)"),
+ tf_size, MAX_TRACE_UPLOAD);
+ continue;
+ }
+
+ writer->ops->frame_ops->start (writer, tp_num);
+ gotten = 6;
+
+ if (tf_size > 0)
+ {
+ offset += 6;
+ gotten = target_get_raw_trace_data (buf, offset,
+ tf_size);
+ /* GDB gets to know the size of the trace frame, so GDB
+ should get them. */
+ if (gotten < tf_size)
+ error (_("The target gets incomplete trace data "
+ "(%lld bytes) in one trace frame "
+ "(%d bytes)."), gotten, tf_size);
+
+ gotten = tf_size;
+
+ for (block = 0; block < tf_size; )
+ {
+ gdb_byte block_type = buf[block++];
+
+ switch (block_type)
+ {
+ case 'R':
+ TRACE_WRITE_R_BLOCK (writer, &buf[block],
+ trace_regblock_size);
+ block += trace_regblock_size;
+ break;
+ case 'M':
+ {
+ unsigned short mlen;
+ ULONGEST addr;
+
+ addr = (ULONGEST)
+ extract_unsigned_integer (&buf[block], 8,
+ byte_order);
+ block += 8;
+ mlen = (unsigned short)
+ extract_unsigned_integer (&buf[block], 2,
+ byte_order);
+
+ block += 2;
+ TRACE_WRITE_M_BLOCK (writer, addr,
+ &buf[block], mlen);
+
+ block += mlen;
+ break;
+ }
+ case 'V':
+ {
+ int vnum
+ = (int) extract_signed_integer (&buf[block],
+ 4,
+ byte_order);
+ LONGEST val
+ = extract_signed_integer (&buf[block + 4],
+ 8,
+ byte_order);
+
+ block += (4 + 8);
+ TRACE_WRITE_V_BLOCK (writer, vnum, val);
+ }
+ break;
+ default:
+ error (_("Unknown block type '%c' (0x%x) in"
+ " trace frame"),
+ block_type, block_type);
+ }
+ }
+ }
+
+ writer->ops->frame_ops->end (writer);
+ }
+
if (gotten < 0)
error (_("Failure to get requested trace buffer data"));
/* No more data is forthcoming, we're done. */
if (gotten == 0)
break;
- written = fwrite (buf, gotten, 1, fp);
- if (written < 1)
- perror_with_name (pathname);
offset += gotten;
}
- /* Mark the end of trace data. (We know that gotten is 0 at this point.) */
- written = fwrite (&gotten, 4, 1, fp);
- if (written < 1)
- perror_with_name (pathname);
+ writer->ops->end (writer);
+}
+
+/* Return a trace writer for TFILE format. */
+
+static struct trace_file_writer *
+tfile_trace_file_writer_new (void)
+{
+ struct tfile_trace_file_writer *writer
+ = xmalloc (sizeof (struct tfile_trace_file_writer));
+
+ writer->base.ops = &tfile_write_ops;
+ writer->fp = NULL;
+ writer->pathname = NULL;
- do_cleanups (cleanup);
+ return (struct trace_file_writer *) writer;
}
static void
@@ -3185,6 +3482,7 @@ trace_save_command (char *args, int from_tty)
char **argv;
char *filename = NULL;
struct cleanup *back_to;
+ struct trace_file_writer *writer = NULL;
if (args == NULL)
error_no_arg (_("file in which to save trace data"));
@@ -3205,7 +3503,11 @@ trace_save_command (char *args, int from_tty)
if (!filename)
error_no_arg (_("file in which to save trace data"));
- trace_save (filename, target_does_save);
+ 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 file '%s'.\n"), filename);
@@ -3213,6 +3515,21 @@ trace_save_command (char *args, int from_tty)
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 ();
+ trace_save (filename, writer, target_does_save);
+
+ back_to = make_cleanup (trace_file_writer_xfree, writer);
+ do_cleanups (back_to);
+}
+
/* Tell the target what to do with an ongoing tracing run if GDB
disconnects for some reason. */
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index b2b9d7c..e48e743 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -205,6 +205,93 @@ struct static_tracepoint_marker
char *extra;
};
+struct trace_file_writer;
+
+/* Operations to write trace frames to a specific trace format. */
+
+struct trace_frame_write_ops
+{
+ /* Write a new trace frame. The tracepoint number of this trace
+ frame is TPNUM. */
+ void (*start) (struct trace_file_writer *self, uint16_t tpnum);
+
+ /* Write an 'R' block. Buffer BUF contains its contents and SIZE is
+ its size. */
+ void (*write_r_block) (struct trace_file_writer *self,
+ gdb_byte *buf, int size);
+
+ /* Write an 'M' block. Buffer BUF contains its contents and LENGTH
+ is its length. ADDR is the start address of collected
+ memory. */
+ void (*write_m_block) (struct trace_file_writer *self,
+ ULONGEST addr, gdb_byte *buf,
+ uint16_t length);
+
+ /* Write a 'V' block. NUM is the trace variable number and VAL is
+ the value of the trace variable. */
+ void (*write_v_block) (struct trace_file_writer *self, int num,
+ LONGEST val);
+
+ /* The end of the trace frame. */
+ void (*end) (struct trace_file_writer *self);
+};
+
+/* Operations to write trace buffers to a specific trace format. */
+
+struct trace_file_write_ops
+{
+ /* Save the data to file or directory NAME of desired format in
+ target side. Return true for success, otherwise return
+ false. */
+ int (*target_save) (struct trace_file_writer *self,
+ const char *name);
+
+ /* Write the trace buffers to file or directory NAME. */
+ void (*start) (struct trace_file_writer *self,
+ const char *name);
+
+ /* Write the trace header. */
+ void (*write_header) (struct trace_file_writer *self);
+
+ /* Write the type of block about registers. SIZE is the size of
+ all registers on the target. */
+ void (*write_regblock_type) (struct trace_file_writer *self,
+ int size);
+
+ /* Write trace status TS. */
+ void (*write_status) (struct trace_file_writer *self,
+ struct trace_status *ts);
+
+ /* Write the uploaded TSV. */
+ void (*write_uploaded_tsv) (struct trace_file_writer *self,
+ struct uploaded_tsv *tsv);
+
+ /* Write the uploaded tracepoint TP. */
+ void (*write_uploaded_tp) (struct trace_file_writer *self,
+ struct uploaded_tp *tp);
+
+ /* Write to mark the definition part is end. */
+ void (*write_definition_end) (struct trace_file_writer *self);
+
+ /* Write the raw data of trace buffer. The content is in BUF and
+ length is LEN. */
+ void (*write_raw_data) (struct trace_file_writer *self,
+ gdb_byte *buf, LONGEST len);
+
+ /* Operations to write trace frames. */
+ struct trace_frame_write_ops *frame_ops;
+
+ /* The end of writing trace buffers. */
+ void (*end) (struct trace_file_writer *self);
+};
+
+/* Trace file writer for a given format. */
+
+struct trace_file_writer
+{
+ struct trace_file_write_ops *ops;
+};
+
extern void parse_static_tracepoint_marker_definition
(char *line, char **pp,
struct static_tracepoint_marker *marker);
@@ -281,7 +368,8 @@ extern void tfind_1 (enum trace_find_type type, int num,
ULONGEST addr1, ULONGEST addr2,
int from_tty);
-extern void trace_save (const char *filename, int target_does_save);
+extern void trace_save_tfile (const char *filename,
+ int target_does_save);
extern struct traceframe_info *parse_traceframe_info (const char *tframe_info);
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 8:58 ` Yao Qi
@ 2013-03-01 10:05 ` Pedro Alves
2013-03-01 10:41 ` Pedro Alves
` (2 more replies)
0 siblings, 3 replies; 37+ messages in thread
From: Pedro Alves @ 2013-03-01 10:05 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 03/01/2013 08:57 AM, Yao Qi wrote:
> Hi Pedro,
> thanks for the quick review. The updated patch address most of your comments except some I'd like to discuss with you below,
>
> On 03/01/2013 12:33 AM, Pedro Alves wrote:
>> Ok, I think I got it.
>>
>> I think an important missing clue here is that "raw" in
>> "write_raw_data" and target_get_raw_trace_data actually means
>> frames in "tfile" format, right? "ops->write_raw_data" being
>> NULL means that the writer can't handle parsing the "tfile"
>> frames itself.
>>
>> "raw" here is almost meaningless to the reader -- surely "raw" in
>> the ctf context could mean raw ctf "something".
>> Maybe "raw" should really be s/raw/tfile/ or s/raw/raw_tfile/ ?
>>
>
> "raw" here means the data in the trace buffers, and "raw data" has few to
> do with the file format.
And what is the format of that "raw" data? Exactly the format
described in the manual, in the "Trace File Format" node:
"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."
So it's not really "raw" binary data -- there's a format, with
headers and all. And it can't be any other format, otherwise
the patch's design won't work... (**)
>
> TFILE is a format that composed by ascii definition part and trace frames dumped from the raw data directly. There could be another trace file format FOO that stores raw data as well.
It's not "raw". It's trace frames in gdb's trace file format
as defined in the manual.
>
>>> >+ {
>>> >+ uint16_t tp_num;
>>> >+ uint32_t tf_size;
>>> >+ unsigned int read_length;
>>> >+ unsigned int block;
>>> >+
>>> >+ /* Read the first six bytes in, which is the tracepoint
>>> >+ number and trace frame size. */
>>> >+ gotten = target_get_raw_trace_data (buf, offset, 6);
>>> >+
>>> >+ if (gotten == 0)
>>> >+ break;
>>> >+ tp_num = (uint16_t)
>>> >+ extract_unsigned_integer (&buf[0], 2, byte_order);
>>> >+
>>> >+ tf_size = (uint32_t)
>>> >+ extract_unsigned_integer (&buf[2], 4, byte_order);
(**) ... as can be seen here and below with
+ switch (block_type)
+ {
+ case 'R':
...
+ case 'M':
...
etc.
This is parsing the "raw"'s headers, in gdb's trace file format.
This as the else branch. The "then" branch only works if the target
can interpret "buf" as trace frames in gdb's trace file format.
>>> >+
>>> >+ writer->ops->frame_ops->start (writer, tp_num);
>>> >+ gotten = 6;
>>> >+
>>> >+ if (tf_size <= MAX_TRACE_UPLOAD)
>>> >+ read_length = tf_size;
>>> >+ else
>>> >+ {
>>> >+ read_length = MAX_TRACE_UPLOAD;
>>> >+ gdb_assert (0);
>> Leftover? I guess this means you didn't try with a big trace
>> frame?
>>
>
> I change this part that GDB will skip the frame if it is too big, and print a warning, like this:
>
> warning: Skip this trace frame because its size (708) is greater than the size of GDB internal buffer (200)
>
> I set MAX_TRACE_UPLOAD to 200, and run the testsuite. The CTF file is still correct, but some frames are lost.
Ugh. Why do we have this limitation at all?
target_get_raw_trace_data works with an offset and size, so
I don't even see why we need to fetch the whole trace frame
in one go.
>
>>> >+ }
>>> >+
>>> >+ /* Write to mark the definition part is end. */
>> This doesn't parse correctly. Something like:
>>
>> /* Called to signal the end of the definition part. */
>>
>> perhaps.
>>
>
> Is it incorrect?
I think so.
> The basic sentence pattern is "write (an intransitive verb) to do sth.", and "[that] the definition part is end" is a noun clause.
"to signal" is not intransitive. But I don't think an
intransitive verb is what you wanted anyway.
"is end" isn't correct.
"is over", or "is done" or "is/has finished" or "has ended"
would be good. By my order of preference:
"Write to mark the end of the definition part."
"Write to mark the definition part's end."
"Write to mark the definition part has ended."
Though "Write to mark" still sounds a little odd to me.
It sounds to me like:
"Write (anything at all you wish, unrelated to the
definition part having ended. Perhaps a poem.)
as celebration to mark the annual event that the
definition part has ended. :-)
But one of the above is good enough for me.
Unless a native speaker chimes in, let's leave it at that.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 10:05 ` Pedro Alves
@ 2013-03-01 10:41 ` Pedro Alves
2013-03-01 11:26 ` Yao Qi
2013-03-03 8:45 ` Yao Qi
2 siblings, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2013-03-01 10:41 UTC (permalink / raw)
To: Pedro Alves; +Cc: Yao Qi, gdb-patches
On 03/01/2013 10:05 AM, Pedro Alves wrote:
>
> "to signal" is not intransitive. But I don't think an
> intransitive verb is what you wanted anyway.
On second though I think I got confused in this claim.
Sorry about that. Anyway, the rest still stands.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 10:05 ` Pedro Alves
2013-03-01 10:41 ` Pedro Alves
@ 2013-03-01 11:26 ` Yao Qi
2013-03-01 12:13 ` Pedro Alves
2013-03-03 8:45 ` Yao Qi
2 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-03-01 11:26 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On 03/01/2013 06:05 PM, Pedro Alves wrote:
> And what is the format of that "raw" data? Exactly the format
"raw data" doesn't mean format-less data. The format here is how data
are organized in trace *buffer*. Data stored in trace buffer are
treated as "raw".
> described in the manual, in the "Trace File Format" node:
>
> "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."
Yes, this is only about TFILE format, instead of how data are stored in
trace buffer, which is GDB internal.
>
> So it's not really "raw" binary data -- there's a format, with
> headers and all. And it can't be any other format, otherwise
> the patch's design won't work... (**)
>
I never say "raw data is binary data or raw data is format-less data".
My point is raw data has its own format, and GDB is free to store them
directly by write_raw_data or parse them (according to how data are
stored in trace buffers) first and store them in some
format (CTF and even TFILE).
>> >
>> >TFILE is a format that composed by ascii definition part and trace frames dumped from the raw data directly. There could be another trace file format FOO that stores raw data as well.
> It's not "raw". It's trace frames in gdb's trace file format
> as defined in the manual.
>
It is just because TFILE format use the same format that data are
stored in trace buffers. One day, we can use another way to organize
the trace buffers, but still output TFILE format in default. For
example, we may add checksum for each block in trace buffers in
GDBserver in the future, but still generate TFILE format trace file in
GDB side. When we get raw data (with checksum in each block) from the
remote target, can we call them "tfile format data"?. Another example
is GDBserver can send the format of trace buffers to GDB first via xml,
and GDB can parse the data got from GDBserver according the xml
description, and save them into CTF or TFILE format. We can't call the
data from the remote target "tfile format data".
>> >
>>>>> >>> >+ {
>>>>> >>> >+ uint16_t tp_num;
>>>>> >>> >+ uint32_t tf_size;
>>>>> >>> >+ unsigned int read_length;
>>>>> >>> >+ unsigned int block;
>>>>> >>> >+
>>>>> >>> >+ /* Read the first six bytes in, which is the tracepoint
>>>>> >>> >+ number and trace frame size. */
>>>>> >>> >+ gotten = target_get_raw_trace_data (buf, offset, 6);
>>>>> >>> >+
>>>>> >>> >+ if (gotten == 0)
>>>>> >>> >+ break;
>>>>> >>> >+ tp_num = (uint16_t)
>>>>> >>> >+ extract_unsigned_integer (&buf[0], 2, byte_order);
>>>>> >>> >+
>>>>> >>> >+ tf_size = (uint32_t)
>>>>> >>> >+ extract_unsigned_integer (&buf[2], 4, byte_order);
> (**) ... as can be seen here and below with
>
> + switch (block_type)
> + {
> + case 'R':
> ...
> + case 'M':
> ...
>
> etc.
>
> This is parsing the "raw"'s headers, in gdb's trace file format.
> This as the else branch. The "then" branch only works if the target
> can interpret "buf" as trace frames in gdb's trace file format.
>
Again, it is not trace file format. It is how data are stored in trace
buffers.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 11:26 ` Yao Qi
@ 2013-03-01 12:13 ` Pedro Alves
2013-03-01 15:55 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2013-03-01 12:13 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 03/01/2013 11:25 AM, Yao Qi wrote:
> On 03/01/2013 06:05 PM, Pedro Alves wrote:
>> And what is the format of that "raw" data? Exactly the format
>
> "raw data" doesn't mean format-less data. The format here is how data are organized in trace *buffer*. Data stored in trace buffer are treated as "raw".
I'm about as familiar with gdb's tracing code as they get, and
I _still_ got confused with reading this part of the patch. I'd
think that'd indicate something wasn't as clear as possible.
>
>> described in the manual, in the "Trace File Format" node:
>>
>> "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."
>
> Yes, this is only about TFILE format, instead of how data are stored in trace buffer, which is GDB internal.
Huh. No. Really. See:
@item qTBuffer:@var{offset},@var{len}
@cindex @samp{qTBuffer} packet
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.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It's not any random internal detail at all. It's a defined format.
>>
>> So it's not really "raw" binary data -- there's a format, with
>> headers and all. And it can't be any other format, otherwise
>> the patch's design won't work... (**)
>>
>
> I never say "raw data is binary data or raw data is format-less data".
> My point is raw data has its own format, and GDB is free to store them
> directly by write_raw_data or parse them (according to how data are stored in trace buffers) first and store them in some
> format (CTF and even TFILE).
The patch clearly assumes the data stored in the trace buffers
is as described in the manual, in the "Trace File Format" node,
as I pointed out above. ("The trace frame section ...").
>
>>> >
>>> >TFILE is a format that composed by ascii definition part and trace frames dumped from the raw data directly. There could be another trace file format FOO that stores raw data as well.
>> It's not "raw". It's trace frames in gdb's trace file format
>> as defined in the manual.
>>
>
> It is just because TFILE format use the same format that data are
> stored in trace buffers. One day, we can use another way to organize
> the trace buffers, but still output TFILE format in default. For example, we may
> add checksum for each block in trace buffers in GDBserver in the future,
> but still generate TFILE format trace file in GDB side. When we get raw
> data (with checksum in each block) from the remote target, can we call
> them "tfile format data"?. Another example is GDBserver can send the format
> of trace buffers to GDB first via xml, and GDB can parse the data got
> from GDBserver according the xml description, and save them into CTF or
> TFILE format. We can't call the data from the remote target "tfile format data".
At the point there's more than one format that GDB can fetch from the
target, then the code that fetches it will need to know what format it is
getting. Say, nothing would change in the patch, then ...
LONGEST gotten = 0;
if (writer->ops->write_raw_data != NULL)
{
gotten = target_get_raw_trace_data (buf, offset,
MAX_TRACE_UPLOAD);
^^^^^^^^^^^^^^^^^^^^^^^^
say this sometimes returns XML.
if (gotten == 0)
break;
writer->ops->write_raw_data (writer, buf, gotten);
^^^^^^^^^^^^^^
what format is the writer getting? How can it tell?
}
... this leaves gdb or the writer with no idea what "kind"
of "raw" data it is getting. It may not understand or
want XML at all!
Clearly, more code will need to be added here to handle
the multiple types, and the possibility that the format
we get from the target is not the same as the writer wants.
Maybe we'll have then:
/* See if we can pass down raw trace buffer trace frames
directly, as per FOO's trace file format, if both the target
and the writer support that format. */
if (writer->ops->write_raw_FOO_data != NULL)
{
gotten = target_get_raw_FOO_trace_data (buf, offset,
MAX_TRACE_UPLOAD);
if (gotten == 0)
break;
writer->ops->write_raw_FOO_data (writer, buf, gotten);
}
/* Nope. See if we can pass down raw trace buffer trace frames
directly, as per GDB's trace file format, if both the target
and the writer support that format. */
else if (writer->ops->write_raw_data != NULL)
{
gotten = target_get_raw_trace_data (buf, offset,
MAX_TRACE_UPLOAD);
say this returns XML.
if (gotten == 0)
break;
writer->ops->write_raw_data (writer, buf, gotten);
}
/* Nope. We'll need to do trace buffer format conversion. Try
format foo first, as it is more complete. */
else
{
... if possible, fetch data from the target in foo format,
parse it, and call writer hooks with the pieces ...
...
... else if possible, fetch frame data from the target in gdb
trace file format, parse it, and call writer hooks
with the pieces ...
}
See, the comments should make all this much clearer. That's really
all I'm asking -- to make this bit of code a bit clearer.
>
>>> >
>>>>>> >>> >+ {
>>>>>> >>> >+ uint16_t tp_num;
>>>>>> >>> >+ uint32_t tf_size;
>>>>>> >>> >+ unsigned int read_length;
>>>>>> >>> >+ unsigned int block;
>>>>>> >>> >+
>>>>>> >>> >+ /* Read the first six bytes in, which is the tracepoint
>>>>>> >>> >+ number and trace frame size. */
>>>>>> >>> >+ gotten = target_get_raw_trace_data (buf, offset, 6);
>>>>>> >>> >+
>>>>>> >>> >+ if (gotten == 0)
>>>>>> >>> >+ break;
>>>>>> >>> >+ tp_num = (uint16_t)
>>>>>> >>> >+ extract_unsigned_integer (&buf[0], 2, byte_order);
>>>>>> >>> >+
>>>>>> >>> >+ tf_size = (uint32_t)
>>>>>> >>> >+ extract_unsigned_integer (&buf[2], 4, byte_order);
>> (**) ... as can be seen here and below with
>>
>> + switch (block_type)
>> + {
>> + case 'R':
>> ...
>> + case 'M':
>> ...
>>
>> etc.
>>
>> This is parsing the "raw"'s headers, in gdb's trace file format.
>> This as the else branch. The "then" branch only works if the target
>> can interpret "buf" as trace frames in gdb's trace file format.
>>
> Again, it is not trace file format.
Again, it is.
> It is how data are stored in trace buffers.
It is, because the the target passes the trace buffers in
trace file format back to gdb. gdbserver uses the same format
natively, for its own buffers, as naturally this avoid any
conversion.
(actually, historically, the tfile format was designed as
reusing gdbserver's, but anyway, same difference).
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 12:13 ` Pedro Alves
@ 2013-03-01 15:55 ` Yao Qi
2013-03-05 20:59 ` Pedro Alves
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-03-01 15:55 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On 03/01/2013 08:13 PM, Pedro Alves wrote:
>> I never say "raw data is binary data or raw data is format-less data".
>> >My point is raw data has its own format, and GDB is free to store them
>> >directly by write_raw_data or parse them (according to how data are stored in trace buffers) first and store them in some
>> >format (CTF and even TFILE).
> The patch clearly assumes the data stored in the trace buffers
Yes, I read some gdbserver code when writing this patch.
> is as described in the manual, in the "Trace File Format" node,
> as I pointed out above. ("The trace frame section ...").
>
No, the "Trace File Format" node is about trace file format, not trace
buffer format. Two formats are the same, but can be different.
>> >
>>>>> >>> >
>>>>> >>> >TFILE is a format that composed by ascii definition part and trace frames dumped from the raw data directly. There could be another trace file format FOO that stores raw data as well.
>>> >>It's not "raw". It's trace frames in gdb's trace file format
>>> >>as defined in the manual.
>>> >>
>> >
>> >It is just because TFILE format use the same format that data are
>> >stored in trace buffers. One day, we can use another way to organize
>> >the trace buffers, but still output TFILE format in default. For example, we may
>> >add checksum for each block in trace buffers in GDBserver in the future,
>> >but still generate TFILE format trace file in GDB side. When we get raw
>> >data (with checksum in each block) from the remote target, can we call
>> >them "tfile format data"?. Another example is GDBserver can send the format
>> >of trace buffers to GDB first via xml, and GDB can parse the data got
>> >from GDBserver according the xml description, and save them into CTF or
>> >TFILE format. We can't call the data from the remote target "tfile format data".
> At the point there's more than one format that GDB can fetch from the
> target, then the code that fetches it will need to know what format it is
> getting. Say, nothing would change in the patch, then ...
>
> LONGEST gotten = 0;
>
> if (writer->ops->write_raw_data != NULL)
> {
> gotten = target_get_raw_trace_data (buf, offset,
> MAX_TRACE_UPLOAD);
> ^^^^^^^^^^^^^^^^^^^^^^^^
> say this sometimes returns XML.
> if (gotten == 0)
> break;
> writer->ops->write_raw_data (writer, buf, gotten);
> ^^^^^^^^^^^^^^
>
> what format is the writer getting? How can it tell?
When GDB connects to GDBserver, GDB can get an XML file to describe the
format of trace buffers, we call it 'trace buffer description', similar
to target description we are using to describe registers. Then, GDB is
able to generate the corresponding writer according the XML file, and
use the write to write trace file.
target_get_raw_trace_data doesn't have to know the format, because it is
transferring memory from the target to GDB.
>
> }
>
> ... this leaves gdb or the writer with no idea what "kind"
> of "raw" data it is getting. It may not understand or
> want XML at all!
>
> Clearly, more code will need to be added here to handle
> the multiple types, and the possibility that the format
> we get from the target is not the same as the writer wants.
>
> Maybe we'll have then:
>
> /* See if we can pass down raw trace buffer trace frames
> directly, as per FOO's trace file format, if both the target
> and the writer support that format. */
> if (writer->ops->write_raw_FOO_data != NULL)
> {
> gotten = target_get_raw_FOO_trace_data (buf, offset,
> MAX_TRACE_UPLOAD);
> if (gotten == 0)
> break;
> writer->ops->write_raw_FOO_data (writer, buf, gotten);
> }
>
> /* Nope. See if we can pass down raw trace buffer trace frames
> directly, as per GDB's trace file format, if both the target
> and the writer support that format. */
> else if (writer->ops->write_raw_data != NULL)
> {
> gotten = target_get_raw_trace_data (buf, offset,
> MAX_TRACE_UPLOAD);
> say this returns XML.
> if (gotten == 0)
> break;
> writer->ops->write_raw_data (writer, buf, gotten);
> }
>
> /* Nope. We'll need to do trace buffer format conversion. Try
> format foo first, as it is more complete. */
> else
> {
>
> ... if possible, fetch data from the target in foo format,
> parse it, and call writer hooks with the pieces ...
>
> ...
>
> ... else if possible, fetch frame data from the target in gdb
> trace file format, parse it, and call writer hooks
> with the pieces ...
> }
>
> See, the comments should make all this much clearer. That's really
> all I'm asking -- to make this bit of code a bit clearer.
It doesn't look right to me, because you are mixing up 'trace file
writer' together with 'trace buffer parser'. GDB can support multiple
'trace buffer parser', and uses one selected parser to parse the data
get from target_get_raw_trace_data. GDB can also support multiple
'trace file writer', and uses one selected writer to a specific trace
file format. Before these patches, GDB has one writer (tfile writer)
and one parser, and writer and parser is hard-coded into GDB source.
After these patches, GDB will have one parser and two writers (tfile
writer and ctf writer). The parser should be determined by the reply
from the remote target, and the writer should be determined by the
command setting.
If writer supports "write_raw_data", the parser won't parse the data at
all, and call "write_raw_data" to write data to disk directly. If the
writer doesn't supports "write_raw_data", the parser has to parse the
data, break data into pieces, and feed the small piece to writer.
>
>> >
>>>>> >>> >
>>>>>>>>>>> >>>>>> >>> >+ {
>>>>>>>>>>> >>>>>> >>> >+ uint16_t tp_num;
>>>>>>>>>>> >>>>>> >>> >+ uint32_t tf_size;
>>>>>>>>>>> >>>>>> >>> >+ unsigned int read_length;
>>>>>>>>>>> >>>>>> >>> >+ unsigned int block;
>>>>>>>>>>> >>>>>> >>> >+
>>>>>>>>>>> >>>>>> >>> >+ /* Read the first six bytes in, which is the tracepoint
>>>>>>>>>>> >>>>>> >>> >+ number and trace frame size. */
>>>>>>>>>>> >>>>>> >>> >+ gotten = target_get_raw_trace_data (buf, offset, 6);
>>>>>>>>>>> >>>>>> >>> >+
>>>>>>>>>>> >>>>>> >>> >+ if (gotten == 0)
>>>>>>>>>>> >>>>>> >>> >+ break;
>>>>>>>>>>> >>>>>> >>> >+ tp_num = (uint16_t)
>>>>>>>>>>> >>>>>> >>> >+ extract_unsigned_integer (&buf[0], 2, byte_order);
>>>>>>>>>>> >>>>>> >>> >+
>>>>>>>>>>> >>>>>> >>> >+ tf_size = (uint32_t)
>>>>>>>>>>> >>>>>> >>> >+ extract_unsigned_integer (&buf[2], 4, byte_order);
>>> >>(**) ... as can be seen here and below with
>>> >>
>>> >>+ switch (block_type)
>>> >>+ {
>>> >>+ case 'R':
>>> >>...
>>> >>+ case 'M':
>>> >>...
>>> >>
>>> >>etc.
>>> >>
>>> >>This is parsing the "raw"'s headers, in gdb's trace file format.
>>> >>This as the else branch. The "then" branch only works if the target
>>> >>can interpret "buf" as trace frames in gdb's trace file format.
>>> >>
>> >Again, it is not trace file format.
> Again, it is.
>
>> >It is how data are stored in trace buffers.
> It is, because the the target passes the trace buffers in
> trace file format back to gdb. gdbserver uses the same format
> natively, for its own buffers, as naturally this avoid any
> conversion.
>
> (actually, historically, the tfile format was designed as
> reusing gdbserver's, but anyway, same difference).
The term "raw data" makes trouble here, so are you happy if I write the
code in this way?
struct trace_file_write_ops
{
...
/* Write the data of trace buffer without parsing. The content is
in BUF and length is LEN. */
void (*write_trace_buffer) (struct trace_file_writer *self,
gdb_byte *buf, LONGEST len);
/* Operations to write trace frames. The user of this field is
responsible to parse the data of trace buffer. Either field
'write_trace_buffer' or field ' frame_ops' is NULL. */
struct trace_frame_write_ops *frame_ops;
...
};
in trace_save,
/* The writer supports writing contents of trace buffer directly
to trace file. Don't parse the contents of trace buffer. */
if (writer->ops->write_trace_buffer != NULL)
{
gotten = target_get_raw_trace_data (buf, offset,
MAX_TRACE_UPLOAD);
if (gotten == 0)
break;
writer->ops->write_trace_buffer (writer, buf, gotten);
}
else
{
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 15:55 ` Yao Qi
@ 2013-03-05 20:59 ` Pedro Alves
0 siblings, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2013-03-05 20:59 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 03/01/2013 03:55 PM, Yao Qi wrote:
> On 03/01/2013 08:13 PM, Pedro Alves wrote:
>>> I never say "raw data is binary data or raw data is format-less data".
>>> >My point is raw data has its own format, and GDB is free to store them
>>> >directly by write_raw_data or parse them (according to how data are stored in trace buffers) first and store them in some
>>> >format (CTF and even TFILE).
>> The patch clearly assumes the data stored in the trace buffers
>
> Yes, I read some gdbserver code when writing this patch.
>
>> is as described in the manual, in the "Trace File Format" node,
>> as I pointed out above. ("The trace frame section ...").
>>
>
> No, the "Trace File Format" node is about trace file format, not trace
> buffer format. Two formats are the same, but can be different.
But they are not at present. If some future change changes this,
let's discuss it then. target_get_raw_trace_data calls qTBuffer,
which, as documented, sends data in the format described in the
trace file format node in the manual. That's it. Some comments
around the target_get_raw_trace_data/write->ops->write_raw_data
calls indicating that would have made the code much clearer
for me at first glance. Not so anymore of course, since
I've now spent a long time discussing and staring at the code.
Sorry, I don't know how to better explain it. This argument
is going nowhere. Let's move on, and I'll see about adding
some comments afterwards.
>
>>> >
>>>>>> >>> >
>>>>>> >>> >TFILE is a format that composed by ascii definition part and trace frames dumped from the raw data directly. There could be another trace file format FOO that stores raw data as well.
>>>> >>It's not "raw". It's trace frames in gdb's trace file format
>>>> >>as defined in the manual.
>>>> >>
>>> >
>>> >It is just because TFILE format use the same format that data are
>>> >stored in trace buffers. One day, we can use another way to organize
>>> >the trace buffers, but still output TFILE format in default. For example, we may
>>> >add checksum for each block in trace buffers in GDBserver in the future,
>>> >but still generate TFILE format trace file in GDB side. When we get raw
>>> >data (with checksum in each block) from the remote target, can we call
>>> >them "tfile format data"?. Another example is GDBserver can send the format
>>> >of trace buffers to GDB first via xml, and GDB can parse the data got
>>> >from GDBserver according the xml description, and save them into CTF or
>>> >TFILE format. We can't call the data from the remote target "tfile format data".
>> At the point there's more than one format that GDB can fetch from the
>> target, then the code that fetches it will need to know what format it is
>> getting. Say, nothing would change in the patch, then ...
>>
>> LONGEST gotten = 0;
>>
>> if (writer->ops->write_raw_data != NULL)
>> {
>> gotten = target_get_raw_trace_data (buf, offset,
>> MAX_TRACE_UPLOAD);
>> ^^^^^^^^^^^^^^^^^^^^^^^^
>> say this sometimes returns XML.
>> if (gotten == 0)
>> break;
>> writer->ops->write_raw_data (writer, buf, gotten);
>> ^^^^^^^^^^^^^^
>>
>> what format is the writer getting? How can it tell?
>
> When GDB connects to GDBserver, GDB can get an XML file to describe the format of trace buffers, we call it 'trace buffer description', similar to target description we are using to describe registers. Then, GDB is able to generate the corresponding writer according the XML file, and use the write to write trace file.
>
> target_get_raw_trace_data doesn't have to know the format, because it is transferring memory from the target to GDB.
So the target sends a different buffer format to gdb,
using the same qTBuffer packet? How will older GDB's
cope? The packet is explicitly defined as returning
a buffer "as per the trace file format". I'd imagine
that GDB itself will need to explicitly request the
data in the format it knows the target supports.
So again (the example code above showed this example),
if the XML file describes a format called FOO, and the
writer is CTF, how can
if (writer->ops->write_raw_data != NULL)
{
gotten = target_get_raw_trace_data (buf, offset,
MAX_TRACE_UPLOAD);
if (gotten == 0)
break;
writer->ops->write_raw_data (writer, buf, gotten);
work? What's there in the design to convert the raw FOO to
raw CTF?
(Ignoring the old-gdb compat issue, ) I could see something like:
if (writer->ops->write_raw_data != NULL
+ && (target_raw_trace_data_format ()
+ == writer->ops->raw_trace_data_format ()))
{
gotten = target_get_raw_trace_data (buf, offset,
MAX_TRACE_UPLOAD);
if (gotten == 0)
break;
writer->ops->write_raw_data (writer, buf, gotten);
}
else
{
+ if (target_raw_trace_format () == FOO)
+ {
+ /* parse in FOO format and push frame pieces to writer */
+ gotten = target_get_raw_trace_data (buf, ...);
+ ...
+ }
else if (target_raw_trace_format () == TFILE)
{
/* The target's frame buffer is in GDB's trace file
frame format. */
// The existing fallback code that parses tfile-format buffer
// goes here.
}
}
But we're not there yet. Adding some comments around
/* target_get_raw_trace_data returns a frame buffer in
GDB's trace file frame format. */
near this code is all I was asking, really. If in future this
is no longer true, then let's discuss it in the future.
>> See, the comments should make all this much clearer. That's really
>> all I'm asking -- to make this bit of code a bit clearer.
>
> It doesn't look right to me, because you are mixing up 'trace file writer'
> together with 'trace buffer parser'. GDB can support multiple 'trace buffer parser',
> and uses one selected parser to parse the data get from target_get_raw_trace_data.
> GDB can also support multiple 'trace file writer', and uses one selected writer
> to a specific trace file format. Before these patches, GDB has one writer (tfile
> writer) and one parser, and writer and parser is hard-coded into GDB source. After
> these patches, GDB will have one parser and two writers (tfile writer and ctf writer).
> The parser should be determined by the reply from the remote target, and the writer
> should be determined by the command setting.
No, I'm not mixing it up. I'm just showing a design based on
different target_ops and remote protocol entry points for each
format to be returned by the remote side.
In the snippet I posted, just remove the else, and gdb would
fall through trying all formats. Or replace different entry
points with some enumeration identifying the format. It ends
up being the same.
>
> If writer supports "write_raw_data", the parser won't parse the data at all,
> and call "write_raw_data" to write data to disk directly. If the writer doesn't
> supports "write_raw_data", the parser has to parse the data, break data into
> pieces, and feed the small piece to writer.
Sure. It's the parser vs writer format mismatch that I was
concerned about.
>>
>>> >It is how data are stored in trace buffers.
>> It is, because the the target passes the trace buffers in
>> trace file format back to gdb. gdbserver uses the same format
>> natively, for its own buffers, as naturally this avoid any
>> conversion.
>>
>> (actually, historically, the tfile format was designed as
>> reusing gdbserver's, but anyway, same difference).
>
> The term "raw data" makes trouble here, so are you happy if I write the code in this way?
No, I'm fine with "raw data" now, an actually prefer it over
that, as it matches the target method. Leave it as is, I'll
see about adding some comments afterwards.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 1/5] Refactor 'tsave'
2013-03-01 10:05 ` Pedro Alves
2013-03-01 10:41 ` Pedro Alves
2013-03-01 11:26 ` Yao Qi
@ 2013-03-03 8:45 ` Yao Qi
2 siblings, 0 replies; 37+ messages in thread
From: Yao Qi @ 2013-03-03 8:45 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1878 bytes --]
On 03/01/2013 06:05 PM, Pedro Alves wrote:
> Ugh. Why do we have this limitation at all?
> target_get_raw_trace_data works with an offset and size, so
> I don't even see why we need to fetch the whole trace frame
> in one go.
>
We can get the a fixed-length of data from the target, but we have face
the problem that a block may be broken into two pieces. So we have to
check the boundary, shift contents in buf, and fetch more data to
complete the block. This makes the code hard to read and maintain.
Finally, I decide to fetch data on a block level. That is, fetch one
byte first to determine the type, and then fetch the block as a whole.
M block is an exception here, because it can be very large, so I split
the API to write M block into two (for header and content respectively,
see more in the comments of this patch). In this way, GDB has more
round-trip with the target to get trace buffer data, but the code is
clean and able to handle large M block. I tested it by setting
MAX_TRACE_UPLOAD to 450, and cause multiple fetches to finish the whole
M block. It works well.
>> >
>>>>> >>> >+ }
>>>>> >>> >+
>
>>>>> >>> >+ /* Write to mark the definition part is end. */
>>> >>This doesn't parse correctly. Something like:
>>> >>
>>> >> /* Called to signal the end of the definition part. */
>>> >>
>>> >>perhaps.
>>> >>
>> >
>> >Is it incorrect?
> I think so.
>
>> >The basic sentence pattern is "write (an intransitive verb) to do sth.", and "[that] the definition part is end" is a noun clause.
> "to signal" is not intransitive. But I don't think an
> intransitive verb is what you wanted anyway.
>
> "is end" isn't correct.
>
> "is over", or "is done" or "is/has finished" or "has ended"
> would be good. By my order of preference:
>
> "Write to mark the end of the definition part."
I choose this one.
--
Yao (é½å°§)
[-- Attachment #2: 0001-Refactor-tsave.patch --]
[-- Type: text/x-patch, Size: 27788 bytes --]
gdb:
2013-03-03 Yao Qi <yao@codesourcery.com>
* tracepoint.c (trace_file_writer_xfree): New.
(struct tfile_writer_data): New.
(tfile_can_target_save, tfile_start): New.
(tfile_write_header, tfile_write_regblock_type): New.
(tfile_write_status, tfile_write_uploaded_tsv): New.
(tfile_write_uploaded_tp, tfile_write_definition_end): New.
(tfile_write_raw_data, (tfile_end): New.
(tfile_write_ops): New global variable.
(TRACE_WRITE_R_BLOCK): New macro.
(TRACE_WRITE_M_BLOCK_HEADER): New macro.
(TRACE_WRITE_M_BLOCK_MEMORY): New macro.
(TRACE_WRITE_V_BLOCK): New macro.
(trace_save): Add extra one parameter WRITER. Make it static.
Use WRITER to writer trace.
(tfile_trace_file_writer_new): New.
(trace_save_command): Caller update.
(trace_save_tfile): Write trace data in TFILE format.
* tracepoint.h (struct trace_frame_write_ops): New.
(struct trace_file_write_ops): New.
(struct trace_file_writer): New.
(trace_save): Remove its declaration.
(trace_save_tfile): Declare it.
* mi/mi-main.c (mi_cmd_trace_save): Call trace_save_tfile
instead of trace_save.
---
gdb/mi/mi-main.c | 2 +-
gdb/tracepoint.c | 628 +++++++++++++++++++++++++++++++++++++++++++-----------
gdb/tracepoint.h | 104 +++++++++-
3 files changed, 603 insertions(+), 131 deletions(-)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 37294e0..206b626 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -2495,7 +2495,7 @@ mi_cmd_trace_save (char *command, char **argv, int argc)
filename = argv[0];
}
- trace_save (filename, target_saves);
+ trace_save_tfile (filename, target_saves);
}
void
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 9a80aa3..eb93645 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -2983,90 +2983,326 @@ encode_source_string (int tpnum, ULONGEST addr,
return -1;
}
-extern int trace_regblock_size;
+/* Free trace file writer. */
-/* Save tracepoint data to file named FILENAME. 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_file_writer_xfree (void *arg)
+{
+ xfree (arg);
+}
-void
-trace_save (const char *filename, int target_does_save)
+/* TFILE trace writer. */
+
+struct tfile_trace_file_writer
{
- struct cleanup *cleanup;
- char *pathname;
- struct trace_status *ts = current_trace_status ();
- int err, status;
+ struct trace_file_writer base;
+
+ /* File pointer to tfile trace file. */
FILE *fp;
- struct uploaded_tp *uploaded_tps = NULL, *utp;
- struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
- int a;
- char *act;
- LONGEST gotten = 0;
- ULONGEST offset = 0;
-#define MAX_TRACE_UPLOAD 2000
- gdb_byte buf[MAX_TRACE_UPLOAD];
- int written;
+ /* Path name of the tfile trace file. */
+ char *pathname;
- /* 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;
- }
+ struct cleanup *cleanup;
+};
- /* 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);
+/* This is the implementation of trace_file_write_ops method
+ target_save. We just call the generic target
+ target_save_trace_data to do target-side saving. */
+
+static int
+tfile_target_save (struct trace_file_writer *self,
+ const char *filename)
+{
+ int err = target_save_trace_data (filename);
- pathname = tilde_expand (filename);
- cleanup = make_cleanup (xfree, pathname);
+ return (err >= 0);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ start. It creates the trace file FILENAME and registers some
+ cleanups. */
- fp = fopen (pathname, "wb");
- if (!fp)
+static void
+tfile_start (struct trace_file_writer *self, const char *filename)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ writer->pathname = tilde_expand (filename);
+ writer->cleanup = make_cleanup (xfree, writer->pathname);
+ writer->fp = fopen (writer->pathname, "wb");
+ if (writer->fp == NULL)
error (_("Unable to open file '%s' for saving trace data (%s)"),
filename, safe_strerror (errno));
- make_cleanup_fclose (fp);
+ make_cleanup_fclose (writer->fp);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_header. Write the TFILE header. */
+
+static void
+tfile_write_header (struct trace_file_writer *self)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+ int written;
/* Write a file header, with a high-bit-set char to indicate a
binary file, plus a hint as what this file is, and a version
number in case of future needs. */
- written = fwrite ("\x7fTRACE0\n", 8, 1, fp);
+ written = fwrite ("\x7fTRACE0\n", 8, 1, writer->fp);
if (written < 1)
- perror_with_name (pathname);
+ perror_with_name (writer->pathname);
+}
- /* Write descriptive info. */
+/* This is the implementation of trace_file_write_ops method
+ write_regblock_type. Write the size of register block. */
- /* Write out the size of a register block. */
- fprintf (fp, "R %x\n", trace_regblock_size);
+static void
+tfile_write_regblock_type (struct trace_file_writer *self, int size)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
- /* Write out status of the tracing run (aka "tstatus" info). */
- fprintf (fp, "status %c;%s",
+ fprintf (writer->fp, "R %x\n", size);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_status. */
+
+static void
+tfile_write_status (struct trace_file_writer *self,
+ struct trace_status *ts)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ fprintf (writer->fp, "status %c;%s",
(ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
if (ts->stop_reason == tracepoint_error)
{
char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
bin2hex ((gdb_byte *) ts->stop_desc, buf, 0);
- fprintf (fp, ":%s", buf);
+ fprintf (writer->fp, ":%s", buf);
}
- fprintf (fp, ":%x", ts->stopping_tracepoint);
+ fprintf (writer->fp, ":%x", ts->stopping_tracepoint);
if (ts->traceframe_count >= 0)
- fprintf (fp, ";tframes:%x", ts->traceframe_count);
+ fprintf (writer->fp, ";tframes:%x", ts->traceframe_count);
if (ts->traceframes_created >= 0)
- fprintf (fp, ";tcreated:%x", ts->traceframes_created);
+ fprintf (writer->fp, ";tcreated:%x", ts->traceframes_created);
if (ts->buffer_free >= 0)
- fprintf (fp, ";tfree:%x", ts->buffer_free);
+ fprintf (writer->fp, ";tfree:%x", ts->buffer_free);
if (ts->buffer_size >= 0)
- fprintf (fp, ";tsize:%x", ts->buffer_size);
+ fprintf (writer->fp, ";tsize:%x", ts->buffer_size);
if (ts->disconnected_tracing)
- fprintf (fp, ";disconn:%x", ts->disconnected_tracing);
+ fprintf (writer->fp, ";disconn:%x", ts->disconnected_tracing);
if (ts->circular_buffer)
- fprintf (fp, ";circular:%x", ts->circular_buffer);
- fprintf (fp, "\n");
+ fprintf (writer->fp, ";circular:%x", ts->circular_buffer);
+ fprintf (writer->fp, "\n");
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tsv. */
+
+static void
+tfile_write_uploaded_tsv (struct trace_file_writer *self,
+ struct uploaded_tsv *utsv)
+{
+ char *buf = "";
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ if (utsv->name)
+ {
+ buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
+ bin2hex ((gdb_byte *) (utsv->name), buf, 0);
+ }
+
+ fprintf (writer->fp, "tsv %x:%s:%x:%s\n",
+ utsv->number, phex_nz (utsv->initial_value, 8),
+ utsv->builtin, buf);
+
+ if (utsv->name)
+ xfree (buf);
+}
+
+#define MAX_TRACE_UPLOAD 2000
+
+/* This is the implementation of trace_file_write_ops method
+ write_uploaded_tp. */
+
+static void
+tfile_write_uploaded_tp (struct trace_file_writer *self,
+ struct uploaded_tp *utp)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+ int a;
+ char *act;
+ gdb_byte buf[MAX_TRACE_UPLOAD];
+
+ fprintf (writer->fp, "tp T%x:%s:%c:%x:%x",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ (utp->enabled ? 'E' : 'D'), utp->step, utp->pass);
+ if (utp->type == bp_fast_tracepoint)
+ fprintf (writer->fp, ":F%x", utp->orig_size);
+ if (utp->cond)
+ fprintf (writer->fp,
+ ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
+ utp->cond);
+ fprintf (writer->fp, "\n");
+ for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
+ fprintf (writer->fp, "tp A%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
+ fprintf (writer->fp, "tp S%x:%s:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+ if (utp->at_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
+ fprintf (writer->fp, "tp Z%s\n", buf);
+ }
+ if (utp->cond_string)
+ {
+ encode_source_string (utp->number, utp->addr,
+ "cond", utp->cond_string,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (writer->fp, "tp Z%s\n", buf);
+ }
+ for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
+ {
+ encode_source_string (utp->number, utp->addr, "cmd", act,
+ buf, MAX_TRACE_UPLOAD);
+ fprintf (writer->fp, "tp Z%s\n", buf);
+ }
+ fprintf (writer->fp, "tp V%x:%s:%x:%s\n",
+ utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ utp->hit_count,
+ phex_nz (utp->traceframe_usage,
+ sizeof (utp->traceframe_usage)));
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_definition_end. */
+
+static void
+tfile_write_definition_end (struct trace_file_writer *self)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ fprintf (writer->fp, "\n");
+}
+
+/* This is the implementation of trace_file_write_ops method
+ write_raw_data. */
+
+static void
+tfile_write_raw_data (struct trace_file_writer *self, gdb_byte *buf,
+ LONGEST len)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+
+ if (fwrite (buf, len, 1, writer->fp) < 1)
+ perror_with_name (writer->pathname);
+}
+
+/* This is the implementation of trace_file_write_ops method
+ end. */
+
+static void
+tfile_end (struct trace_file_writer *self)
+{
+ struct tfile_trace_file_writer *writer
+ = (struct tfile_trace_file_writer *) self;
+ uint32_t gotten = 0;
+
+ /* Mark the end of trace data. */
+ if (fwrite (&gotten, 4, 1, writer->fp) < 1)
+ perror_with_name (writer->pathname);
+
+ do_cleanups (writer->cleanup);
+}
+
+/* Operations to write trace buffers into TFILE format. */
+
+static const struct trace_file_write_ops tfile_write_ops =
+{
+ tfile_target_save,
+ tfile_start,
+ tfile_write_header,
+ tfile_write_regblock_type,
+ tfile_write_status,
+ tfile_write_uploaded_tsv,
+ tfile_write_uploaded_tp,
+ tfile_write_definition_end,
+ tfile_write_raw_data,
+ NULL,
+ tfile_end,
+};
+
+/* 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))
+
+extern int trace_regblock_size;
+
+/* 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;
+ 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
@@ -3081,22 +3317,7 @@ trace_save (const char *filename, int target_does_save)
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);
- }
+ writer->ops->write_uploaded_tsv (writer, utsv);
free_uploaded_tsvs (&uploaded_tsvs);
@@ -3106,76 +3327,205 @@ trace_save (const char *filename, int target_does_save)
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)
{
- 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; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
- fprintf (fp, "tp A%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
- fprintf (fp, "tp S%x:%s:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
- if (utp->at_string)
- {
- encode_source_string (utp->number, utp->addr,
- "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
- }
- if (utp->cond_string)
+ 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)
{
- encode_source_string (utp->number, utp->addr,
- "cond", utp->cond_string,
- buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
+ /* 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;
}
- for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
+ else
{
- encode_source_string (utp->number, utp->addr, "cmd", act,
- buf, MAX_TRACE_UPLOAD);
- fprintf (fp, "tp Z%s\n", buf);
+ 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);
}
- fprintf (fp, "tp V%x:%s:%x:%s\n",
- utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
- utp->hit_count,
- phex_nz (utp->traceframe_usage,
- sizeof (utp->traceframe_usage)));
}
- free_uploaded_tps (&uploaded_tps);
+ writer->ops->end (writer);
+}
- /* Mark the end of the definition section. */
- fprintf (fp, "\n");
+/* Return a trace writer for TFILE format. */
- /* 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;
- written = fwrite (buf, gotten, 1, fp);
- if (written < 1)
- perror_with_name (pathname);
- offset += gotten;
- }
+static struct trace_file_writer *
+tfile_trace_file_writer_new (void)
+{
+ struct tfile_trace_file_writer *writer
+ = xmalloc (sizeof (struct tfile_trace_file_writer));
- /* Mark the end of trace data. (We know that gotten is 0 at this point.) */
- written = fwrite (&gotten, 4, 1, fp);
- if (written < 1)
- perror_with_name (pathname);
+ writer->base.ops = &tfile_write_ops;
+ writer->fp = NULL;
+ writer->pathname = NULL;
- do_cleanups (cleanup);
+ return (struct trace_file_writer *) writer;
}
static void
@@ -3185,6 +3535,7 @@ trace_save_command (char *args, int from_tty)
char **argv;
char *filename = NULL;
struct cleanup *back_to;
+ struct trace_file_writer *writer = NULL;
if (args == NULL)
error_no_arg (_("file in which to save trace data"));
@@ -3205,7 +3556,11 @@ trace_save_command (char *args, int from_tty)
if (!filename)
error_no_arg (_("file in which to save trace data"));
- trace_save (filename, target_does_save);
+ 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 file '%s'.\n"), filename);
@@ -3213,6 +3568,21 @@ trace_save_command (char *args, int from_tty)
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 ();
+ trace_save (filename, writer, target_does_save);
+
+ back_to = make_cleanup (trace_file_writer_xfree, writer);
+ do_cleanups (back_to);
+}
+
/* Tell the target what to do with an ongoing tracing run if GDB
disconnects for some reason. */
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index b2b9d7c..fd3a225 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -205,6 +205,107 @@ struct static_tracepoint_marker
char *extra;
};
+struct trace_file_writer;
+
+/* Operations to write trace frames to a specific trace format. */
+
+struct trace_frame_write_ops
+{
+ /* Write a new trace frame. The tracepoint number of this trace
+ frame is TPNUM. */
+ void (*start) (struct trace_file_writer *self, uint16_t tpnum);
+
+ /* Write an 'R' block. Buffer BUF contains its contents and SIZE is
+ its size. */
+ void (*write_r_block) (struct trace_file_writer *self,
+ gdb_byte *buf, int size);
+
+ /* Write an 'M' block, the header and memory contents respectively.
+ The header of 'M' block is composed of the start address and the
+ length of memory collection, and the memory contents contain
+ the collected memory contents in tracing.
+ For extremely large M block, GDB is unable to get its contents
+ and write them into trace file in one go, due to the limitation
+ of the remote target or the size of internal buffer, we split
+ the operation to 'M' block to two operations. */
+ /* Write the head of 'M' block. ADDR is the start address of
+ collected memory and LENGTH is the length of memory contents. */
+ void (*write_m_block_header) (struct trace_file_writer *self,
+ uint64_t addr, uint16_t length);
+ /* Write the memory contents of 'M' block. Buffer BUF contains
+ its contents and LENGTH is its length. This method can be called
+ multiple times to write large memory contents of a single 'M'
+ block. */
+ void (*write_m_block_memory) (struct trace_file_writer *self,
+ gdb_byte *buf, uint16_t length);
+
+ /* Write a 'V' block. NUM is the trace variable number and VAL is
+ the value of the trace variable. */
+ void (*write_v_block) (struct trace_file_writer *self, int num,
+ LONGEST val);
+
+ /* The end of the trace frame. */
+ void (*end) (struct trace_file_writer *self);
+};
+
+/* Operations to write trace buffers to a specific trace format. */
+
+struct trace_file_write_ops
+{
+ /* Save the data to file or directory NAME of desired format in
+ target side. Return true for success, otherwise return
+ false. */
+ int (*target_save) (struct trace_file_writer *self,
+ const char *name);
+
+ /* Write the trace buffers to file or directory NAME. */
+ void (*start) (struct trace_file_writer *self,
+ const char *name);
+
+ /* Write the trace header. */
+ void (*write_header) (struct trace_file_writer *self);
+
+ /* Write the type of block about registers. SIZE is the size of
+ all registers on the target. */
+ void (*write_regblock_type) (struct trace_file_writer *self,
+ int size);
+
+ /* Write trace status TS. */
+ void (*write_status) (struct trace_file_writer *self,
+ struct trace_status *ts);
+
+ /* Write the uploaded TSV. */
+ void (*write_uploaded_tsv) (struct trace_file_writer *self,
+ struct uploaded_tsv *tsv);
+
+ /* Write the uploaded tracepoint TP. */
+ void (*write_uploaded_tp) (struct trace_file_writer *self,
+ struct uploaded_tp *tp);
+
+ /* Write to mark the end of the definition part. */
+ void (*write_definition_end) (struct trace_file_writer *self);
+
+ /* Write the data of trace buffer without parsing. The content is
+ in BUF and length is LEN. */
+ void (*write_trace_buffer) (struct trace_file_writer *self,
+ gdb_byte *buf, LONGEST len);
+
+ /* Operations to write trace frames. The user of this field is
+ responsible to parse the data of trace buffer. Either field
+ 'write_trace_buffer' or field ' frame_ops' is NULL. */
+ const struct trace_frame_write_ops *frame_ops;
+
+ /* The end of writing trace buffers. */
+ void (*end) (struct trace_file_writer *self);
+};
+
+/* Trace file writer for a given format. */
+
+struct trace_file_writer
+{
+ const struct trace_file_write_ops *ops;
+};
+
extern void parse_static_tracepoint_marker_definition
(char *line, char **pp,
struct static_tracepoint_marker *marker);
@@ -281,7 +382,8 @@ extern void tfind_1 (enum trace_find_type type, int num,
ULONGEST addr1, ULONGEST addr2,
int from_tty);
-extern void trace_save (const char *filename, int target_does_save);
+extern void trace_save_tfile (const char *filename,
+ int target_does_save);
extern struct traceframe_info *parse_traceframe_info (const char *tframe_info);
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH 5/5] ctf test: report.exp
2013-02-27 2:18 [PATCH 0/5, 2nd try] CTF Support Yao Qi
2013-02-27 2:18 ` [PATCH 1/5] Refactor 'tsave' Yao Qi
@ 2013-02-27 2:19 ` Yao Qi
2013-02-27 18:48 ` Pedro Alves
2013-02-27 2:19 ` [PATCH 3/5] Read CTF by the ctf target Yao Qi
` (2 subsequent siblings)
4 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-02-27 2:19 UTC (permalink / raw)
To: gdb-patches
This is a test to see GDB gets the same output as live inferior and
TFILE trace format.
This patch depends on this patch,
[PATCH 1/2] Check trace data from trace file
http://sourceware.org/ml/gdb-patches/2013-02/msg00611.html
gdb/testsuite:
2013-02-27 Yao Qi <yao@codesourcery.com>
* gdb.trace/report.exp: Test GDB saves trace data to CTF
format and read CTF trace file if GDB supports.
---
gdb/testsuite/gdb.trace/report.exp | 21 +++++++++++++++++++++
1 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/gdb/testsuite/gdb.trace/report.exp b/gdb/testsuite/gdb.trace/report.exp
index b32bca1..d074c1f 100644
--- a/gdb/testsuite/gdb.trace/report.exp
+++ b/gdb/testsuite/gdb.trace/report.exp
@@ -408,6 +408,10 @@ gdb_tfind_test "finished: make sure not debugging any trace frame" \
# Save trace frames to tfile.
gdb_test "tsave report.tf" "Trace data saved to file 'report.tf'.*"
+# Save trace frames to ctf.
+gdb_test "tsave -ctf report.ctf" \
+ "Trace data saved to directory 'report.ctf'.*"
+
# Change target to tfile.
set test "change to tfile target"
gdb_test_multiple "target tfile report.tf" "$test" {
@@ -421,3 +425,20 @@ gdb_test_multiple "target tfile report.tf" "$test" {
}
# Test the collected trace frames from tfile.
use_collected_data "tfile"
+
+# Save trace frames to ctf format if GDB is able to read ctf data.
+set gdb_can_read_ctf_data 0
+gdb_test_multiple "target ctf" "" {
+ -re "Undefined target command: \"ctf\"\. Try \"help target\"\.\r\n$gdb_prompt $" {
+ set gdb_can_read_ctf_data 0
+ }
+ -re "No CTF directory specified.*\r\n$gdb_prompt $" {
+ set gdb_can_read_ctf_data 1
+ }
+}
+
+if { $gdb_can_read_ctf_data } {
+ # Change target to ctf.
+ gdb_test_no_output "target ctf report.ctf" "change to ctf target"
+ use_collected_data "ctf"
+}
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 5/5] ctf test: report.exp
2013-02-27 2:19 ` [PATCH 5/5] ctf test: report.exp Yao Qi
@ 2013-02-27 18:48 ` Pedro Alves
2013-02-28 1:36 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Pedro Alves @ 2013-02-27 18:48 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
Hi Yao, do you have an updated version of this patch
that applies on current mainline? This no longer applies
as the report.exp patch changed.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 5/5] ctf test: report.exp
2013-02-27 18:48 ` Pedro Alves
@ 2013-02-28 1:36 ` Yao Qi
2013-02-28 18:30 ` Pedro Alves
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-02-28 1:36 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On 02/28/2013 02:44 AM, Pedro Alves wrote:
> Hi Yao, do you have an updated version of this patch
> that applies on current mainline? This no longer applies
> as the report.exp patch changed.
>
As I mentioned in this patch, it depends on patch
[PATCH 1/2] Check trace data from trace file
http://sourceware.org/ml/gdb-patches/2013-02/msg00611.html
and changes in report.exp shouldn't affect this patch.
I committed it just now, so patch 5/5 should be applied cleanly on top
of mainline.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 5/5] ctf test: report.exp
2013-02-28 1:36 ` Yao Qi
@ 2013-02-28 18:30 ` Pedro Alves
0 siblings, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2013-02-28 18:30 UTC (permalink / raw)
To: Yao Qi; +Cc: Pedro Alves, gdb-patches
On 02/28/2013 12:51 AM, Yao Qi wrote:
> On 02/28/2013 02:44 AM, Pedro Alves wrote:
>> Hi Yao, do you have an updated version of this patch
>> that applies on current mainline? This no longer applies
>> as the report.exp patch changed.
>>
>
> As I mentioned in this patch, it depends on patch
>
> [PATCH 1/2] Check trace data from trace file
> http://sourceware.org/ml/gdb-patches/2013-02/msg00611.html
>
> and changes in report.exp shouldn't affect this patch.
Sorry, got confused and thought the patch had already been
applied.
>
> I committed it just now, so patch 5/5 should be applied cleanly on top of mainline.
Thanks.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH 3/5] Read CTF by the ctf target
2013-02-27 2:18 [PATCH 0/5, 2nd try] CTF Support Yao Qi
2013-02-27 2:18 ` [PATCH 1/5] Refactor 'tsave' Yao Qi
2013-02-27 2:19 ` [PATCH 5/5] ctf test: report.exp Yao Qi
@ 2013-02-27 2:19 ` Yao Qi
2013-02-28 17:59 ` Pedro Alves
2013-03-01 15:51 ` Tom Tromey
2013-02-27 2:19 ` [PATCH 4/5] ctf doc and NEWS Yao Qi
2013-02-27 2:19 ` [PATCH 2/5] Save trace into CTF format Yao Qi
4 siblings, 2 replies; 37+ messages in thread
From: Yao Qi @ 2013-02-27 2:19 UTC (permalink / raw)
To: gdb-patches
This patch adds a new target 'ctf' in GDB, so GDB can read CTF trace
data in this target. Some of the implementations of target vector for
ctf target are copied from tfile target, so some of them can be shared
in the future.
We use libbabeltrace to read CTF data, because libbabeltrace is
designed to do this, and we don't have to reinvent the wheel again in
GDB. Some configure stuff is modified to check whether the
libbabeltrace is installed.
gdb:
2013-02-27 Hui Zhu <hui_zhu@mentor.com>
Yao Qi <yao@codesourcery.com>
* configure.ac: Check libbabeltrace is installed.
* config.in: Regenerate.
* configure: Regenerate.
* Makefile.in (LIBBABELTRACE, LIBBABELTRACE_CFLAGS): New.
(INTERNAL_CFLAGS_BASE): Append LIBBABELTRACE_CFLAGS.
(CLIBS): Add LIBBABELTRACE.
* ctf.c (ctx, ctf_iter, trace_dirname): New.
(ctf_close_dir, ctf_open_dir, ctf_open): New.
(ctf_close, ctf_files_info): New.
(ctf_fetch_registers, ctf_xfer_partial): New.
(ctf_get_trace_state_variable_value): New.
(ctf_get_tpnum_from_frame_event): New.
(ctf_get_traceframe_address): New.
(ctf_trace_find, ctf_has_all_memory): New.
(ctf_has_memory, ctf_has_stack): New.
(ctf_has_registers, ctf_thread_alive): New.
(ctf_traceframe_info, init_ctf_ops): New.
(_initialize_ctf): New.
* tracepoint.c (traceframe_number, tracepoint_number): Remove
'static'.
(struct traceframe_info): Move it to ...
* tracepoint.h: ... here.
(tracepoint_number, tracepoint_number): Declare.
---
gdb/Makefile.in | 10 +-
gdb/config.in | 3 +
gdb/configure | 92 +++++++
gdb/configure.ac | 50 ++++
gdb/ctf.c | 733 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tracepoint.c | 12 +-
gdb/tracepoint.h | 12 +
7 files changed, 900 insertions(+), 12 deletions(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5be6c77..df5404a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -154,6 +154,11 @@ LIBEXPAT = @LIBEXPAT@
# Where is lzma? This will be empty if lzma was not available.
LIBLZMA = @LIBLZMA@
+# Where is libbabeltrace? This will be empty if lzma was not available.
+LIBBABELTRACE = @btlibs@
+LIBBABELTRACE_CFLAGS = @btinc@
+
+
WARN_CFLAGS = @WARN_CFLAGS@
WERROR_CFLAGS = @WERROR_CFLAGS@
GDB_WARN_CFLAGS = $(WARN_CFLAGS)
@@ -453,7 +458,8 @@ INTERNAL_CFLAGS_BASE = \
$(CFLAGS) $(GLOBAL_CFLAGS) $(PROFILE_CFLAGS) \
$(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) \
$(BFD_CFLAGS) $(INCLUDE_CFLAGS) $(LIBDECNUMBER_CFLAGS) \
- $(INTL_CFLAGS) $(INCGNU) $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS)
+ $(INTL_CFLAGS) $(INCGNU) $(LIBBABELTRACE_CFLAGS) \
+ $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS)
INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS)
INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS)
@@ -475,7 +481,7 @@ INTERNAL_LDFLAGS = $(CFLAGS) $(GLOBAL_CFLAGS) $(MH_LDFLAGS) $(LDFLAGS) $(CONFIG_
# LIBIBERTY appears twice on purpose.
CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
$(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) @LIBS@ @PYTHON_LIBS@ \
- $(LIBEXPAT) $(LIBLZMA) \
+ $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) \
$(LIBIBERTY) $(WIN32LIBS) $(LIBGNU)
CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \
$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU)
diff --git a/gdb/config.in b/gdb/config.in
index 9e21325..c4e8eaa 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -180,6 +180,9 @@
/* Define if your <locale.h> file defines LC_MESSAGES. */
#undef HAVE_LC_MESSAGES
+/* Define if libbabeltrace is available */
+#undef HAVE_LIBBABELTRACE
+
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
diff --git a/gdb/configure b/gdb/configure
index c54709c..3f8c6d1 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -592,6 +592,8 @@ enable_option_checking=no
ac_subst_vars='LTLIBOBJS
LIBOBJS
GDB_NM_FILE
+btinc
+btlibs
frags
target_subdir
CONFIG_UNINSTALL
@@ -819,6 +821,9 @@ with_x
enable_sim
enable_multi_ice
enable_gdbserver
+with_babeltrace
+with_bt_include
+with_bt_lib
'
ac_precious_vars='build_alias
host_alias
@@ -1532,6 +1537,11 @@ Optional Packages:
--with-tcl directory containing tcl configuration (tclConfig.sh)
--with-tk directory containing tk configuration (tkConfig.sh)
--with-x use the X Window System
+ --with-babeltrace=PATH Specify prefix directory for the installed BABELTRACE package
+ Equivalent to --with-babeltrace-include=PATH/include
+ plus --with-babeltrace-lib=PATH/lib
+ --with-babeltrace-include=PATH Specify directory for installed babeltrace include files
+ --with-babeltrace-lib=PATH Specify the directory for the installed babeltrace library
Some influential environment variables:
CC C compiler command
@@ -14090,6 +14100,88 @@ if test "$enable_gdbserver" = "yes" -a "$gdbserver_build_enabled" != "yes"; then
as_fn_error "Automatic gdbserver build is not supported for this configuration" "$LINENO" 5
fi
+# Check for babeltrace and babeltrace-ctf
+btlibs=""
+btinc=""
+
+
+# Check whether --with-babeltrace was given.
+if test "${with_babeltrace+set}" = set; then :
+ withval=$with_babeltrace;
+fi
+
+
+# Check whether --with-bt_include was given.
+if test "${with_bt_include+set}" = set; then :
+ withval=$with_bt_include;
+fi
+
+
+# Check whether --with-bt_lib was given.
+if test "${with_bt_lib+set}" = set; then :
+ withval=$with_bt_lib;
+fi
+
+
+case $with_babeltrace in
+ no)
+ btlibs=
+ btinc=
+ ;;
+ "" | yes)
+ btlibs=" -lbabeltrace -lbabeltrace-ctf "
+ btinc=""
+ ;;
+ *)
+ btlibs="-L$with_babeltrace/lib -lbabeltrace -lbabeltrace-ctf"
+ btinc="-I$with_babeltrace/include "
+ ;;
+esac
+if test "x$with_bt_include" != x; then
+ btinc="-I$with_bt_include "
+fi
+if test "x$with_bt_lib" != x; then
+ tlibs="-L$with_bt_lib -lbabeltrace -lbabeltrace-ctf"
+fi
+
+if test "x$with_babeltrace" != "xno"; then
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $btinc"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for babeltrace" >&5
+$as_echo_n "checking for babeltrace... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/iterator.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; };
+$as_echo "#define HAVE_LIBBABELTRACE 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }; btlibs= ; btinc=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CFLAGS="$saved_CFLAGS"
+fi
+
+# Flags needed for UST
+
+
+
+
# If nativefile (NAT_FILE) is not set in config/*/*.m[ht] files, we link
# to an empty version.
diff --git a/gdb/configure.ac b/gdb/configure.ac
index e501766..e5bf83c 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -2318,6 +2318,56 @@ if test "$enable_gdbserver" = "yes" -a "$gdbserver_build_enabled" != "yes"; then
AC_MSG_ERROR(Automatic gdbserver build is not supported for this configuration)
fi
+# Check for babeltrace and babeltrace-ctf
+btlibs=""
+btinc=""
+
+AC_ARG_WITH(babeltrace, [ --with-babeltrace=PATH Specify prefix directory for the installed BABELTRACE package
+ Equivalent to --with-babeltrace-include=PATH/include
+ plus --with-babeltrace-lib=PATH/lib])
+AC_ARG_WITH(bt_include, [ --with-babeltrace-include=PATH Specify directory for installed babeltrace include files])
+AC_ARG_WITH(bt_lib, [ --with-babeltrace-lib=PATH Specify the directory for the installed babeltrace library])
+
+case $with_babeltrace in
+ no)
+ btlibs=
+ btinc=
+ ;;
+ "" | yes)
+ btlibs=" -lbabeltrace -lbabeltrace-ctf "
+ btinc=""
+ ;;
+ *)
+ btlibs="-L$with_babeltrace/lib -lbabeltrace -lbabeltrace-ctf"
+ btinc="-I$with_babeltrace/include "
+ ;;
+esac
+if test "x$with_bt_include" != x; then
+ btinc="-I$with_bt_include "
+fi
+if test "x$with_bt_lib" != x; then
+ tlibs="-L$with_bt_lib -lbabeltrace -lbabeltrace-ctf"
+fi
+
+if test "x$with_babeltrace" != "xno"; then
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $btinc"
+ AC_MSG_CHECKING([for babeltrace])
+ AC_TRY_COMPILE([
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/iterator.h>
+ ],[],
+ [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_LIBBABELTRACE, 1, [Define if libbabeltrace is available])],
+ [AC_MSG_RESULT([no]); btlibs= ; btinc= ])
+ CFLAGS="$saved_CFLAGS"
+fi
+
+# Flags needed for UST
+AC_SUBST(btlibs)
+AC_SUBST(btinc)
+
+
# If nativefile (NAT_FILE) is not set in config/*/*.m[ht] files, we link
# to an empty version.
diff --git a/gdb/ctf.c b/gdb/ctf.c
index c4809d8..fe28d42 100644
--- a/gdb/ctf.c
+++ b/gdb/ctf.c
@@ -23,6 +23,7 @@
#include "ctf.h"
#include "tracepoint.h"
#include "regcache.h"
+#include "exec.h"
#include <ctype.h>
@@ -678,3 +679,735 @@ ctf_trace_file_writer (void)
return writer;
}
+
+#ifdef HAVE_LIBBABELTRACE
+/* Use libbabeltrace to read CTF data. The libbabeltrace provides
+ iterator to iterate over each event in CTF data and APIs to get
+ details of event and packet, so it is very convenient to use
+ libbabeltrace to access events in CTF. */
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/iterator.h>
+
+/* The struct pointer for current CTF directory. */
+static struct bt_context *ctx = NULL;
+static struct bt_ctf_iter *ctf_iter = NULL;
+/* The name of CTF directory. */
+static char *trace_dirname;
+
+static struct target_ops ctf_ops;
+
+static void
+ctf_close_dir (void)
+{
+ if (ctf_iter)
+ {
+ bt_ctf_iter_destroy (ctf_iter);
+ ctf_iter = NULL;
+ }
+ if (ctx)
+ {
+ bt_context_put (ctx);
+ ctx = NULL;
+ }
+}
+
+extern int trace_regblock_size;
+
+/* Open CTF trace data in DIRNAME. */
+
+static void
+ctf_open_dir (char *dirname)
+{
+ int ret;
+ struct bt_iter_pos begin_pos;
+ struct bt_iter_pos *pos;
+
+ ctx = bt_context_create ();
+ if (!ctx)
+ error (_("Unable to initialize libbabeltrace"));
+ ret = bt_context_add_trace (ctx, dirname, "ctf", NULL, NULL, NULL);
+ if (ret < 0)
+ {
+ ctf_close_dir ();
+ error (_("Unable to use libbabeltrace open \"%s\""), dirname);
+ }
+
+ begin_pos.type = BT_SEEK_BEGIN;
+ ctf_iter = bt_ctf_iter_create (ctx, &begin_pos, NULL);
+ if (ctf_iter == NULL)
+ {
+ ctf_close_dir ();
+ error (_("Unable to use libbabeltrace open \"%s\""), dirname);
+ }
+
+ /* Iterate over events, and look for an event for register block
+ to set trace_regblock_size. */
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ while (1)
+ {
+ const char *name;
+ struct bt_ctf_event *event;
+
+ event = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event);
+
+ if (name == NULL)
+ break;
+ else if (strcmp (name, "register") == 0)
+ {
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope(event,
+ BT_EVENT_FIELDS);
+ const struct bt_definition *array
+ = bt_ctf_get_field(event, scope, "contents");
+
+ trace_regblock_size
+ = bt_ctf_get_array_len (bt_ctf_get_decl_from_def (array));
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+
+}
+
+static void
+ctf_open (char *dirname, int from_tty)
+{
+ target_preopen (from_tty);
+ if (!dirname)
+ error (_("No CTF directory specified."));
+
+ ctf_open_dir (dirname);
+
+ trace_dirname = xstrdup (dirname);
+ push_target (&ctf_ops);
+}
+
+static void
+ctf_close (int quitting)
+{
+ ctf_close_dir ();
+ xfree (trace_dirname);
+ trace_dirname = NULL;
+}
+
+static void
+ctf_files_info (struct target_ops *t)
+{
+ printf_filtered ("\t`%s'\n", trace_dirname);
+}
+
+/* Iterate over events whose name is "register" in current frame,
+ extract contents from events, and set REGCACHE with the contents.
+ If no matched events are found, mark registers unavailable. */
+
+static void
+ctf_fetch_registers (struct target_ops *ops,
+ struct regcache *regcache, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int offset, regn, regsize, pc_regno;
+ char *regs = NULL;
+ struct bt_ctf_event *event = NULL;
+ struct bt_iter_pos *pos;
+
+ /* An uninitialized reg size says we're not going to be
+ successful at getting register blocks. */
+ if (!trace_regblock_size)
+ return;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ while (1)
+ {
+ const char *name;
+ struct bt_ctf_event *event1;
+
+ event1 = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event1);
+
+ if (name == NULL || strcmp (name, "frame") == 0)
+ break;
+ else if (strcmp (name, "register") == 0)
+ {
+ event = event1;
+ break;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+
+ if (event != NULL)
+ {
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope(event,
+ BT_EVENT_FIELDS);
+ const struct bt_definition *array
+ = bt_ctf_get_field(event, scope, "contents");
+
+ regs = bt_ctf_get_char_array (array);
+ /* Assume the block is laid out in GDB register number order,
+ each register with the size that it has in GDB. */
+ offset = 0;
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ {
+ regsize = register_size (gdbarch, regn);
+ /* Make sure we stay within block bounds. */
+ if (offset + regsize >= trace_regblock_size)
+ break;
+ if (regcache_register_status (regcache, regn) == REG_UNKNOWN)
+ {
+ if (regno == regn)
+ {
+ regcache_raw_supply (regcache, regno, regs + offset);
+ break;
+ }
+ else if (regno == -1)
+ {
+ regcache_raw_supply (regcache, regn, regs + offset);
+ }
+ }
+ offset += regsize;
+ }
+ /* xfree (regs); */
+ return;
+ }
+
+ regs = alloca (trace_regblock_size);
+
+ /* We get here if no register data has been found. Mark registers
+ as unavailable. */
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ regcache_raw_supply (regcache, regn, NULL);
+
+ /* We can often usefully guess that the PC is going to be the same
+ as the address of the tracepoint. */
+ pc_regno = gdbarch_pc_regnum (gdbarch);
+ if (pc_regno >= 0 && (regno == -1 || regno == pc_regno))
+ {
+ struct tracepoint *tp = get_tracepoint (tracepoint_number);
+
+ if (tp && tp->base.loc)
+ {
+ /* But don't try to guess if tracepoint is multi-location... */
+ if (tp->base.loc->next)
+ {
+ warning (_("Tracepoint %d has multiple "
+ "locations, cannot infer $pc"),
+ tp->base.number);
+ return;
+ }
+ /* ... or does while-stepping. */
+ if (tp->step_count > 0)
+ {
+ warning (_("Tracepoint %d does while-stepping, "
+ "cannot infer $pc"),
+ tp->base.number);
+ return;
+ }
+
+ store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
+ gdbarch_byte_order (gdbarch),
+ tp->base.loc->address);
+ regcache_raw_supply (regcache, pc_regno, regs);
+ }
+ }
+}
+
+/* Iterate over events whose name is started with "memory_" in
+ current frame, extract the address and length from events. If
+ OFFSET is within the range, read the contents from events to
+ READBUF. */
+
+static LONGEST
+ctf_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset,
+ LONGEST len)
+{
+ /* We're only doing regular memory for now. */
+ if (object != TARGET_OBJECT_MEMORY)
+ return -1;
+
+ if (readbuf == NULL)
+ error (_("ctf_xfer_partial: trace file is read-only"));
+
+ if (traceframe_number != -1)
+ {
+ struct bt_iter_pos *pos;
+ int i = 0;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ /* Iterate through the traceframe's blocks, looking for
+ memory. */
+ while (1)
+ {
+ ULONGEST maddr, amt;
+ unsigned short mlen;
+ enum bfd_endian byte_order
+ = gdbarch_byte_order (target_gdbarch ());
+ const struct bt_definition *scope;
+ const struct bt_definition *def;
+ struct bt_ctf_event *event
+ = bt_ctf_iter_read_event (ctf_iter);
+ const char *name = bt_ctf_event_name (event);
+
+ if (strcmp (name, "frame") == 0)
+ break;
+ else if (strncmp (name, "memory_", 7) != 0)
+ {
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+
+ continue;
+ }
+
+ scope = bt_ctf_get_top_level_scope(event,
+ BT_EVENT_FIELDS);
+
+ def = bt_ctf_get_field (event, scope, "address");
+ maddr = bt_ctf_get_uint64 (def);
+ def = bt_ctf_get_field (event, scope, "length");
+ mlen = (uint16_t) bt_ctf_get_uint64 (def);
+
+ /* If the block includes the first part of the desired
+ range, return as much it has; GDB will re-request the
+ remainder, which might be in a different block of this
+ trace frame. */
+ if (maddr <= offset && offset < (maddr + mlen))
+ {
+ const struct bt_definition *array
+ = bt_ctf_get_field(event, scope, "contents");
+ const struct bt_declaration *decl
+ = bt_ctf_get_decl_from_def (array);
+ gdb_byte *contents;
+
+ gdb_assert (mlen == bt_ctf_get_array_len (decl));
+ contents = bt_ctf_get_char_array (array);
+
+ amt = (maddr + mlen) - offset;
+ if (amt > len)
+ amt = len;
+
+ memcpy (readbuf, &contents[offset - maddr], amt);
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+
+ return amt;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+ }
+
+ /* It's unduly pedantic to refuse to look at the executable for
+ read-only pieces; so do the equivalent of readonly regions aka
+ QTro packet. */
+ /* FIXME account for relocation at some point. */
+ if (exec_bfd)
+ {
+ asection *s;
+ bfd_size_type size;
+ bfd_vma vma;
+
+ for (s = exec_bfd->sections; s; s = s->next)
+ {
+ if ((s->flags & SEC_LOAD) == 0
+ || (s->flags & SEC_READONLY) == 0)
+ continue;
+
+ vma = s->vma;
+ size = bfd_get_section_size (s);
+ if (vma <= offset && offset < (vma + size))
+ {
+ ULONGEST amt;
+
+ amt = (vma + size) - offset;
+ if (amt > len)
+ amt = len;
+
+ amt = bfd_get_section_contents (exec_bfd, s,
+ readbuf, offset - vma, amt);
+ return amt;
+ }
+ }
+ }
+
+ /* Indicate failure to find the requested memory block. */
+ return -1;
+}
+
+/* Iterate over events whose name is "tsv" in current frame. When the
+ trace variable is found, set the value of it to *VAL and return
+ true, otherwise return false. */
+
+static int
+ctf_get_trace_state_variable_value (int tsvnum, LONGEST *val)
+{
+ struct bt_iter_pos *pos;
+ int found = 0;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ /* Iterate through the traceframe's blocks, looking for 'V'
+ block. */
+ while (1)
+ {
+ struct bt_ctf_event *event
+ = bt_ctf_iter_read_event (ctf_iter);
+ const char *name = bt_ctf_event_name (event);
+
+ if (strcmp (name, "frame") == 0)
+ break;
+ else if (strcmp (name, "tsv") == 0)
+ {
+ const struct bt_definition *scope;
+ const struct bt_definition *def;
+
+ scope = bt_ctf_get_top_level_scope(event,
+ BT_EVENT_FIELDS);
+
+ def = bt_ctf_get_field (event, scope, "num");
+ if (tsvnum == (int) bt_ctf_get_uint64 (def))
+ {
+ def = bt_ctf_get_field (event, scope, "val");
+ *val = bt_ctf_get_uint64 (def);
+
+ found = 1;
+ break;
+ }
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+
+ /* Didn't find anything. */
+ return found;
+}
+
+/* Return the tracepoint number in "frame" event. */
+
+static int
+ctf_get_tpnum_from_frame_event (struct bt_ctf_event *event)
+{
+ /* The packet context of events has a field "tpnum". */
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope(event, BT_STREAM_PACKET_CONTEXT);
+ uint64_t tpnum
+ = bt_ctf_get_uint64 (bt_ctf_get_field(event, scope, "tpnum"));
+
+ return (int) tpnum;
+}
+
+/* Return what address the current frame was collected at. */
+
+static ULONGEST
+ctf_get_traceframe_address (void)
+{
+ struct bt_ctf_event *event = NULL;
+ struct bt_iter_pos *pos
+ = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ ULONGEST addr = 0;
+
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ while (1)
+ {
+ const char *name;
+ struct bt_ctf_event *event1;
+
+ event1 = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event1);
+
+ if (name == NULL)
+ break;
+ else if (strcmp (name, "frame") == 0)
+ {
+ event = event1;
+ break;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ if (event != NULL)
+ {
+ int tpnum = ctf_get_tpnum_from_frame_event (event);
+ struct tracepoint *tp
+ = get_tracepoint_by_number_on_target (tpnum);
+
+ /* FIXME this is a poor heuristic if multiple locations. */
+ if (tp && tp->base.loc)
+ addr = tp->base.loc->address;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+
+ return addr;
+}
+
+/* Iterate the events whose name is "frame", extract the tracepoint
+ number in it. Return traceframe number when matched. */
+
+static int
+ctf_trace_find (enum trace_find_type type, int num,
+ ULONGEST addr1, ULONGEST addr2, int *tpp)
+{
+ int ret = -1;
+ int tfnum = 0;
+ int found = 0;
+ struct bt_iter_pos pos;
+
+ if (num == -1)
+ {
+ if (tpp)
+ *tpp = -1;
+ return -1;
+ }
+
+ /* Set iterator back to the beginning. */
+ pos.type = BT_SEEK_BEGIN;
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), &pos);
+
+ while (1)
+ {
+ int id;
+ struct bt_ctf_event *event;
+ const char *name;
+
+ event = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event);
+
+ if (event == NULL || name == NULL)
+ return -1;
+
+ if (strcmp (name, "frame") == 0)
+ {
+ ULONGEST tfaddr;
+
+ if (type == tfind_number)
+ {
+ /* Looking for a specific trace frame. */
+ if (tfnum == num)
+ found = 1;
+ }
+ else
+ {
+ /* Start from the _next_ trace frame. */
+ if (tfnum > traceframe_number)
+ {
+ switch (type)
+ {
+ case tfind_tp:
+ {
+ struct tracepoint *tp = get_tracepoint (num);
+
+ if (tp != NULL
+ && (tp->number_on_target
+ == ctf_get_tpnum_from_frame_event (event)))
+ found = 1;
+ break;
+ }
+ case tfind_pc:
+ tfaddr = ctf_get_traceframe_address ();
+ if (tfaddr == addr1)
+ found = 1;
+ break;
+ case tfind_range:
+ tfaddr = ctf_get_traceframe_address ();
+ if (addr1 <= tfaddr && tfaddr <= addr2)
+ found = 1;
+ break;
+ case tfind_outside:
+ tfaddr = ctf_get_traceframe_address ();
+ if (!(addr1 <= tfaddr && tfaddr <= addr2))
+ found = 1;
+ break;
+ default:
+ internal_error (__FILE__, __LINE__, _("unknown tfind type"));
+ }
+ }
+ }
+ if (found)
+ {
+ if (tpp)
+ *tpp = ctf_get_tpnum_from_frame_event (event);
+
+ /* Skip the event "frame". */
+ bt_iter_next (bt_ctf_get_iter (ctf_iter));
+
+ return tfnum;
+ }
+ tfnum++;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ return -1;
+ }
+
+ return -1;
+}
+
+static int
+ctf_has_all_memory (struct target_ops *ops)
+{
+ return 0;
+}
+
+static int
+ctf_has_memory (struct target_ops *ops)
+{
+ return 0;
+}
+
+static int
+ctf_has_stack (struct target_ops *ops)
+{
+ return traceframe_number != -1;
+}
+
+static int
+ctf_has_registers (struct target_ops *ops)
+{
+ return traceframe_number != -1;
+}
+
+static int
+ctf_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+ return 1;
+}
+
+/* Iterate the events whose name is started with "memory_", in current
+ frame, extract memory range information, and return them in
+ traceframe_info. */
+
+static struct traceframe_info *
+ctf_traceframe_info (void)
+{
+ struct traceframe_info *info = XCNEW (struct traceframe_info);
+ const char *name;
+ struct bt_iter_pos *pos;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ do
+ {
+ struct bt_ctf_event *event
+ = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event);
+
+ if (name == NULL || strcmp (name, "register") == 0
+ || strcmp (name, "frame") == 0)
+ ;
+ else if (strncmp (name, "memory_", 7) == 0)
+ {
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope(event,
+ BT_EVENT_FIELDS);
+ const struct bt_definition *def;
+ struct mem_range *r;
+
+ r = VEC_safe_push (mem_range_s, info->memory, NULL);
+ def = bt_ctf_get_field (event, scope, "address");
+ r->start = bt_ctf_get_uint64 (def);
+
+ def = bt_ctf_get_field (event, scope, "length");
+ r->length = (uint16_t) bt_ctf_get_uint64 (def);
+ }
+ else
+ warning (_("Unhandled trace block type (%s) "
+ "while building trace frame info."),
+ name);
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+ while (name != NULL && strcmp (name, "frame") != 0);
+
+ /* Restore the position. */
+ bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
+
+ /* traceframe_walk_blocks (build_traceframe_info, 0, info); */
+ return info;
+}
+
+static void
+init_ctf_ops (void)
+{
+ ctf_ops.to_shortname = "ctf";
+ ctf_ops.to_longname = "CTF file";
+ ctf_ops.to_doc = "Use a CTF directory as a target.\n\
+Specify the filename of the CTF directory.";
+ ctf_ops.to_open = ctf_open;
+ ctf_ops.to_close = ctf_close;
+ ctf_ops.to_fetch_registers = ctf_fetch_registers;
+ ctf_ops.to_xfer_partial = ctf_xfer_partial;
+ ctf_ops.to_files_info = ctf_files_info;
+ ctf_ops.to_trace_find = ctf_trace_find;
+ ctf_ops.to_get_trace_state_variable_value
+ = ctf_get_trace_state_variable_value;
+ ctf_ops.to_stratum = process_stratum;
+ ctf_ops.to_has_all_memory = ctf_has_all_memory;
+ ctf_ops.to_has_memory = ctf_has_memory;
+ ctf_ops.to_has_stack = ctf_has_stack;
+ ctf_ops.to_has_registers = ctf_has_registers;
+ ctf_ops.to_traceframe_info = ctf_traceframe_info;
+ ctf_ops.to_thread_alive = ctf_thread_alive;
+ ctf_ops.to_magic = OPS_MAGIC;
+}
+
+#endif
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_ctf;
+
+/* module initialization */
+void
+_initialize_ctf (void)
+{
+#ifdef HAVE_LIBBABELTRACE
+ init_ctf_ops ();
+
+ add_target (&ctf_ops);
+#endif
+}
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index 7434df7..d214117 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -129,14 +129,6 @@ extern void (*deprecated_readline_end_hook) (void);
typedef struct trace_state_variable tsv_s;
DEF_VEC_O(tsv_s);
-/* An object describing the contents of a traceframe. */
-
-struct traceframe_info
-{
- /* Collected memory. */
- VEC(mem_range_s) *memory;
-};
-
static VEC(tsv_s) *tvariables;
/* The next integer to assign to a variable. */
@@ -144,10 +136,10 @@ static VEC(tsv_s) *tvariables;
static int next_tsv_number = 1;
/* Number of last traceframe collected. */
-static int traceframe_number;
+int traceframe_number;
/* Tracepoint for last traceframe collected. */
-static int tracepoint_number;
+int tracepoint_number;
/* Symbol for function for last traceframe collected. */
static struct symbol *traceframe_fun;
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index 44ff2de..008f7e2 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -24,6 +24,14 @@
#include "memrange.h"
#include "gdb_vecs.h"
+/* An object describing the contents of a traceframe. */
+
+struct traceframe_info
+{
+ /* Collected memory. */
+ VEC(mem_range_s) *memory;
+};
+
/* A trace state variable is a value managed by a target being
traced. A trace state variable (or tsv for short) can be accessed
and assigned to by tracepoint actions and conditionals, but is not
@@ -142,6 +150,10 @@ struct trace_status *current_trace_status (void);
extern char *default_collect;
+extern int tracepoint_number;
+
+extern int traceframe_number;
+
/* Struct to collect random info about tracepoints on the target. */
struct uploaded_tp
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 3/5] Read CTF by the ctf target
2013-02-27 2:19 ` [PATCH 3/5] Read CTF by the ctf target Yao Qi
@ 2013-02-28 17:59 ` Pedro Alves
2013-03-01 2:38 ` Hui Zhu
2013-03-01 7:55 ` Yao Qi
2013-03-01 15:51 ` Tom Tromey
1 sibling, 2 replies; 37+ messages in thread
From: Pedro Alves @ 2013-02-28 17:59 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
(not a complete review)
On 02/27/2013 02:17 AM, Yao Qi wrote:
> +if test "x$with_babeltrace" != "xno"; then
> + saved_CFLAGS="$CFLAGS"
> + CFLAGS="$CFLAGS $btinc"
> + AC_MSG_CHECKING([for babeltrace])
> + AC_TRY_COMPILE([
> +#include <babeltrace/babeltrace.h>
> +#include <babeltrace/ctf/events.h>
> +#include <babeltrace/ctf/iterator.h>
We know from previous patches that the latest released
babeltrace breaks gdb, due to the lookup_enum function. Does
this catch that? I see namespacing fixes going into
babeltrace as recently as last 15th. Do we know if
babeltrace's API is more or less done and close to a release?
> + ],[],
> + [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_LIBBABELTRACE, 1, [Define if libbabeltrace is available])],
> + [AC_MSG_RESULT([no]); btlibs= ; btinc= ])
> + CFLAGS="$saved_CFLAGS"
> +fi
> +
> +# Flags needed for UST
babeltrace
> +AC_SUBST(btlibs)
> +AC_SUBST(btinc)
> +
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] Read CTF by the ctf target
2013-02-28 17:59 ` Pedro Alves
@ 2013-03-01 2:38 ` Hui Zhu
2013-05-07 13:07 ` Mathieu Desnoyers
2013-03-01 7:55 ` Yao Qi
1 sibling, 1 reply; 37+ messages in thread
From: Hui Zhu @ 2013-03-01 2:38 UTC (permalink / raw)
To: Mathieu Desnoyers; +Cc: Yao Qi, gdb-patches, Pedro Alves, lttng-dev
On Fri, Mar 1, 2013 at 1:18 AM, Pedro Alves <palves@redhat.com> wrote:
> (not a complete review)
>
> On 02/27/2013 02:17 AM, Yao Qi wrote:
>
>> +if test "x$with_babeltrace" != "xno"; then
>> + saved_CFLAGS="$CFLAGS"
>> + CFLAGS="$CFLAGS $btinc"
>> + AC_MSG_CHECKING([for babeltrace])
>> + AC_TRY_COMPILE([
>> +#include <babeltrace/babeltrace.h>
>> +#include <babeltrace/ctf/events.h>
>> +#include <babeltrace/ctf/iterator.h>
>
> We know from previous patches that the latest released
> babeltrace breaks gdb, due to the lookup_enum function. Does
> this catch that? I see namespacing fixes going into
> babeltrace as recently as last 15th. Do we know if
> babeltrace's API is more or less done and close to a release?
Hi Mathieu,
Could you help us with this question?
Thanks,
Hui
>
>> + ],[],
>> + [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_LIBBABELTRACE, 1, [Define if libbabeltrace is available])],
>> + [AC_MSG_RESULT([no]); btlibs= ; btinc= ])
>> + CFLAGS="$saved_CFLAGS"
>> +fi
>> +
>> +# Flags needed for UST
>
> babeltrace
>
>> +AC_SUBST(btlibs)
>> +AC_SUBST(btinc)
>> +
>
> --
> Pedro Alves
>
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] Read CTF by the ctf target
2013-03-01 2:38 ` Hui Zhu
@ 2013-05-07 13:07 ` Mathieu Desnoyers
2013-05-07 13:24 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Mathieu Desnoyers @ 2013-05-07 13:07 UTC (permalink / raw)
To: Hui Zhu; +Cc: Yao Qi, gdb-patches, Pedro Alves, lttng-dev
* Hui Zhu (teawater@gmail.com) wrote:
> On Fri, Mar 1, 2013 at 1:18 AM, Pedro Alves <palves@redhat.com> wrote:
> > (not a complete review)
> >
> > On 02/27/2013 02:17 AM, Yao Qi wrote:
> >
> >> +if test "x$with_babeltrace" != "xno"; then
> >> + saved_CFLAGS="$CFLAGS"
> >> + CFLAGS="$CFLAGS $btinc"
> >> + AC_MSG_CHECKING([for babeltrace])
> >> + AC_TRY_COMPILE([
> >> +#include <babeltrace/babeltrace.h>
> >> +#include <babeltrace/ctf/events.h>
> >> +#include <babeltrace/ctf/iterator.h>
> >
> > We know from previous patches that the latest released
> > babeltrace breaks gdb, due to the lookup_enum function. Does
> > this catch that? I see namespacing fixes going into
> > babeltrace as recently as last 15th. Do we know if
> > babeltrace's API is more or less done and close to a release?
>
> Hi Mathieu,
>
> Could you help us with this question?
We did the 1.1.0 release specifically for namespace fixes on March 23rd.
commit c99b1910bea848e8f0ae5641bb63b8f4f84f3ec0
Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Date: Sat Mar 23 13:34:22 2013 -0400
Version 1.1.0
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Thanks,
Mathieu
>
> Thanks,
> Hui
>
> >
> >> + ],[],
> >> + [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_LIBBABELTRACE, 1, [Define if libbabeltrace is available])],
> >> + [AC_MSG_RESULT([no]); btlibs= ; btinc= ])
> >> + CFLAGS="$saved_CFLAGS"
> >> +fi
> >> +
> >> +# Flags needed for UST
> >
> > babeltrace
> >
> >> +AC_SUBST(btlibs)
> >> +AC_SUBST(btinc)
> >> +
> >
> > --
> > Pedro Alves
> >
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 3/5] Read CTF by the ctf target
2013-05-07 13:07 ` Mathieu Desnoyers
@ 2013-05-07 13:24 ` Yao Qi
2013-05-07 13:28 ` Mathieu Desnoyers
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-05-07 13:24 UTC (permalink / raw)
To: Mathieu Desnoyers; +Cc: Hui Zhu, gdb-patches, Pedro Alves, lttng-dev
On 05/07/2013 09:07 PM, Mathieu Desnoyers wrote:
> We did the 1.1.0 release specifically for namespace fixes on March 23rd.
>
> commit c99b1910bea848e8f0ae5641bb63b8f4f84f3ec0
> Author: Mathieu Desnoyers<mathieu.desnoyers@efficios.com>
> Date: Sat Mar 23 13:34:22 2013 -0400
>
> Version 1.1.0
>
> Signed-off-by: Mathieu Desnoyers<mathieu.desnoyers@efficios.com>
Mathieu,
We noticed this timely release and start to use babeltrace 1.1.0 in GDB.
GDB is able read ctf via babeltrace now, just fyi... and thanks for
your help on some ctf explanations.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] Read CTF by the ctf target
2013-05-07 13:24 ` Yao Qi
@ 2013-05-07 13:28 ` Mathieu Desnoyers
0 siblings, 0 replies; 37+ messages in thread
From: Mathieu Desnoyers @ 2013-05-07 13:28 UTC (permalink / raw)
To: Yao Qi; +Cc: Hui Zhu, gdb-patches, Pedro Alves, lttng-dev
* Yao Qi (yao@codesourcery.com) wrote:
> On 05/07/2013 09:07 PM, Mathieu Desnoyers wrote:
>> We did the 1.1.0 release specifically for namespace fixes on March 23rd.
>>
>> commit c99b1910bea848e8f0ae5641bb63b8f4f84f3ec0
>> Author: Mathieu Desnoyers<mathieu.desnoyers@efficios.com>
>> Date: Sat Mar 23 13:34:22 2013 -0400
>>
>> Version 1.1.0
>>
>> Signed-off-by: Mathieu Desnoyers<mathieu.desnoyers@efficios.com>
>
> Mathieu,
> We noticed this timely release and start to use babeltrace 1.1.0 in GDB.
> GDB is able read ctf via babeltrace now, just fyi... and thanks for
> your help on some ctf explanations.
That's great !
Thank you,
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] Read CTF by the ctf target
2013-02-28 17:59 ` Pedro Alves
2013-03-01 2:38 ` Hui Zhu
@ 2013-03-01 7:55 ` Yao Qi
2013-03-01 9:15 ` Pedro Alves
1 sibling, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-03-01 7:55 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
On 03/01/2013 01:18 AM, Pedro Alves wrote:
> We know from previous patches that the latest released
> babeltrace breaks gdb, due to the lookup_enum function. Does
> this catch that? I see namespacing fixes going into
> babeltrace as recently as last 15th. Do we know if
We are using babeltrace trunk now, so the problem of lookup_enum
function goes away.
> babeltrace's API is more or less done and close to a release?
>
I am not sure, but looks the babeltrace's APIs are quite stable. I
don't see the schedule of the next babeltrace release on its website.
However, since we start to use babeltrace in GDB, I'll keep my eyes on
it and run GDB testsuite along with babeltrace trunk to make sure
nothing is broken.
>> >+ ],[],
>> >+ [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_LIBBABELTRACE, 1, [Define if libbabeltrace is available])],
>> >+ [AC_MSG_RESULT([no]); btlibs= ; btinc= ])
>> >+ CFLAGS="$saved_CFLAGS"
>> >+fi
>> >+
>> >+# Flags needed for UST
> babeltrace
>
A copy-paste error. I'll post a updated patched soon.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] Read CTF by the ctf target
2013-03-01 7:55 ` Yao Qi
@ 2013-03-01 9:15 ` Pedro Alves
0 siblings, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2013-03-01 9:15 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
On 03/01/2013 07:55 AM, Yao Qi wrote:
> On 03/01/2013 01:18 AM, Pedro Alves wrote:
>> We know from previous patches that the latest released
>> babeltrace breaks gdb, due to the lookup_enum function. Does
>> this catch that? I see namespacing fixes going into
>> babeltrace as recently as last 15th. Do we know if
>
> We are using babeltrace trunk now,
sure
> so the problem of lookup_enum function goes away.
the question really whether including those headers is enough
to catch that the user is building against a broken babeltrace,
or whether the test should try something more.
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 3/5] Read CTF by the ctf target
2013-02-27 2:19 ` [PATCH 3/5] Read CTF by the ctf target Yao Qi
2013-02-28 17:59 ` Pedro Alves
@ 2013-03-01 15:51 ` Tom Tromey
2013-03-03 10:39 ` Yao Qi
1 sibling, 1 reply; 37+ messages in thread
From: Tom Tromey @ 2013-03-01 15:51 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
>>>>> "Yao" == Yao Qi <yao@codesourcery.com> writes:
Yao> +# Where is libbabeltrace? This will be empty if lzma was not available.
Yao> +LIBBABELTRACE = @btlibs@
Yao> +LIBBABELTRACE_CFLAGS = @btinc@
This mentions "lzma", but I didn't see a link... maybe just a
cut-and-pasto.
It is clearer to use the same variable names in configure.ac and
Makefile.in. I'd suggest @LIBBABELTRACE@ and @LIBBABELTRACE_CFLAGS@ and
then change configure.ac.
Yao> +AC_ARG_WITH(babeltrace, [ --with-babeltrace=PATH Specify prefix directory for the installed BABELTRACE package
Yao> + Equivalent to --with-babeltrace-include=PATH/include
Yao> + plus --with-babeltrace-lib=PATH/lib])
It's better to use AS_HELP_STRING.
Yao> +AC_ARG_WITH(bt_include, [ --with-babeltrace-include=PATH Specify directory for installed babeltrace include files])
Lines are too long. AS_HELP_STRING will help with that :)
Yao> +AC_ARG_WITH(bt_lib, [ --with-babeltrace-lib=PATH Specify the directory for the installed babeltrace library])
Yao> +
Yao> +case $with_babeltrace in
Yao> + no)
Yao> + btlibs=
Yao> + btinc=
Yao> + ;;
Yao> + "" | yes)
Yao> + btlibs=" -lbabeltrace -lbabeltrace-ctf "
Yao> + btinc=""
Yao> + ;;
Yao> + *)
Yao> + btlibs="-L$with_babeltrace/lib -lbabeltrace -lbabeltrace-ctf"
Yao> + btinc="-I$with_babeltrace/include "
Yao> + ;;
Yao> +esac
Earlier code initialized the output variables, but it turns out there
was no need to do that.
Yao> + [AC_MSG_RESULT([yes]); AC_DEFINE(HAVE_LIBBABELTRACE, 1, [Define if libbabeltrace is available])],
Needs some line breaks.
Yao> +extern int trace_regblock_size;
Maybe in tracepoint.h instead?
I see other instances of this (in tracepoint.c and remote.c).
In most cases I think extern declarations belong in headers.
(There can be exceptions, IMO, but this doesn't seem like one.)
Yao> + const struct bt_definition *scope
Yao> + = bt_ctf_get_top_level_scope(event,
Yao> + BT_EVENT_FIELDS);
Yao> + const struct bt_definition *array
Yao> + = bt_ctf_get_field(event, scope, "contents");
Yao> +
Yao> + trace_regblock_size
Yao> + = bt_ctf_get_array_len (bt_ctf_get_decl_from_def (array));
Some spaces missing before "(".
Yao> + /* Restore the position. */
Yao> + bt_iter_set_pos(bt_ctf_get_iter (ctf_iter), pos);
Likewise.
There are more too, I didn't mark them all.
Yao> + /* xfree (regs); */
It seems like this should be deleted.
I don't really know, though.
Yao> + unsigned short mlen;
I'm curious about the rationale for the type here.
And a bit later:
Yao> + mlen = (uint16_t) bt_ctf_get_uint64 (def);
Why use uint16_t here and not mlen's real type?
But I don't understand why this has a shorter type anyhow.
Yao> + /* It's unduly pedantic to refuse to look at the executable for
Yao> + read-only pieces; so do the equivalent of readonly regions aka
Yao> + QTro packet. */
Yao> + /* FIXME account for relocation at some point. */
There's a rule against adding new FIXME comments. Sometimes I think
maybe we're too strict about this; though in practice it does seem to me
that these are rarely fixed for their own sake, but instead just as a
side effect of something else.
I suppose you could rephrase it to be a note.
Or is it fixable? It isn't clear to me in what situation this code is
invoked.
I was curious why this looks directly at the exec_bfd rather than using
something like ALL_OBJFILE_OSECTIONS. It seemed like this might help
with the relocation problem.
Yao> + if (exec_bfd)
A while ago we agreed to use the more explicit `exec_bfd != NULL' form.
Yao> + /* Didn't find anything. */
Yao> + return found;
I think this can be reached in the "did find something" case as well, so
I suggest just dropping the comment.
Yao> +/* Return what address the current frame was collected at. */
I think "Return the address at which the current frame was collected".
Yao> + /* FIXME this is a poor heuristic if multiple locations. */
Yao> + if (tp && tp->base.loc)
Yao> + addr = tp->base.loc->address;
The FIXME rule again.
Elsewhere the code just rejects multiple locations; but I'm not sure if
that is appropriate here.
Tom
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 3/5] Read CTF by the ctf target
2013-03-01 15:51 ` Tom Tromey
@ 2013-03-03 10:39 ` Yao Qi
2013-03-03 10:46 ` Yao Qi
0 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-03-03 10:39 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 2360 bytes --]
On 03/01/2013 11:50 PM, Tom Tromey wrote:
>
> Yao> + /* xfree (regs); */
>
> It seems like this should be deleted.
> I don't really know, though.
It can be removed.
>
> Yao> + unsigned short mlen;
>
> I'm curious about the rationale for the type here.
Its type should "uint16_t", because ....
> And a bit later:
>
> Yao> + mlen = (uint16_t) bt_ctf_get_uint64 (def);
>
> Why use uint16_t here and not mlen's real type?
> But I don't understand why this has a shorter type anyhow.
>
... the length of contents is 2-bytes, so we choose "uint16_t" for its type.
> Yao> + /* It's unduly pedantic to refuse to look at the executable for
> Yao> + read-only pieces; so do the equivalent of readonly regions aka
> Yao> + QTro packet. */
> Yao> + /* FIXME account for relocation at some point. */
>
> There's a rule against adding new FIXME comments. Sometimes I think
> maybe we're too strict about this; though in practice it does seem to me
> that these are rarely fixed for their own sake, but instead just as a
> side effect of something else.
Some code are copied from the tfile target (tracepoint.c). This FIXME
(and the one below) is copied from tfile target as well. My original
plan is to share some code between tfile target and ctf target after ctf
target patch goes in, as both of them are about reading trace data from
file. So I didn't pay much attention on these comments.
>
> I suppose you could rephrase it to be a note.
> Or is it fixable? It isn't clear to me in what situation this code is
> invoked.
>
Neither do I. It is beyond my knowledge to give an accurate comment or
note here, so I remove this FIXME. My next step is to get rid of the
duplication (including these comments and FIXMEs) of tfile target and
ctf target. We may need other patches to address these FIXMEs separately.
> I was curious why this looks directly at the exec_bfd rather than using
> something like ALL_OBJFILE_OSECTIONS. It seemed like this might help
> with the relocation problem.
>
> Yao> + if (exec_bfd)
>
> A while ago we agreed to use the more explicit `exec_bfd != NULL' form.
Fixed.
>
> Yao> + /* Didn't find anything. */
> Yao> + return found;
>
> I think this can be reached in the "did find something" case as well, so
> I suggest just dropping the comment.
This line is removed.
--
Yao (é½å°§)
[-- Attachment #2: 0003-Read-CTF-by-the-ctf-target.patch --]
[-- Type: text/x-patch, Size: 32650 bytes --]
From 9171e512d75495adc6cf2bc9368cbe70c6068307 Mon Sep 17 00:00:00 2001
From: Yao Qi <yao@codesourcery.com>
Date: Tue, 26 Feb 2013 17:35:53 +0800
Subject: [PATCH 3/5] Read CTF by the ctf target
This patch adds a new target 'ctf' in GDB, so GDB can read CTF trace
data in this target. Some of the implementations of target vector for
ctf target are copied from tfile target, so some of them can be shared
in the future.
We use libbabeltrace to read CTF data, because libbabeltrace is
designed to do this, and we don't have to reinvent the wheel again in
GDB. Some configure stuff is modified to check whether the
libbabeltrace is installed.
gdb:
2013-03-03 Hui Zhu <hui_zhu@mentor.com>
Yao Qi <yao@codesourcery.com>
* configure.ac: Check libbabeltrace is installed.
* config.in: Regenerate.
* configure: Regenerate.
* Makefile.in (LIBBABELTRACE, LIBBABELTRACE_CFLAGS): New.
(INTERNAL_CFLAGS_BASE): Append LIBBABELTRACE_CFLAGS.
(CLIBS): Add LIBBABELTRACE.
* ctf.c (ctx, ctf_iter, trace_dirname): New.
(ctf_close_dir, ctf_open_dir, ctf_open): New.
(ctf_close, ctf_files_info): New.
(ctf_fetch_registers, ctf_xfer_partial): New.
(ctf_get_trace_state_variable_value): New.
(ctf_get_tpnum_from_frame_event): New.
(ctf_get_traceframe_address): New.
(ctf_trace_find, ctf_has_all_memory): New.
(ctf_has_memory, ctf_has_stack): New.
(ctf_has_registers, ctf_thread_alive): New.
(ctf_traceframe_info, init_ctf_ops): New.
(_initialize_ctf): New.
* tracepoint.c (traceframe_number, tracepoint_number): Remove
'static'.
(struct traceframe_info, trace_regblock_size): Move it to ...
* tracepoint.h: ... here.
(tracepoint_number, tracepoint_number): Declare.
---
gdb/Makefile.in | 11 +-
gdb/config.in | 3 +
gdb/configure | 141 +++++++++++
gdb/configure.ac | 82 ++++++
gdb/ctf.c | 727 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/tracepoint.c | 14 +-
gdb/tracepoint.h | 14 +
7 files changed, 978 insertions(+), 14 deletions(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5be6c77..9275b12 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -154,6 +154,12 @@ LIBEXPAT = @LIBEXPAT@
# Where is lzma? This will be empty if lzma was not available.
LIBLZMA = @LIBLZMA@
+# Where is libbabeltrace? This will be empty if lbabeltrace was not
+# available.
+LIBBABELTRACE = @LIBBABELTRACE@
+LIBBABELTRACE_CFLAGS = @LIBBABELTRACE_CFLAGS@
+
+
WARN_CFLAGS = @WARN_CFLAGS@
WERROR_CFLAGS = @WERROR_CFLAGS@
GDB_WARN_CFLAGS = $(WARN_CFLAGS)
@@ -453,7 +459,8 @@ INTERNAL_CFLAGS_BASE = \
$(CFLAGS) $(GLOBAL_CFLAGS) $(PROFILE_CFLAGS) \
$(GDB_CFLAGS) $(OPCODES_CFLAGS) $(READLINE_CFLAGS) \
$(BFD_CFLAGS) $(INCLUDE_CFLAGS) $(LIBDECNUMBER_CFLAGS) \
- $(INTL_CFLAGS) $(INCGNU) $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS)
+ $(INTL_CFLAGS) $(INCGNU) $(LIBBABELTRACE_CFLAGS) \
+ $(ENABLE_CFLAGS) $(INTERNAL_CPPFLAGS)
INTERNAL_WARN_CFLAGS = $(INTERNAL_CFLAGS_BASE) $(GDB_WARN_CFLAGS)
INTERNAL_CFLAGS = $(INTERNAL_WARN_CFLAGS) $(GDB_WERROR_CFLAGS)
@@ -475,7 +482,7 @@ INTERNAL_LDFLAGS = $(CFLAGS) $(GLOBAL_CFLAGS) $(MH_LDFLAGS) $(LDFLAGS) $(CONFIG_
# LIBIBERTY appears twice on purpose.
CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
$(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) @LIBS@ @PYTHON_LIBS@ \
- $(LIBEXPAT) $(LIBLZMA) \
+ $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) \
$(LIBIBERTY) $(WIN32LIBS) $(LIBGNU)
CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \
$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU)
diff --git a/gdb/config.in b/gdb/config.in
index 9e21325..c4e8eaa 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -180,6 +180,9 @@
/* Define if your <locale.h> file defines LC_MESSAGES. */
#undef HAVE_LC_MESSAGES
+/* Define if libbabeltrace is available */
+#undef HAVE_LIBBABELTRACE
+
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
diff --git a/gdb/configure b/gdb/configure
index c54709c..1bb5d3e 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -592,6 +592,8 @@ enable_option_checking=no
ac_subst_vars='LTLIBOBJS
LIBOBJS
GDB_NM_FILE
+LIBBABELTRACE_CFLAGS
+LIBBABELTRACE
frags
target_subdir
CONFIG_UNINSTALL
@@ -819,6 +821,9 @@ with_x
enable_sim
enable_multi_ice
enable_gdbserver
+with_babeltrace
+with_bt_include
+with_bt_lib
'
ac_precious_vars='build_alias
host_alias
@@ -1532,6 +1537,15 @@ Optional Packages:
--with-tcl directory containing tcl configuration (tclConfig.sh)
--with-tk directory containing tk configuration (tkConfig.sh)
--with-x use the X Window System
+ --with-babeltrace Specify prefix directory for the installed
+ BABELTRACE package Equivalent to
+ --with-babeltrace-include=PATH/include plus
+ --with-babeltrace-lib=PATH/lib
+ --with-babeltrace-include
+ Specify directory for installed babeltrace include
+ files
+ --with-babeltrace-lib Specify the directory for the installed babeltrace
+ library
Some influential environment variables:
CC C compiler command
@@ -14090,6 +14104,133 @@ if test "$enable_gdbserver" = "yes" -a "$gdbserver_build_enabled" != "yes"; then
as_fn_error "Automatic gdbserver build is not supported for this configuration" "$LINENO" 5
fi
+# Check for babeltrace and babeltrace-ctf
+LIBBABELTRACE
+LIBBABELTRACE_CFLAGS
+
+
+# Check whether --with-babeltrace was given.
+if test "${with_babeltrace+set}" = set; then :
+ withval=$with_babeltrace;
+fi
+
+
+
+# Check whether --with-bt_include was given.
+if test "${with_bt_include+set}" = set; then :
+ withval=$with_bt_include;
+fi
+
+
+# Check whether --with-bt_lib was given.
+if test "${with_bt_lib+set}" = set; then :
+ withval=$with_bt_lib;
+fi
+
+
+case $with_babeltrace in
+ no)
+ LIBBABELTRACE=
+ LIBBABELTRACE_CFLAGS=
+ ;;
+ "" | yes)
+ LIBBABELTRACE=" -lbabeltrace -lbabeltrace-ctf "
+ LIBBABELTRACE_CFLAGS=""
+ ;;
+ *)
+ LIBBABELTRACE="-L$with_babeltrace/lib -lbabeltrace -lbabeltrace-ctf"
+ LIBBABELTRACE_CFLAGS="-I$with_babeltrace/include "
+ ;;
+esac
+if test "x$with_bt_include" != x; then
+ LIBBABELTRACE_CFLAGS="-I$with_bt_include "
+fi
+if test "x$with_bt_lib" != x; then
+ LIBBABELTRACE="-L$with_bt_lib -lbabeltrace -lbabeltrace-ctf"
+fi
+
+if test "x$with_babeltrace" != "xno"; then
+ saved_CFLAGS=$CFLAGS
+ saved_LIBS=$LIBS
+
+ CFLAGS="$CFLAGS $LIBBABELTRACE_CFLAGS"
+ LIBS="$LIBS $LIBBABELTRACE"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for babeltrace" >&5
+$as_echo_n "checking for babeltrace... " >&6; }
+if test "${ac_cv_has_babeltrace_header+set}" = set; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/iterator.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }; ac_cv_has_babeltrace_header=yes
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }; ac_cv_has_babeltrace_header=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_has_babeltrace_header" >&5
+$as_echo "$ac_cv_has_babeltrace_header" >&6; }
+
+ if test "x$ac_cv_has_has_babeltrace_header" != "xno"; then
+ # Check whether there is a function named 'lookup_enum' in
+ # babeltrace library. We can't use the babeltrace library
+ # in which function 'lookup_enum' is defined. This function
+ # is renamed to 'bt_lookup_name' in recent babeltrace.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for broken babeltrace" >&5
+$as_echo_n "checking for broken babeltrace... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+lookup_enum (NULL, NULL, 0)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }; LIBBABELTRACE= ; LIBBABELTRACE_CFLAGS=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; };
+$as_echo "#define HAVE_LIBBABELTRACE 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ else
+ LIBBABELTRACE=
+ LIBBABELTRACE_CFLAGS=
+ fi
+
+ CFLAGS="$saved_CFLAGS"
+ LIBS="$saved_LIBS"
+fi
+
+# Flags needed for babeltrace.
+
+
+
+
# If nativefile (NAT_FILE) is not set in config/*/*.m[ht] files, we link
# to an empty version.
diff --git a/gdb/configure.ac b/gdb/configure.ac
index e501766..9e88cd2 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -2318,6 +2318,88 @@ if test "$enable_gdbserver" = "yes" -a "$gdbserver_build_enabled" != "yes"; then
AC_MSG_ERROR(Automatic gdbserver build is not supported for this configuration)
fi
+# Check for babeltrace and babeltrace-ctf
+LIBBABELTRACE
+LIBBABELTRACE_CFLAGS
+
+AC_ARG_WITH(babeltrace,
+ AC_HELP_STRING([--with-babeltrace],
+ [Specify prefix directory for the installed
+ BABELTRACE package Equivalent to
+ --with-babeltrace-include=PATH/include
+ plus --with-babeltrace-lib=PATH/lib]))
+
+AC_ARG_WITH(bt_include,
+ AC_HELP_STRING([--with-babeltrace-include],
+ [Specify directory for installed babeltrace include
+ files]))
+AC_ARG_WITH(bt_lib,
+ AC_HELP_STRING([--with-babeltrace-lib],
+ [Specify the directory for the installed babeltrace
+ library]))
+
+case $with_babeltrace in
+ no)
+ LIBBABELTRACE=
+ LIBBABELTRACE_CFLAGS=
+ ;;
+ "" | yes)
+ LIBBABELTRACE=" -lbabeltrace -lbabeltrace-ctf "
+ LIBBABELTRACE_CFLAGS=""
+ ;;
+ *)
+ LIBBABELTRACE="-L$with_babeltrace/lib -lbabeltrace -lbabeltrace-ctf"
+ LIBBABELTRACE_CFLAGS="-I$with_babeltrace/include "
+ ;;
+esac
+if test "x$with_bt_include" != x; then
+ LIBBABELTRACE_CFLAGS="-I$with_bt_include "
+fi
+if test "x$with_bt_lib" != x; then
+ LIBBABELTRACE="-L$with_bt_lib -lbabeltrace -lbabeltrace-ctf"
+fi
+
+if test "x$with_babeltrace" != "xno"; then
+ saved_CFLAGS=$CFLAGS
+ saved_LIBS=$LIBS
+
+ CFLAGS="$CFLAGS $LIBBABELTRACE_CFLAGS"
+ LIBS="$LIBS $LIBBABELTRACE"
+
+ AC_CACHE_CHECK([for babeltrace], ac_cv_has_babeltrace_header,
+ [AC_TRY_COMPILE([
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/iterator.h>
+ ],[],
+ [AC_MSG_RESULT([yes]); ac_cv_has_babeltrace_header=yes],
+ [AC_MSG_RESULT([no]); ac_cv_has_babeltrace_header=no])])
+
+ if test "x$ac_cv_has_has_babeltrace_header" != "xno"; then
+ # Check whether there is a function named 'lookup_enum' in
+ # babeltrace library. We can't use the babeltrace library
+ # in which function 'lookup_enum' is defined. This function
+ # is renamed to 'bt_lookup_name' in recent babeltrace.
+ AC_MSG_CHECKING([for broken babeltrace])
+ AC_TRY_LINK([#include <stdio.h>],
+ [lookup_enum (NULL, NULL, 0)],
+ [AC_MSG_RESULT([yes]); LIBBABELTRACE= ; LIBBABELTRACE_CFLAGS= ],
+ [AC_MSG_RESULT([no]); AC_DEFINE(HAVE_LIBBABELTRACE, 1,
+ [Define if libbabeltrace is available])])
+ else
+ LIBBABELTRACE=
+ LIBBABELTRACE_CFLAGS=
+ fi
+
+ CFLAGS="$saved_CFLAGS"
+ LIBS="$saved_LIBS"
+fi
+
+# Flags needed for babeltrace.
+AC_SUBST(LIBBABELTRACE)
+AC_SUBST(LIBBABELTRACE_CFLAGS)
+
+
# If nativefile (NAT_FILE) is not set in config/*/*.m[ht] files, we link
# to an empty version.
diff --git a/gdb/ctf.c b/gdb/ctf.c
index c243fd2..1e35780 100644
--- a/gdb/ctf.c
+++ b/gdb/ctf.c
@@ -23,6 +23,7 @@
#include "ctf.h"
#include "tracepoint.h"
#include "regcache.h"
+#include "exec.h"
#include <ctype.h>
@@ -707,3 +708,729 @@ ctf_trace_file_writer_new (void)
return (struct trace_file_writer *) writer;
}
+
+#ifdef HAVE_LIBBABELTRACE
+/* Use libbabeltrace to read CTF data. The libbabeltrace provides
+ iterator to iterate over each event in CTF data and APIs to get
+ details of event and packet, so it is very convenient to use
+ libbabeltrace to access events in CTF. */
+
+#include <babeltrace/babeltrace.h>
+#include <babeltrace/ctf/events.h>
+#include <babeltrace/ctf/iterator.h>
+
+/* The struct pointer for current CTF directory. */
+static struct bt_context *ctx = NULL;
+static struct bt_ctf_iter *ctf_iter = NULL;
+/* The name of CTF directory. */
+static char *trace_dirname;
+
+static struct target_ops ctf_ops;
+
+static void
+ctf_close_dir (void)
+{
+ if (ctf_iter)
+ {
+ bt_ctf_iter_destroy (ctf_iter);
+ ctf_iter = NULL;
+ }
+ if (ctx)
+ {
+ bt_context_put (ctx);
+ ctx = NULL;
+ }
+}
+
+/* Open CTF trace data in DIRNAME. */
+
+static void
+ctf_open_dir (char *dirname)
+{
+ int ret;
+ struct bt_iter_pos begin_pos;
+ struct bt_iter_pos *pos;
+
+ ctx = bt_context_create ();
+ if (!ctx)
+ error (_("Unable to initialize libbabeltrace"));
+ ret = bt_context_add_trace (ctx, dirname, "ctf", NULL, NULL, NULL);
+ if (ret < 0)
+ {
+ ctf_close_dir ();
+ error (_("Unable to use libbabeltrace open \"%s\""), dirname);
+ }
+
+ begin_pos.type = BT_SEEK_BEGIN;
+ ctf_iter = bt_ctf_iter_create (ctx, &begin_pos, NULL);
+ if (ctf_iter == NULL)
+ {
+ ctf_close_dir ();
+ error (_("Unable to use libbabeltrace open \"%s\""), dirname);
+ }
+
+ /* Iterate over events, and look for an event for register block
+ to set trace_regblock_size. */
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ while (1)
+ {
+ const char *name;
+ struct bt_ctf_event *event;
+
+ event = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event);
+
+ if (name == NULL)
+ break;
+ else if (strcmp (name, "register") == 0)
+ {
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope (event,
+ BT_EVENT_FIELDS);
+ const struct bt_definition *array
+ = bt_ctf_get_field (event, scope, "contents");
+
+ trace_regblock_size
+ = bt_ctf_get_array_len (bt_ctf_get_decl_from_def (array));
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+
+}
+
+static void
+ctf_open (char *dirname, int from_tty)
+{
+ target_preopen (from_tty);
+ if (!dirname)
+ error (_("No CTF directory specified."));
+
+ ctf_open_dir (dirname);
+
+ trace_dirname = xstrdup (dirname);
+ push_target (&ctf_ops);
+}
+
+static void
+ctf_close (int quitting)
+{
+ ctf_close_dir ();
+ xfree (trace_dirname);
+ trace_dirname = NULL;
+}
+
+static void
+ctf_files_info (struct target_ops *t)
+{
+ printf_filtered ("\t`%s'\n", trace_dirname);
+}
+
+/* Iterate over events whose name is "register" in current frame,
+ extract contents from events, and set REGCACHE with the contents.
+ If no matched events are found, mark registers unavailable. */
+
+static void
+ctf_fetch_registers (struct target_ops *ops,
+ struct regcache *regcache, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int offset, regn, regsize, pc_regno;
+ char *regs = NULL;
+ struct bt_ctf_event *event = NULL;
+ struct bt_iter_pos *pos;
+
+ /* An uninitialized reg size says we're not going to be
+ successful at getting register blocks. */
+ if (!trace_regblock_size)
+ return;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ while (1)
+ {
+ const char *name;
+ struct bt_ctf_event *event1;
+
+ event1 = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event1);
+
+ if (name == NULL || strcmp (name, "frame") == 0)
+ break;
+ else if (strcmp (name, "register") == 0)
+ {
+ event = event1;
+ break;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+
+ if (event != NULL)
+ {
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope (event,
+ BT_EVENT_FIELDS);
+ const struct bt_definition *array
+ = bt_ctf_get_field (event, scope, "contents");
+
+ regs = bt_ctf_get_char_array (array);
+ /* Assume the block is laid out in GDB register number order,
+ each register with the size that it has in GDB. */
+ offset = 0;
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ {
+ regsize = register_size (gdbarch, regn);
+ /* Make sure we stay within block bounds. */
+ if (offset + regsize >= trace_regblock_size)
+ break;
+ if (regcache_register_status (regcache, regn) == REG_UNKNOWN)
+ {
+ if (regno == regn)
+ {
+ regcache_raw_supply (regcache, regno, regs + offset);
+ break;
+ }
+ else if (regno == -1)
+ {
+ regcache_raw_supply (regcache, regn, regs + offset);
+ }
+ }
+ offset += regsize;
+ }
+ return;
+ }
+
+ regs = alloca (trace_regblock_size);
+
+ /* We get here if no register data has been found. Mark registers
+ as unavailable. */
+ for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ regcache_raw_supply (regcache, regn, NULL);
+
+ /* We can often usefully guess that the PC is going to be the same
+ as the address of the tracepoint. */
+ pc_regno = gdbarch_pc_regnum (gdbarch);
+ if (pc_regno >= 0 && (regno == -1 || regno == pc_regno))
+ {
+ struct tracepoint *tp = get_tracepoint (tracepoint_number);
+
+ if (tp && tp->base.loc)
+ {
+ /* But don't try to guess if tracepoint is multi-location... */
+ if (tp->base.loc->next)
+ {
+ warning (_("Tracepoint %d has multiple "
+ "locations, cannot infer $pc"),
+ tp->base.number);
+ return;
+ }
+ /* ... or does while-stepping. */
+ if (tp->step_count > 0)
+ {
+ warning (_("Tracepoint %d does while-stepping, "
+ "cannot infer $pc"),
+ tp->base.number);
+ return;
+ }
+
+ store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
+ gdbarch_byte_order (gdbarch),
+ tp->base.loc->address);
+ regcache_raw_supply (regcache, pc_regno, regs);
+ }
+ }
+}
+
+/* Iterate over events whose name is started with "memory_" in
+ current frame, extract the address and length from events. If
+ OFFSET is within the range, read the contents from events to
+ READBUF. */
+
+static LONGEST
+ctf_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset,
+ LONGEST len)
+{
+ /* We're only doing regular memory for now. */
+ if (object != TARGET_OBJECT_MEMORY)
+ return -1;
+
+ if (readbuf == NULL)
+ error (_("ctf_xfer_partial: trace file is read-only"));
+
+ if (traceframe_number != -1)
+ {
+ struct bt_iter_pos *pos;
+ int i = 0;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ /* Iterate through the traceframe's blocks, looking for
+ memory. */
+ while (1)
+ {
+ ULONGEST maddr, amt;
+ uint16_t mlen;
+ enum bfd_endian byte_order
+ = gdbarch_byte_order (target_gdbarch ());
+ const struct bt_definition *scope;
+ const struct bt_definition *def;
+ struct bt_ctf_event *event
+ = bt_ctf_iter_read_event (ctf_iter);
+ const char *name = bt_ctf_event_name (event);
+
+ if (strcmp (name, "frame") == 0)
+ break;
+ else if (strncmp (name, "memory_", 7) != 0)
+ {
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+
+ continue;
+ }
+
+ scope = bt_ctf_get_top_level_scope (event,
+ BT_EVENT_FIELDS);
+
+ def = bt_ctf_get_field (event, scope, "address");
+ maddr = bt_ctf_get_uint64 (def);
+ def = bt_ctf_get_field (event, scope, "length");
+ mlen = (uint16_t) bt_ctf_get_uint64 (def);
+
+ /* If the block includes the first part of the desired
+ range, return as much it has; GDB will re-request the
+ remainder, which might be in a different block of this
+ trace frame. */
+ if (maddr <= offset && offset < (maddr + mlen))
+ {
+ const struct bt_definition *array
+ = bt_ctf_get_field (event, scope, "contents");
+ const struct bt_declaration *decl
+ = bt_ctf_get_decl_from_def (array);
+ gdb_byte *contents;
+
+ gdb_assert (mlen == bt_ctf_get_array_len (decl));
+ contents = bt_ctf_get_char_array (array);
+
+ amt = (maddr + mlen) - offset;
+ if (amt > len)
+ amt = len;
+
+ memcpy (readbuf, &contents[offset - maddr], amt);
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+
+ return amt;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+ }
+
+ /* It's unduly pedantic to refuse to look at the executable for
+ read-only pieces; so do the equivalent of readonly regions aka
+ QTro packet. */
+ if (exec_bfd != NULL)
+ {
+ asection *s;
+ bfd_size_type size;
+ bfd_vma vma;
+
+ for (s = exec_bfd->sections; s; s = s->next)
+ {
+ if ((s->flags & SEC_LOAD) == 0
+ || (s->flags & SEC_READONLY) == 0)
+ continue;
+
+ vma = s->vma;
+ size = bfd_get_section_size (s);
+ if (vma <= offset && offset < (vma + size))
+ {
+ ULONGEST amt;
+
+ amt = (vma + size) - offset;
+ if (amt > len)
+ amt = len;
+
+ amt = bfd_get_section_contents (exec_bfd, s,
+ readbuf, offset - vma, amt);
+ return amt;
+ }
+ }
+ }
+
+ /* Indicate failure to find the requested memory block. */
+ return -1;
+}
+
+/* Iterate over events whose name is "tsv" in current frame. When the
+ trace variable is found, set the value of it to *VAL and return
+ true, otherwise return false. */
+
+static int
+ctf_get_trace_state_variable_value (int tsvnum, LONGEST *val)
+{
+ struct bt_iter_pos *pos;
+ int found = 0;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ /* Iterate through the traceframe's blocks, looking for 'V'
+ block. */
+ while (1)
+ {
+ struct bt_ctf_event *event
+ = bt_ctf_iter_read_event (ctf_iter);
+ const char *name = bt_ctf_event_name (event);
+
+ if (strcmp (name, "frame") == 0)
+ break;
+ else if (strcmp (name, "tsv") == 0)
+ {
+ const struct bt_definition *scope;
+ const struct bt_definition *def;
+
+ scope = bt_ctf_get_top_level_scope (event,
+ BT_EVENT_FIELDS);
+
+ def = bt_ctf_get_field (event, scope, "num");
+ if (tsvnum == (int) bt_ctf_get_uint64 (def))
+ {
+ def = bt_ctf_get_field (event, scope, "val");
+ *val = bt_ctf_get_uint64 (def);
+
+ found = 1;
+ break;
+ }
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+
+ return found;
+}
+
+/* Return the tracepoint number in "frame" event. */
+
+static int
+ctf_get_tpnum_from_frame_event (struct bt_ctf_event *event)
+{
+ /* The packet context of events has a field "tpnum". */
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope (event, BT_STREAM_PACKET_CONTEXT);
+ uint64_t tpnum
+ = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, "tpnum"));
+
+ return (int) tpnum;
+}
+
+/* Return the address at which the current frame was collected. */
+
+static ULONGEST
+ctf_get_traceframe_address (void)
+{
+ struct bt_ctf_event *event = NULL;
+ struct bt_iter_pos *pos
+ = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ ULONGEST addr = 0;
+
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ while (1)
+ {
+ const char *name;
+ struct bt_ctf_event *event1;
+
+ event1 = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event1);
+
+ if (name == NULL)
+ break;
+ else if (strcmp (name, "frame") == 0)
+ {
+ event = event1;
+ break;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+
+ if (event != NULL)
+ {
+ int tpnum = ctf_get_tpnum_from_frame_event (event);
+ struct tracepoint *tp
+ = get_tracepoint_by_number_on_target (tpnum);
+
+ if (tp && tp->base.loc)
+ addr = tp->base.loc->address;
+ }
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+
+ return addr;
+}
+
+/* Iterate the events whose name is "frame", extract the tracepoint
+ number in it. Return traceframe number when matched. */
+
+static int
+ctf_trace_find (enum trace_find_type type, int num,
+ ULONGEST addr1, ULONGEST addr2, int *tpp)
+{
+ int ret = -1;
+ int tfnum = 0;
+ int found = 0;
+ struct bt_iter_pos pos;
+
+ if (num == -1)
+ {
+ if (tpp)
+ *tpp = -1;
+ return -1;
+ }
+
+ /* Set iterator back to the beginning. */
+ pos.type = BT_SEEK_BEGIN;
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), &pos);
+
+ while (1)
+ {
+ int id;
+ struct bt_ctf_event *event;
+ const char *name;
+
+ event = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event);
+
+ if (event == NULL || name == NULL)
+ return -1;
+
+ if (strcmp (name, "frame") == 0)
+ {
+ ULONGEST tfaddr;
+
+ if (type == tfind_number)
+ {
+ /* Looking for a specific trace frame. */
+ if (tfnum == num)
+ found = 1;
+ }
+ else
+ {
+ /* Start from the _next_ trace frame. */
+ if (tfnum > traceframe_number)
+ {
+ switch (type)
+ {
+ case tfind_tp:
+ {
+ struct tracepoint *tp = get_tracepoint (num);
+
+ if (tp != NULL
+ && (tp->number_on_target
+ == ctf_get_tpnum_from_frame_event (event)))
+ found = 1;
+ break;
+ }
+ case tfind_pc:
+ tfaddr = ctf_get_traceframe_address ();
+ if (tfaddr == addr1)
+ found = 1;
+ break;
+ case tfind_range:
+ tfaddr = ctf_get_traceframe_address ();
+ if (addr1 <= tfaddr && tfaddr <= addr2)
+ found = 1;
+ break;
+ case tfind_outside:
+ tfaddr = ctf_get_traceframe_address ();
+ if (!(addr1 <= tfaddr && tfaddr <= addr2))
+ found = 1;
+ break;
+ default:
+ internal_error (__FILE__, __LINE__, _("unknown tfind type"));
+ }
+ }
+ }
+ if (found)
+ {
+ if (tpp)
+ *tpp = ctf_get_tpnum_from_frame_event (event);
+
+ /* Skip the event "frame". */
+ bt_iter_next (bt_ctf_get_iter (ctf_iter));
+
+ return tfnum;
+ }
+ tfnum++;
+ }
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ return -1;
+ }
+
+ return -1;
+}
+
+static int
+ctf_has_all_memory (struct target_ops *ops)
+{
+ return 0;
+}
+
+static int
+ctf_has_memory (struct target_ops *ops)
+{
+ return 0;
+}
+
+static int
+ctf_has_stack (struct target_ops *ops)
+{
+ return traceframe_number != -1;
+}
+
+static int
+ctf_has_registers (struct target_ops *ops)
+{
+ return traceframe_number != -1;
+}
+
+static int
+ctf_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+ return 1;
+}
+
+/* Iterate the events whose name is started with "memory_", in current
+ frame, extract memory range information, and return them in
+ traceframe_info. */
+
+static struct traceframe_info *
+ctf_traceframe_info (void)
+{
+ struct traceframe_info *info = XCNEW (struct traceframe_info);
+ const char *name;
+ struct bt_iter_pos *pos;
+
+ /* Save the current position. */
+ pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter));
+ gdb_assert (pos->type == BT_SEEK_RESTORE);
+
+ do
+ {
+ struct bt_ctf_event *event
+ = bt_ctf_iter_read_event (ctf_iter);
+
+ name = bt_ctf_event_name (event);
+
+ if (name == NULL || strcmp (name, "register") == 0
+ || strcmp (name, "frame") == 0)
+ ;
+ else if (strncmp (name, "memory_", 7) == 0)
+ {
+ const struct bt_definition *scope
+ = bt_ctf_get_top_level_scope (event,
+ BT_EVENT_FIELDS);
+ const struct bt_definition *def;
+ struct mem_range *r;
+
+ r = VEC_safe_push (mem_range_s, info->memory, NULL);
+ def = bt_ctf_get_field (event, scope, "address");
+ r->start = bt_ctf_get_uint64 (def);
+
+ def = bt_ctf_get_field (event, scope, "length");
+ r->length = (uint16_t) bt_ctf_get_uint64 (def);
+ }
+ else
+ warning (_("Unhandled trace block type (%s) "
+ "while building trace frame info."),
+ name);
+
+ if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0)
+ break;
+ }
+ while (name != NULL && strcmp (name, "frame") != 0);
+
+ /* Restore the position. */
+ bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos);
+
+ /* traceframe_walk_blocks (build_traceframe_info, 0, info); */
+ return info;
+}
+
+static void
+init_ctf_ops (void)
+{
+ ctf_ops.to_shortname = "ctf";
+ ctf_ops.to_longname = "CTF file";
+ ctf_ops.to_doc = "Use a CTF directory as a target.\n\
+Specify the filename of the CTF directory.";
+ ctf_ops.to_open = ctf_open;
+ ctf_ops.to_close = ctf_close;
+ ctf_ops.to_fetch_registers = ctf_fetch_registers;
+ ctf_ops.to_xfer_partial = ctf_xfer_partial;
+ ctf_ops.to_files_info = ctf_files_info;
+ ctf_ops.to_trace_find = ctf_trace_find;
+ ctf_ops.to_get_trace_state_variable_value
+ = ctf_get_trace_state_variable_value;
+ ctf_ops.to_stratum = process_stratum;
+ ctf_ops.to_has_all_memory = ctf_has_all_memory;
+ ctf_ops.to_has_memory = ctf_has_memory;
+ ctf_ops.to_has_stack = ctf_has_stack;
+ ctf_ops.to_has_registers = ctf_has_registers;
+ ctf_ops.to_traceframe_info = ctf_traceframe_info;
+ ctf_ops.to_thread_alive = ctf_thread_alive;
+ ctf_ops.to_magic = OPS_MAGIC;
+}
+
+#endif
+
+/* -Wmissing-prototypes */
+extern initialize_file_ftype _initialize_ctf;
+
+/* module initialization */
+void
+_initialize_ctf (void)
+{
+#ifdef HAVE_LIBBABELTRACE
+ init_ctf_ops ();
+
+ add_target (&ctf_ops);
+#endif
+}
diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c
index eab4043..f62c90e 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -129,14 +129,6 @@ extern void (*deprecated_readline_end_hook) (void);
typedef struct trace_state_variable tsv_s;
DEF_VEC_O(tsv_s);
-/* An object describing the contents of a traceframe. */
-
-struct traceframe_info
-{
- /* Collected memory. */
- VEC(mem_range_s) *memory;
-};
-
static VEC(tsv_s) *tvariables;
/* The next integer to assign to a variable. */
@@ -144,10 +136,10 @@ static VEC(tsv_s) *tvariables;
static int next_tsv_number = 1;
/* Number of last traceframe collected. */
-static int traceframe_number;
+int traceframe_number;
/* Tracepoint for last traceframe collected. */
-static int tracepoint_number;
+int tracepoint_number;
/* Symbol for function for last traceframe collected. */
static struct symbol *traceframe_fun;
@@ -3258,8 +3250,6 @@ static const struct trace_file_write_ops tfile_write_ops =
#define TRACE_WRITE_V_BLOCK(writer, num, val) \
writer->ops->frame_ops->write_v_block ((writer), (num), (val))
-extern int trace_regblock_size;
-
/* 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
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index 67a7b87..8aa7d02 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -24,6 +24,14 @@
#include "memrange.h"
#include "gdb_vecs.h"
+/* An object describing the contents of a traceframe. */
+
+struct traceframe_info
+{
+ /* Collected memory. */
+ VEC(mem_range_s) *memory;
+};
+
/* A trace state variable is a value managed by a target being
traced. A trace state variable (or tsv for short) can be accessed
and assigned to by tracepoint actions and conditionals, but is not
@@ -142,6 +150,12 @@ struct trace_status *current_trace_status (void);
extern char *default_collect;
+extern int tracepoint_number;
+
+extern int traceframe_number;
+
+extern int trace_regblock_size;
+
/* Struct to collect random info about tracepoints on the target. */
struct uploaded_tp
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 3/5] Read CTF by the ctf target
2013-03-03 10:39 ` Yao Qi
@ 2013-03-03 10:46 ` Yao Qi
0 siblings, 0 replies; 37+ messages in thread
From: Yao Qi @ 2013-03-03 10:46 UTC (permalink / raw)
To: gdb-patches
On 03/03/2013 06:38 PM, Yao Qi wrote:
> 2013-03-03 Hui Zhu<hui_zhu@mentor.com>
> Yao Qi<yao@codesourcery.com>
>
> * configure.ac: Check libbabeltrace is installed.
This changelog entry should be updated a little bit,
* configure.ac: Check libbabeltrace is installed and
libbabeltrace is broken or not.
because in configure.ac, we use macro AC_TRY_LINK to see function
'lookup_enum' can be found in libbabeltrace or not. If 'lookup_enum' is
found in libbabeltrace, we don't use it.
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH 4/5] ctf doc and NEWS.
2013-02-27 2:18 [PATCH 0/5, 2nd try] CTF Support Yao Qi
` (2 preceding siblings ...)
2013-02-27 2:19 ` [PATCH 3/5] Read CTF by the ctf target Yao Qi
@ 2013-02-27 2:19 ` Yao Qi
2013-02-27 18:39 ` Eli Zaretskii
2013-02-27 2:19 ` [PATCH 2/5] Save trace into CTF format Yao Qi
4 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-02-27 2:19 UTC (permalink / raw)
To: gdb-patches
This is almost the same as previous version, except that I update the
example according to the current output of GDB and write three NEWS
entries for this series.
2013-02-27 Hui Zhu <hui_zhu@mentor.com>
Yao Qi <yao@codesourcery.com>
* gdb.texinfo (Trace Files): Add "tsave -ctf" and target ctf.
* NEWS: Mention these changes.
---
gdb/NEWS | 7 +++++++
gdb/doc/gdb.texinfo | 38 ++++++++++++++++++++++++++++++++------
2 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 0877aa2..ba312eb 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -69,6 +69,8 @@ Lynx 178 PowerPC powerpc-*-lynx*178
* The command 'info tracepoints' can now display 'installed on target'
or 'not installed on target' for each non-pending location of tracepoint.
+* The command 'tsave' can now support new option '-ctf' to save trace
+ buffer into Common Trace Format.
* New configure options
@@ -78,6 +80,9 @@ Lynx 178 PowerPC powerpc-*-lynx*178
Release versions, on the other hand, are built without -lmcheck
by default. The --enable-libmcheck/--disable-libmcheck configure
options allow the user to override that default.
+--with-babeltrace/--with-babeltrace-include/--with-babeltrace-lib
+ This configure option allows the user to build GDB with
+ libbabeltrace by which GDB can read Common Trace Format data.
* New commands (for set/show, see "New options" below)
@@ -153,6 +158,8 @@ show filename-display
** Output of the "-trace-status" command includes a "trace-file" field
containing the name of the trace file being examined. This field is
optional, and only present when examining a trace file.
+ ** The -trace-save MI command can optionally save trace buffer into Common
+ Trace Format now.
* GDB now supports the "mini debuginfo" section, .gnu_debugdata.
You must have the LZMA library available when configuring GDB for this
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5f39d2e..9080763 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -12120,6 +12120,7 @@ of trace data, via the @code{target tfile} command.
@kindex tsave
@item tsave [ -r ] @var{filename}
+@itemx tsave [-ctf] @var{dirname}
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
@@ -12128,16 +12129,41 @@ 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.)
+By default, this command will save trace frame into tfile format.
+You can supply the optional argument @code{-ctf} to save date in CTF
+format. The @dfn{Common Trace Format} (CTF) is proposed as a trace format
+that can be shared by multiple debugging and tracing tools. Please go to
+@indicateurl{http://www.efficios.com/ctf} to get more information.
@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.
+@kindex target ctf
+@kindex ctf
+@itemx target ctf @var{dirname}
+Use the file named @var{filename} or directory named @var{dirname} 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} or @var{dirname} must be on a filesystem accessible to
+the host.
+
+@smallexample
+(@value{GDBP}) target ctf ctf.ctf
+(@value{GDBP}) tfind
+Found trace frame 0, tracepoint 2
+39 ++a; /* set tracepoint 1 here */
+(@value{GDBP}) tdump
+Data collected at tracepoint 2, trace frame 0:
+i = 0
+a = 0
+b = 1 '\001'
+c = @{"123", "456", "789", "123", "456", "789"@}
+d = @{@{@{a = 1, b = 2@}, @{a = 3, b = 4@}@}, @{@{a = 5, b = 6@}, @{a = 7, b = 8@}@}@}
+(@value{GDBP}) p b
+$1 = 1
+@end smallexample
@end table
--
1.7.7.6
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 4/5] ctf doc and NEWS.
2013-02-27 2:19 ` [PATCH 4/5] ctf doc and NEWS Yao Qi
@ 2013-02-27 18:39 ` Eli Zaretskii
0 siblings, 0 replies; 37+ messages in thread
From: Eli Zaretskii @ 2013-02-27 18:39 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
> From: Yao Qi <yao@codesourcery.com>
> Date: Wed, 27 Feb 2013 10:17:38 +0800
>
> This is almost the same as previous version, except that I update the
> example according to the current output of GDB and write three NEWS
> entries for this series.
Thanks.
> +* The command 'tsave' can now support new option '-ctf' to save trace
> + buffer into Common Trace Format.
"to save the trace buffer in Common Trace Format"
> +--with-babeltrace/--with-babeltrace-include/--with-babeltrace-lib
> + This configure option allows the user to build GDB with
> + libbabeltrace by which GDB can read Common Trace Format data.
^^^^^^^^
"using which"
> + ** The -trace-save MI command can optionally save trace buffer into Common
> + Trace Format now. ^^^^
"in"
> +By default, this command will save trace frame into tfile format.
^^^^
"in"
> @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.
> +@kindex target ctf
> +@kindex ctf
> +@itemx target ctf @var{dirname}
Please have all the @kindex entries before the @item/@itemx lines.
OK with those changes.
^ permalink raw reply [flat|nested] 37+ messages in thread
* [PATCH 2/5] Save trace into CTF format
2013-02-27 2:18 [PATCH 0/5, 2nd try] CTF Support Yao Qi
` (3 preceding siblings ...)
2013-02-27 2:19 ` [PATCH 4/5] ctf doc and NEWS Yao Qi
@ 2013-02-27 2:19 ` Yao Qi
2013-02-28 13:17 ` Yao Qi
4 siblings, 1 reply; 37+ messages in thread
From: Yao Qi @ 2013-02-27 2:19 UTC (permalink / raw)
To: gdb-patches
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
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-02-27 2:19 ` [PATCH 2/5] Save trace into CTF format Yao Qi
@ 2013-02-28 13:17 ` Yao Qi
2013-02-28 13:17 ` Andreas Schwab
2013-03-01 19:22 ` Tom Tromey
0 siblings, 2 replies; 37+ messages in thread
From: Yao Qi @ 2013-02-28 13:17 UTC (permalink / raw)
To: gdb-patches
On 02/27/2013 10:17 AM, Yao Qi wrote:
> +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);
"&one" should be cast to "gdb_byte *" instead of "void *".
> +
> +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);
Writing a unsigned 32-bit integer is popular in this patch, so I add a
new function "ctf_save_write_uint32" to avoid many casts in the code.
Here is the updated patch to address them.
--
Yao (é½å°§)
gdb:
2013-02-28 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 | 690
++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/ctf.h | 24 ++
gdb/mi/mi-main.c | 11 +-
gdb/tracepoint.c | 36 +++-
gdb/tracepoint.h | 2 +
6 files changed, 760 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..58acf4c
--- /dev/null
+++ b/gdb/ctf.c
@@ -0,0 +1,690 @@
+/* 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;
+}
+
+/* Write a unsigned 32-bit integer to datastream file represented by
+ HANDLER. */
+
+static int
+ctf_save_write_uint32 (struct trace_write_handler *handler,
+ uint32_t u32)
+{
+ return ctf_save_write (handler, (gdb_byte *) &u32, 4);
+}
+
+/* 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_uint32 (&data->tcs, u32);
+
+ /* content_size. */
+ u32 = 0;
+ ctf_save_write_uint32 (&data->tcs, u32);
+
+ /* packet_size. */
+ u32 = 12;
+ if (data->tcs.packet_start + u32 < 4096)
+ u32 = CTF_FILE_MIN_SIZE - data->tcs.packet_start;
+
+ u32 *= TARGET_CHAR_BIT;
+ ctf_save_write_uint32 (&data->tcs, u32);
+
+ /* 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_uint32 (&data->tcs, u32);
+ /* 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, (gdb_byte *) &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, (gdb_byte *) &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, (gdb_byte *) &event_id, 4, 4);
+
+ /* Address. */
+ ctf_save_align_write (&data->tcs, (gdb_byte *) &addr, 8, 8);
+
+ /* Length. */
+ ctf_save_align_write (&data->tcs, (gdb_byte *) &length, 2, 2);
+
+ /* Contents. */
+ ctf_save_align_write (&data->tcs, (gdb_byte *) 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, (gdb_byte *) &one, 4, 4);
+
+ /* val. */
+ ctf_save_align_write (&data->tcs, (gdb_byte *) &val, 8, 8);
+ /* num. */
+ ctf_save_align_write (&data->tcs, (gdb_byte *) &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_uint32 (&data->tcs, u32);
+
+ /* Write the packet size. */
+ u32 += 4 * TARGET_CHAR_BIT;
+ ctf_save_write_uint32 (&data->tcs, u32);
+
+ 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_uint32 (&data->tcs, u32);
+ 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 390f779..c2720e8 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);
}
@@ -5552,6 +5579,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
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-02-28 13:17 ` Yao Qi
@ 2013-02-28 13:17 ` Andreas Schwab
2013-02-28 17:19 ` Pedro Alves
2013-03-01 19:22 ` Tom Tromey
1 sibling, 1 reply; 37+ messages in thread
From: Andreas Schwab @ 2013-02-28 13:17 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
Yao Qi <yao@codesourcery.com> writes:
> On 02/27/2013 10:17 AM, Yao Qi wrote:
>> +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);
>
> "&one" should be cast to "gdb_byte *" instead of "void *".
Either one should be int32_t, or 4 should be sizeof (one).
Andreas.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-02-28 13:17 ` Andreas Schwab
@ 2013-02-28 17:19 ` Pedro Alves
0 siblings, 0 replies; 37+ messages in thread
From: Pedro Alves @ 2013-02-28 17:19 UTC (permalink / raw)
To: Andreas Schwab; +Cc: Yao Qi, gdb-patches
On 02/28/2013 11:31 AM, Andreas Schwab wrote:
> Yao Qi <yao@codesourcery.com> writes:
>
>> On 02/27/2013 10:17 AM, Yao Qi wrote:
>>> +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);
>>
>> "&one" should be cast to "gdb_byte *" instead of "void *".
>
> Either one should be int32_t, or 4 should be sizeof (one).
And what about endianness?
--
Pedro Alves
^ permalink raw reply [flat|nested] 37+ messages in thread
* Re: [PATCH 2/5] Save trace into CTF format
2013-02-28 13:17 ` Yao Qi
2013-02-28 13:17 ` Andreas Schwab
@ 2013-03-01 19:22 ` Tom Tromey
2013-03-03 10:19 ` Yao Qi
1 sibling, 1 reply; 37+ messages in thread
From: Tom Tromey @ 2013-03-01 19:22 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
>>>>> "Yao" == Yao Qi <yao@codesourcery.com> writes:
Yao> +/* Handler of writing trace of CTF format. */
Yao> +
Yao> +struct trace_write_handler
I don't think that comment is very useful.
Perhaps it could just say that this is the state kept while writing a
CTF file. Assuming that is what it is.
Yao> + if (fwrite (buf, size, 1, fd) != 1)
Yao> + error (_("Unable to write file for saving trace data (%s)"),
Yao> + safe_strerror (errno));
We had this whole thread about using ui-out with patches and everything.
Upthread you said that it was abandoned due to lack of error checking on
write. But I thought that was addressed in the previous thread?
I suppose I don't really care enough to press it.
I don't recall offhand why I thought it was important; maybe some code
duplication, but I don't really see it any more.
Yao> +static void
Yao> +ctf_save_fwrite_format_1 (FILE *fd, const char *format, va_list args)
Yao> +{
Yao> + char *linebuffer;
Yao> + struct cleanup *old_cleanups;
Yao> +
Yao> + linebuffer = xstrvprintf (format, args);
Yao> + old_cleanups = make_cleanup (xfree, linebuffer);
Yao> + ctf_save_fwrite (fd, linebuffer, strlen (linebuffer));
Yao> + do_cleanups (old_cleanups);
Yao> +}
Yao> +
Yao> +/* Write data in FORMAT to FD. */
Yao> +
Yao> +static void
Yao> +ctf_save_fwrite_format (FILE *fd, const char *format, ...)
Yao> +{
Yao> + va_list args;
Yao> +
Yao> + va_start (args, format);
Yao> + ctf_save_fwrite_format_1 (fd, format, args);
Yao> + va_end (args);
Yao> +}
Why not just use vfprintf directly and drop ctf_save_fwrite_format_1 and
the cleanups and the rest?
Yao> +static int
Yao> +ctf_save_fseek (struct trace_write_handler *handler, long offset,
Yao> + int whence)
Yao> +{
Yao> + if (fseek (handler->datastream_fd, offset, whence))
Yao> + error (_("Unable to seek file for saving trace data (%s)"),
Yao> + safe_strerror (errno));
Yao> +
Yao> + if (whence == SEEK_CUR)
Yao> + handler->content_size += offset;
Why only update the size in this one case?
If it is because the callers only use it in specific ways, then I
suggest assertions to cover this, like maybe:
gdb_assert (whence != SEEK_END);
gdb_assert (whence != SEEK_SET || offset < handler->content_size);
Yao> +/* Change the datastream file position to align on ALIGN_SIZE,
Yao> + and Write BUF to datastream file. The size of BUF is SIZE. */
Lowercase "w" in "write".
Yao> + if (handler->metadata_fd)
Yao> + fclose (handler->metadata_fd);
Yao> +
Yao> + if (handler->datastream_fd)
Yao> + fclose (handler->datastream_fd);
Use the "!= NULL" form.
Yao> + /* Create DIRNAME. */
Yao> + file_name = alloca (strlen (dirname) + 1
Yao> + + strlen (CTF_DATASTREAM_NAME) + 1);
I think this assignment is dead.
Yao> + if (!data->tcs.datastream_fd)
Use "!= NULL".
I think there are some other cases I didn't point out.
Yao> + /* Tracepoint number. */
Yao> + ctf_save_write (&data->tcs, (gdb_byte *) &tpnum, 2);
Does endianness matter?
Yao> + /* Step 2: Write event "frame". */
Yao> + /* Event Id. */
Yao> + ctf_save_align_write (&data->tcs, (gdb_byte *) &two, 4, 4);
The comments Andreas and Pedro made apply here.
Yao> + /* Event Id. */
Yao> + ctf_save_align_write (&data->tcs, (gdb_byte *) &event_id, 4, 4);
Yao> +
Yao> + /* Address. */
Yao> + ctf_save_align_write (&data->tcs, (gdb_byte *) &addr, 8, 8);
Here too I think.
There are more spots as well.
Yao> +/* Operations to write various types of trace frames into CTF
Yao> + format. */
Yao> +
Yao> +static struct trace_frame_write_ops ctf_write_frame_ops =
Why not const?
Yao> +static struct trace_file_write_ops ctf_write_ops =
Likewise.
Yao> +extern struct trace_file_writer *ctf_trace_file_writer (void);
Yao> +#endif
Newline between these two.
Yao> mi_cmd_trace_save (char *command, char **argv, int argc)
Yao> {
Yao> int target_saves = 0;
Yao> + int generate_ctf = 0;
Yao> char *filename;
Yao> if (argc != 1 && argc != 2)
Yao> - error (_("Usage: -trace-save [-r] filename"));
Yao> + error (_("Usage: -trace-save [-r] [-ctf] filename"));
Yao> if (argc == 2)
Yao> {
Yao> filename = argv[1];
Yao> if (strcmp (argv[0], "-r") == 0)
Yao> target_saves = 1;
Yao> + if (strcmp (argv[0], "-ctf") == 0)
Yao> + generate_ctf = 1;
I wonder why this doesn't use mi_getopt.
(Not your problem unless you feel like cleaning it up.)
Yao> add_com ("tsave", class_trace, trace_save_command, _("\
Yao> Save the trace data to a file.\n\
Yao> Use the '-r' option to direct the target to save directly to the file,\n\
Yao> +Use the '-ctf' option to save the data to CTF format,\n\
Yao> using its own filesystem."));
This reads strangely after the change.
Does -ctf direct the target to use its own filesystem?
Perhaps rewriting all this into a more "--help" style would be better;
or otherwise at least repeating the final clause.
Tom
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-03-01 19:22 ` Tom Tromey
@ 2013-03-03 10:19 ` Yao Qi
2013-03-07 21:29 ` Tom Tromey
2013-03-16 2:30 ` Joel Brobecker
0 siblings, 2 replies; 37+ messages in thread
From: Yao Qi @ 2013-03-03 10:19 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1987 bytes --]
On 03/02/2013 03:22 AM, Tom Tromey wrote:
> Yao> + if (fwrite (buf, size, 1, fd) != 1)
> Yao> + error (_("Unable to write file for saving trace data (%s)"),
> Yao> + safe_strerror (errno));
>
> We had this whole thread about using ui-out with patches and everything.
> Upthread you said that it was abandoned due to lack of error checking on
> write. But I thought that was addressed in the previous thread?
>
I didn't follow up that discussion, but the the last mail on this topic
was sent from Hui
<http://sourceware.org/ml/gdb-patches/2012-12/msg00332.html> and I
don't see any discussions from then on. Looks the problem of error
checking on write was pointed out but don't have an idea to fix it.
> I suppose I don't really care enough to press it.
> I don't recall offhand why I thought it was important; maybe some code
> duplication, but I don't really see it any more.
>
OK.
>
> Yao> + if (!data->tcs.datastream_fd)
>
> Use "!= NULL".
> I think there are some other cases I didn't point out.
>
> Yao> + /* Tracepoint number. */
> Yao> + ctf_save_write (&data->tcs, (gdb_byte *) &tpnum, 2);
>
> Does endianness matter?
>
We set the "byte_order" to the host-endianness in metadata, and
write data to CTF datastream file in the host-endianness as well. We
don't have to worry about endianness here.
>
> Yao> add_com ("tsave", class_trace, trace_save_command, _("\
> Yao> Save the trace data to a file.\n\
> Yao> Use the '-r' option to direct the target to save directly to the file,\n\
> Yao> +Use the '-ctf' option to save the data to CTF format,\n\
> Yao> using its own filesystem."));
>
> This reads strangely after the change.
> Does -ctf direct the target to use its own filesystem?
Yes, it is a little bit confusing.
> Perhaps rewriting all this into a more "--help" style would be better;
> or otherwise at least repeating the final clause.
I rewrite it in the new patch. The new patch addresses all the comments.
--
Yao (é½å°§)
[-- Attachment #2: 0002-Save-trace-into-CTF-format.patch --]
[-- Type: text/x-patch, Size: 28140 bytes --]
gdb:
2013-03-03 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 | 709 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/ctf.h | 25 ++
gdb/mi/mi-main.c | 47 +++-
gdb/tracepoint.c | 27 ++-
gdb/tracepoint.h | 2 +
6 files changed, 798 insertions(+), 18 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..c243fd2
--- /dev/null
+++ b/gdb/ctf.c
@@ -0,0 +1,709 @@
+/* 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
+
+/* The state kept while writing the CTF datastream file. */
+
+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));
+}
+
+/* Write data in FORMAT to FD. */
+
+static void
+ctf_save_fwrite_format (FILE *fd, const char *format, ...)
+{
+ va_list args;
+
+ va_start (args, format);
+ vfprintf (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;
+}
+
+/* Write a unsigned 32-bit integer to datastream file represented by
+ HANDLER. */
+
+static int
+ctf_save_write_uint32 (struct trace_write_handler *handler,
+ uint32_t u32)
+{
+ return ctf_save_write (handler, (gdb_byte *) &u32, 4);
+}
+
+/* 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)
+{
+ gdb_assert (whence != SEEK_END);
+ gdb_assert (whence != SEEK_SET
+ || offset <= handler->content_size + handler->packet_start);
+
+ 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;
+}
+
+/* Write events to next new 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 != NULL)
+ fclose (handler->metadata_fd);
+
+ if (handler->datastream_fd != NULL)
+ fclose (handler->datastream_fd);
+}
+
+/* CTF trace writer. */
+
+struct ctf_trace_file_writer
+{
+ struct trace_file_writer base;
+
+ 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
+ target_save. */
+
+static int
+ctf_target_save (struct trace_file_writer *self,
+ const char *dirname)
+{
+ /* 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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+ int i;
+
+ writer->event_id = CTF_EVENT_ID_LAST;
+ writer->length = 8;
+ writer->m_block_size = xmalloc (writer->length * sizeof (int));
+ memset (writer->m_block_size, 0, writer->length * sizeof (int));
+
+ /* Create DIRNAME. */
+ 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 (&writer->tcs, '\0', sizeof (writer->tcs));
+
+ file_name = xstrprintf ("%s/%s", dirname, CTF_METADATA_NAME);
+ old_chain = make_cleanup (xfree, file_name);
+
+ writer->tcs.metadata_fd = fopen (file_name, "w");
+ if (writer->tcs.metadata_fd == NULL)
+ error (_("Unable to open file '%s' for saving trace data (%s)"),
+ file_name, safe_strerror (errno));
+ do_cleanups (old_chain);
+
+ ctf_save_metadata_header (&writer->tcs);
+
+ file_name = xstrprintf ("%s/%s", dirname, CTF_DATASTREAM_NAME);
+ old_chain = make_cleanup (xfree, file_name);
+ writer->tcs.datastream_fd = fopen (file_name, "w");
+ if (writer->tcs.datastream_fd == NULL)
+ error (_("Unable to open file '%s' for saving trace data (%s)"),
+ file_name, safe_strerror (errno));
+ do_cleanups (old_chain);
+
+ writer->cleanup = make_cleanup (ctf_save_cleanup, &writer->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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+
+ ctf_save_fwrite_format (writer->tcs.metadata_fd, "\n");
+ ctf_save_fwrite_format (writer->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 (writer->tcs.metadata_fd, "\n");
+ ctf_save_fwrite_format (writer->tcs.metadata_fd,
+ "event {\n\tname = \"frame\";\n\tid = %u;\n"
+ "\tfields := struct { \n"
+ "\t};\n"
+ "};\n", CTF_EVENT_ID_FRAME);
+
+ gdb_assert (writer->tcs.content_size == 0);
+ gdb_assert (writer->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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+
+ ctf_save_fwrite_format (writer->tcs.metadata_fd, "\n");
+
+ ctf_save_fwrite_format (writer->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_trace_file_writer *writer = (struct ctf_trace_file_writer *) self;
+
+ gdb_assert (writer->tcs.content_size == 0);
+ /* The babeltrace requires or assumes that the size of datastream
+ file is greater than 4096 bytes. If we don't generate enough
+ packets and events, create a fake packet which has zero event,
+ to use up the space. */
+ if (writer->tcs.packet_start < CTF_FILE_MIN_SIZE)
+ {
+ uint32_t u32;
+
+ /* magic. */
+ u32 = CTF_MAGIC;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+
+ /* content_size. */
+ u32 = 0;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+
+ /* packet_size. */
+ u32 = 12;
+ if (writer->tcs.packet_start + u32 < 4096)
+ u32 = CTF_FILE_MIN_SIZE - writer->tcs.packet_start;
+
+ u32 *= TARGET_CHAR_BIT;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+
+ /* tpnum. */
+ u32 = 0;
+ ctf_save_write (&writer->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
+ > (writer->tcs.packet_start + writer->tcs.content_size))
+ {
+ gdb_byte b = 0;
+
+ /* Fake the content size to avoid assertion failure in
+ ctf_save_fseek. */
+ writer->tcs.content_size = (CTF_FILE_MIN_SIZE
+ - 1 - writer->tcs.packet_start);
+ ctf_save_fseek (&writer->tcs, CTF_FILE_MIN_SIZE - 1,
+ SEEK_SET);
+ ctf_save_write (&writer->tcs, &b, 1);
+ }
+ }
+
+ do_cleanups (writer->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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+ uint32_t id = CTF_EVENT_ID_FRAME;
+ uint32_t u32;
+
+ /* Step 1: Write packet context. */
+ /* magic. */
+ u32 = CTF_MAGIC;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+ /* content_size and packet_size.. We still don't know the value,
+ write it later. */
+ ctf_save_fseek (&writer->tcs, 4, SEEK_CUR);
+ ctf_save_fseek (&writer->tcs, 4, SEEK_CUR);
+ /* Tracepoint number. */
+ ctf_save_write (&writer->tcs, (gdb_byte *) &tpnum, 2);
+
+ /* Step 2: Write event "frame". */
+ /* Event Id. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &id, 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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+ uint32_t id = CTF_EVENT_ID_REGISTER;
+
+ /* Event Id. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &id, 4, 4);
+
+ /* array contents. */
+ ctf_save_align_write (&writer->tcs, buf, size, 1);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ write_m_block_header. */
+
+static void
+ctf_write_frame_m_block_header (struct trace_file_writer *self,
+ uint64_t addr, uint16_t length)
+{
+ struct ctf_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+ int i;
+ uint32_t event_id = 0;
+
+ /* Check we've seen the 'M' block of length LENGTH before. */
+ for (i = 0; i < writer->length; i++)
+ {
+ if (length == writer->m_block_size[i])
+ {
+ event_id = i + CTF_EVENT_ID_LAST;
+ break;
+ }
+ }
+
+ /* Record it. */
+ if (event_id == 0)
+ {
+ event_id = writer->event_id++;
+
+ if (event_id - CTF_EVENT_ID_LAST >= writer->length)
+ {
+ writer->m_block_size
+ = xrealloc (writer->m_block_size,
+ sizeof (int) * writer->length * 2);
+ memset (&writer->m_block_size[writer->length], 0,
+ writer->length * sizeof (int));
+ writer->length *= 2;
+ }
+
+ writer->m_block_size[event_id - CTF_EVENT_ID_LAST] = length;
+
+ /* Save the type of 'M' block into metadata. */
+ ctf_save_fwrite_format (writer->tcs.metadata_fd, "\n");
+ ctf_save_fwrite_format (writer->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 (&writer->tcs, (gdb_byte *) &event_id, 4, 4);
+
+ /* Address. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &addr, 8, 8);
+
+ /* Length. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &length, 2, 2);
+}
+
+/* This is the implementation of trace_frame_write_ops method
+ write_m_block_memory. */
+
+static void
+ctf_write_frame_m_block_memory (struct trace_file_writer *self,
+ gdb_byte *buf, uint16_t length)
+{
+ struct ctf_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+
+ /* Contents. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) 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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+ uint32_t id = CTF_EVENT_ID_TSV;
+
+ /* Event Id. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &id, 4, 4);
+
+ /* val. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &val, 8, 8);
+ /* num. */
+ ctf_save_align_write (&writer->tcs, (gdb_byte *) &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_trace_file_writer *writer
+ = (struct ctf_trace_file_writer *) self;
+ uint32_t u32;
+ uint32_t t;
+
+ /* Write the content size to packet header. */
+ ctf_save_fseek (&writer->tcs, writer->tcs.packet_start + 4,
+ SEEK_SET);
+ u32 = writer->tcs.content_size * TARGET_CHAR_BIT;
+
+ t = writer->tcs.content_size;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+
+ /* Write the packet size. */
+ u32 += 4 * TARGET_CHAR_BIT;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+
+ writer->tcs.content_size = t;
+
+ /* Write zero at the end of the packet. */
+ ctf_save_fseek (&writer->tcs, writer->tcs.packet_start + t,
+ SEEK_SET);
+ u32 = 0;
+ ctf_save_write_uint32 (&writer->tcs, u32);
+ writer->tcs.content_size = t;
+
+ ctf_save_next_packet (&writer->tcs);
+}
+
+/* Operations to write various types of trace frames into CTF
+ format. */
+
+static const struct trace_frame_write_ops ctf_write_frame_ops =
+{
+ ctf_write_frame_start,
+ ctf_write_frame_r_block,
+ ctf_write_frame_m_block_header,
+ ctf_write_frame_m_block_memory,
+ ctf_write_frame_v_block,
+ ctf_write_frame_end,
+};
+
+/* Operations to write trace buffers into CTF format. */
+
+static const struct trace_file_write_ops ctf_write_ops =
+{
+ ctf_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_new (void)
+{
+ struct ctf_trace_file_writer *writer
+ = xmalloc (sizeof (struct ctf_trace_file_writer));
+
+ writer->base.ops = &ctf_write_ops;
+
+ writer->m_block_size = NULL;
+ writer->length = 0;
+
+ return (struct trace_file_writer *) writer;
+}
diff --git a/gdb/ctf.h b/gdb/ctf.h
new file mode 100644
index 0000000..6a61cf3
--- /dev/null
+++ b/gdb/ctf.h
@@ -0,0 +1,25 @@
+/* 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_new (void);
+
+#endif
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 206b626..5ee5cfa 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,25 +2478,45 @@ void
mi_cmd_trace_save (char *command, char **argv, int argc)
{
int target_saves = 0;
+ int generate_ctf = 0;
char *filename;
+ int oind = 0;
+ char *oarg;
- if (argc != 1 && argc != 2)
- error (_("Usage: -trace-save [-r] filename"));
-
- if (argc == 2)
+ enum opt
+ {
+ TARGET_SAVE_OPT, CTF_OPT
+ };
+ static const struct mi_opt opts[] =
{
- filename = argv[1];
- if (strcmp (argv[0], "-r") == 0)
- target_saves = 1;
- else
- error (_("Invalid option: %s"), argv[0]);
- }
- else
+ {"r", TARGET_SAVE_OPT, 0},
+ {"ctf", CTF_OPT, 0},
+ { 0, 0, 0 }
+ };
+
+ while (1)
{
- filename = argv[0];
+ int opt = mi_getopt ("-trace-save", argc, argv, opts,
+ &oind, &oarg);
+
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case TARGET_SAVE_OPT:
+ target_saves = 1;
+ break;
+ case CTF_OPT:
+ generate_ctf = 1;
+ break;
+ }
}
+ filename = argv[oind];
- 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 eb93645..eab4043 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"
@@ -3535,6 +3536,7 @@ trace_save_command (char *args, int from_tty)
char **argv;
char *filename = NULL;
struct cleanup *back_to;
+ int generate_ctf = 0;
struct trace_file_writer *writer = NULL;
if (args == NULL)
@@ -3547,6 +3549,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
@@ -3556,14 +3560,18 @@ trace_save_command (char *args, int from_tty)
if (!filename)
error_no_arg (_("file in which to save trace data"));
- writer = tfile_trace_file_writer_new ();
+ 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 file '%s'.\n"), filename);
+ printf_filtered (_("Trace data saved to %s '%s'.\n"),
+ generate_ctf ? "directory" : "file", filename);
do_cleanups (back_to);
}
@@ -3583,6 +3591,20 @@ trace_save_tfile (const char *filename, int target_does_save)
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_new ();
+ trace_save (dirname, writer, target_does_save);
+ back_to = make_cleanup (trace_file_writer_xfree, writer);
+ do_cleanups (back_to);
+}
+
/* Tell the target what to do with an ongoing tracing run if GDB
disconnects for some reason. */
@@ -5631,6 +5653,7 @@ _initialize_tracepoint (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."));
diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h
index fd3a225..67a7b87 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -384,6 +384,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
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-03-03 10:19 ` Yao Qi
@ 2013-03-07 21:29 ` Tom Tromey
2013-03-16 2:30 ` Joel Brobecker
1 sibling, 0 replies; 37+ messages in thread
From: Tom Tromey @ 2013-03-07 21:29 UTC (permalink / raw)
To: Yao Qi; +Cc: gdb-patches
>>>>> "Yao" == Yao Qi <yao@codesourcery.com> writes:
Yao> +static void
Yao> +ctf_save_fwrite (FILE *fd, const gdb_byte *buf, size_t size)
Yao> +{
Yao> + if (fwrite (buf, size, 1, fd) != 1)
Yao> + error (_("Unable to write file for saving trace data (%s)"),
Yao> + safe_strerror (errno));
Yao> +}
Why not merge this function with its only caller?
Yao> +static void
Yao> +ctf_save_fwrite_format (FILE *fd, const char *format, ...)
Yao> +{
Yao> + va_list args;
Yao> +
Yao> + va_start (args, format);
Yao> + vfprintf (fd, format, args);
This seems like it needs the same error-checking as ctf_save_fwrite;
plus maybe (?) the content_size update as well.
Yao> +static void
Yao> +ctf_write_frame_v_block (struct trace_file_writer *self,
Yao> + int num, LONGEST val)
Yao> +{
Yao> + struct ctf_trace_file_writer *writer
Yao> + = (struct ctf_trace_file_writer *) self;
Yao> + uint32_t id = CTF_EVENT_ID_TSV;
Yao> +
Yao> + /* Event Id. */
Yao> + ctf_save_align_write (&writer->tcs, (gdb_byte *) &id, 4, 4);
Yao> +
Yao> + /* val. */
Yao> + ctf_save_align_write (&writer->tcs, (gdb_byte *) &val, 8, 8);
It seems safer to use a uint64_t intermediary here.
Yao> + /* num. */
Yao> + ctf_save_align_write (&writer->tcs, (gdb_byte *) &num, 4, 4);
And a uint32_t here.
Yao> +/* Save the trace data to dir DIRENAME of ctf format. */
Typo, "DIRNAME".
Yao> +void
Yao> +trace_save_ctf (const char *dirname, int target_does_save)
Yao> +{
Yao> + struct trace_file_writer *writer;
Yao> + struct cleanup *back_to;
Yao> +
Yao> + writer = ctf_trace_file_writer_new ();
Yao> + trace_save (dirname, writer, target_does_save);
Yao> + back_to = make_cleanup (trace_file_writer_xfree, writer);
Yao> + do_cleanups (back_to);
I think the make_cleanup and trace_save lines have to be exchanged.
Otherwise this doesn't make much sense.
Tom
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-03-03 10:19 ` Yao Qi
2013-03-07 21:29 ` Tom Tromey
@ 2013-03-16 2:30 ` Joel Brobecker
2013-03-16 4:22 ` Yao Qi
1 sibling, 1 reply; 37+ messages in thread
From: Joel Brobecker @ 2013-03-16 2:30 UTC (permalink / raw)
To: Yao Qi; +Cc: Tom Tromey, gdb-patches
> 2013-03-03 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.
[...]
> + ctf_save_fwrite_format (handler->metadata_fd, metadata_fmt,
> + CTF_SAVE_MAJOR, CTF_SAVE_MINOR,
> + BYTE_ORDER == LITTLE_ENDIAN ? "le" : "be");
This is causing a build failure on ppc-aix, for instance, due to:
/[...]ctf.c:223:28: error: 'BYTE_ORDER' undeclared (first use in this function)
/[...]ctf.c:223:28: note: each undeclared identifier is reported only once for each function it appears in
/[...]ctf.c:223:42: error: 'LITTLE_ENDIAN' undeclared (first use in this function)
I don't know how to fix this, because I am not sure whether host
or target endianness were meant. I asssume from the code that it
was host, but not sure how the host endianess would make sense...
What also worries me is that I don't seem to see approval for commit
for this patch. I know both of you are very careful, so perhaps in
a separate message not linked to this sub-thread?
--
Joel
^ permalink raw reply [flat|nested] 37+ messages in thread* Re: [PATCH 2/5] Save trace into CTF format
2013-03-16 2:30 ` Joel Brobecker
@ 2013-03-16 4:22 ` Yao Qi
0 siblings, 0 replies; 37+ messages in thread
From: Yao Qi @ 2013-03-16 4:22 UTC (permalink / raw)
To: Joel Brobecker; +Cc: Tom Tromey, gdb-patches
On 03/16/2013 07:44 AM, Joel Brobecker wrote:
> This is causing a build failure on ppc-aix, for instance, due to:
>
> /[...]ctf.c:223:28: error: 'BYTE_ORDER' undeclared (first use in this function)
> /[...]ctf.c:223:28: note: each undeclared identifier is reported only once for each function it appears in
> /[...]ctf.c:223:42: error: 'LITTLE_ENDIAN' undeclared (first use in this function)
>
The build failure was reported by Pierre, and I post a patch to fix it
http://sourceware.org/ml/gdb-patches/2013-03/msg00667.html
> What also worries me is that I don't seem to see approval for commit
> for this patch. I know both of you are very careful, so perhaps in
> a separate message not linked to this sub-thread?
Oh, we started a new thread, and patches are approved there.
[PATCH v3 00/15] CTF Support
http://sourceware.org/ml/gdb-patches/2013-03/msg00394.html
--
Yao (é½å°§)
^ permalink raw reply [flat|nested] 37+ messages in thread