This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH 1/5] Refactor 'tsave'


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 (éå)
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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]