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 v3 01/15] Refactor 'tsave'


On 03/14/2013 03:26 AM, Tom Tromey wrote:
> I think this needs a NULL check.
> Otherwise I think on error this may crash.

Done.

> 
> Ok with that fixed.

Thanks for the review.  Patch below is what I committed.

-- 
Yao (éå)

gdb:

2013-03-14  Yao Qi  <yao@codesourcery.com>

	* tracepoint.c (trace_file_writer_xfree): New.
	(struct tfile_writer_data): New.
	(tfile_dtor, 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 |  640 +++++++++++++++++++++++++++++++++++++++++++-----------
 gdb/tracepoint.h |  108 +++++++++-
 3 files changed, 619 insertions(+), 131 deletions(-)

diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 20777a3..085439b 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 4db93ca..2bfa500 100644
--- a/gdb/tracepoint.c
+++ b/gdb/tracepoint.c
@@ -2979,90 +2979,339 @@ 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)
+{
+  struct trace_file_writer *writer = arg;
 
-void
-trace_save (const char *filename, int target_does_save)
+  writer->ops->dtor (writer);
+  xfree (writer);
+}
+
+/* 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;
-    }
+/* 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.  */
 
-  /* 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);
+static int
+tfile_target_save (struct trace_file_writer *self,
+		   const char *filename)
+{
+  int err = target_save_trace_data (filename);
+
+  return (err >= 0);
+}
+
+/* This is the implementation of trace_file_write_ops method
+   dtor.  */
+
+static void
+tfile_dtor (struct trace_file_writer *self)
+{
+  struct tfile_trace_file_writer *writer
+    = (struct tfile_trace_file_writer *) self;
+
+  xfree (writer->pathname);
+
+  if (writer->fp != NULL)
+    fclose (writer->fp);
+}
 
-  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->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);
+}
+
+/* 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);
+}
+
+/* Operations to write trace buffers into TFILE format.  */
+
+static const struct trace_file_write_ops tfile_write_ops =
+{
+  tfile_dtor,
+  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
@@ -3077,22 +3326,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);
 
@@ -3102,76 +3336,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
@@ -3181,6 +3544,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"));
@@ -3201,7 +3565,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);
@@ -3209,6 +3577,20 @@ 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 ();
+  back_to = make_cleanup (trace_file_writer_xfree, writer);
+  trace_save (filename, writer, target_does_save);
+  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 b03c95b..a5d3d42 100644
--- a/gdb/tracepoint.h
+++ b/gdb/tracepoint.h
@@ -205,6 +205,111 @@ 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, int32_t 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, int32_t num,
+			 uint64_t 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
+{
+  /* Destructor.  Releases everything from SELF (but not SELF
+     itself).  */
+  void (*dtor) (struct trace_file_writer *self);
+
+  /*  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 +386,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]