[PATCH] Add CTF support to GDB [1/4] Add "-ctf" to tsave command

Hui Zhu teawater@gmail.com
Fri Dec 14 11:37:00 GMT 2012


On Fri, Nov 30, 2012 at 4:06 AM, Tom Tromey <tromey@redhat.com> wrote:
>>>>>> "Hui" == Hui Zhu <hui_zhu@mentor.com> writes:
>
> Hui> This patch is for the CTF write.
> Hui> It add "-ctf" to tsave command.  With this option, tsave can save
> Hui> current trace frame to CTF file format.
>
> Hui> +struct ctf_save_collect_s
> Hui> +{
> Hui> +  struct ctf_save_collect_s *next;
> Hui> +  char *str;
> Hui> +  char *ctf_str;
> Hui> +  int align_size;
> Hui> +  struct expression *expr;
> Hui> +  struct type *type;
> Hui> +  int is_ret;
> Hui> +};
>
> Like Hafiz said -- comments would be nice.

I added some comments in the new patches.

>
> Hui> +static void
> Hui> +ctf_save_fwrite (FILE *fd, const gdb_byte *buf, size_t size)
> Hui> +{
> Hui> +  if (fwrite (buf, size, 1, fd) != 1)
> Hui> +    error (_("Unable to write file for saving trace data (%s)"),
> Hui> +     safe_strerror (errno));
>
> Why not use the existing ui_file code?
>
> Then you could remove this function plus several others.
>
> Maybe it is because you need fseek, but that seems like a simple
> addition to ui_file.

I still not update this part because fseek patch is still not OK.
And after discussion with Pedro, I was really worry about change to
ui_file will make CTF write doesn't have error check.  Could you help
me with it?

>
> Hui> +    case TYPE_CODE_ARRAY:
> Hui> +      for (; TYPE_CODE (type) == TYPE_CODE_ARRAY;
> Hui> +     type = TYPE_TARGET_TYPE (type))
> Hui> +  ;
>
> You probably want some check_typedef calls in there.

Because typedef will be handle as a type in this part, so this part
doesn't need check_typedef.

>
> Hui> +  ctf_save_type_name_write (tcsp, TYPE_FIELD_TYPE (type, biggest_id));
>
> Here too.

I think this part is same with array.

>
> Hui> +      ctf_save_fwrite_format (tcsp->metadata_fd, "%s", TYPE_NAME (type));
>
> What if TYPE_NAME is NULL?

Add code handle it  like TYPE_CODE_STRUCT.

>
> Hui> +static void
> Hui> +ctf_save_type_size_write (struct ctf_save_s *tcsp, struct type *type)
> Hui> +{
> Hui> +  if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
> Hui> +    {
> Hui> +      for (; TYPE_CODE (type) == TYPE_CODE_ARRAY;
> Hui> +     type = TYPE_TARGET_TYPE (type))
>
> check_typedef

This is function will call itself to post all the define of type to file.
So It don't need check_typedef.

>
> Hui> +  if (TYPE_NAME (type) && (strcmp (TYPE_NAME (type), "uint32_t") == 0
> Hui> +                     || strcmp (TYPE_NAME (type), "uint64_t") == 0))
> Hui> +    return;
>
> check_typedef.
>
> Also it seems like this clause should go in the TYPE_CODE_INT case.
>
> Hui> +
> Hui> +  switch (TYPE_CODE (type))
> Hui> +    {
> Hui> +      case TYPE_CODE_TYPEDEF:
> Hui> +  ctf_save_fwrite_format (tcsp->metadata_fd, "typedef ");
> Hui> +  ctf_save_var_define_write (tcsp, TYPE_TARGET_TYPE (type),
>
> check_typedef; though if your intent is to peel just a single layer,
> then it is a bit trickier -- I think the best you can do is always call
> it, then use TYPE_TARGET_TYPE if it is non-NULL or the result of
> check_typedef otherwise.

If use check_typedef, this part will generate the define that
different with the type descriptor of the code.

For example:
Following is the define in the code:
typedef char test_t1;
typedef test_t1 test_t2;
typedef test_t2 test_t3;

If use TYPE_TARGET_TYPE, it will generate following metadata:
typedef char test_t1;
typedef test_t1 test_t2;
typedef test_t2 test_t3;

If use check_typedef, it will generate following metadata:
typedef char test_t1;
typedef char test_t2;
typedef char test_t3;

>
> Hui> +    tcsp->tab = tab;
> [...]
> Hui> +    tcsp->tab = old_tab;
>
> No idea if it matters, but if an exception is thrown during the '...'
> code, then the 'tab' field will be left set improperly.

Please don't worry about this part.
This tab always be set to local value in stack.  So even if it is
drop, it will not affect anything.

For example:
char tab[256];
const char *old_tab;

old_tab = tcsp->tab;
snprintf (tab, 256, "%s\t", old_tab);
tcsp->tab = tab;
[...]
tcsp->tab = old_tab;

>
> Hui> +      case TYPE_CODE_PTR:
> Hui> +  align_size = TYPE_LENGTH (type);
> Hui> +  break;
>
> Surely the alignment rules are ABI dependent.
> I would guess that what you have will work in many cases, but definitely
> not all of them.

All the type will be handle and record in function
ctf_save_type_check_and_write.
The size align will be handle in this function too.

>
> Hui> +    frame = get_current_frame ();
> Hui> +    if (!frame)
> Hui> +      error (_("get current frame fail"));
> Hui> +    frame = get_prev_frame (frame);
> Hui> +    if (!frame)
> Hui> +      error (_("get prev frame fail"));
>
> These messages could be improved.
>
> Hui> +      warning (_("\
> Hui> +Not save \"%s\" of tracepoint %d to ctf file because get its value fail: %s"),
> Hui> +         str, tps->tp->base.number, e.message);
>
> Likewise.

Could you help me with this part?  :)

>
> Hui> +      /* XXX: no sure about variable_length
> Hui> +   and need set is_variable_length if need.  */
> Hui> +      collect->align_size = align_size;
> Hui> +      if (collect->align_size > tps->align_size)
> Hui> +        tps->align_size = collect->align_size;
>
> No new FIXME comments.
> Can you find the answer to this question and either fix the code or drop
> the comment?

Fixed.

>
> Hui> +  char name[strlen (print_name) + 1];
>
> I think you need an explicit alloca here.
> Or xmalloc + xfree, which is probably better.

Fixed.

>
> Although, this approach just seems weird, since it seems like you
> already have the symbol and you want its value; constructing and parsing
> an expression to get this is very roundabout.
>
> I'm not sure I really understand the goal here; but the parsing approach
> is particularly fragile if you have shadowing.

Function ctf_save_collect_get will parse the collect string and add
them to struct.
Each tracepoint will call this function just once.

The code that try to get the value is in function ctf_save:
	      back_chain = make_cleanup (null_cleanup, NULL);
	      TRY_CATCH (e, RETURN_MASK_ERROR)
	        {
		  val = evaluate_expression (collect->expr);
		  content = value_contents (val);
		}
Could you tell me some better way?

>
> Hui> +        iterate_over_block_local_vars (block,
> Hui> +                                       tsv_save_do_loc_arg_collect,
> Hui> +                                       &cb_data);
>
> Why just iterate over this block and not the enclosing ones as well?
>
> Hmm, a lot of this code looks like code from tracepoint.c.
> I think it would be better to share the code if that is possible.

I tried to share code with function add_local_symbols.  But it is not
a big function and use different way to get block.

>
> Hui> +static char *
> Hui> +ctf_save_metadata_change_char (struct ctf_save_s *tcsp, char *ctf_str,
> Hui> +                         int i, const char *str)
> Hui> +{
> Hui> +  char *new;
> Hui> +
> Hui> +  new = xmalloc (strlen (ctf_str) + (i ? 1 : 0) + strlen (str) + 1 - 1);
> Hui> +  ctf_str[i] = '\0';
> Hui> +  sprintf (new, "%s%s%s_%s", ctf_str, (i ? "_" : ""), str, ctf_str + i + 1);
>
> Just use xstrprintf.

Fixed.

>
> Hui> +static void
> Hui> +ctf_save_do_nothing (void *p)
> Hui> +{
> Hui> +}
>
> Use null_cleanup instead.

Fixed.

>
> Hui> +    if (collect->expr)
> Hui> +      free_current_contents (&collect->expr);
>
> Why free_current_contents here?
> That seems weird.

If this collect is $_ret, it will not have collect->expr.
Or maybe this collect will be free because when setup this collect get error.
So check it before free it.

>
> Hui> +  char file_name[strlen (dirname) + 1 + strlen (CTF_DATASTREAM_NAME) + 1];
>
> alloca or malloc.

Fixed.

>
> Hui> +  sprintf (file_name, "%s/%s", dirname, CTF_METADATA_NAME);
> Hui> +  tcs.metadata_fd = fopen (file_name, "w");
> Hui> +  if (!tcs.metadata_fd)
> Hui> +    error (_("Unable to open file '%s' for saving trace data (%s)"),
> Hui> +     file_name, safe_strerror (errno));
> Hui> +  ctf_save_metadata_header (&tcs);
> Hui> +
> Hui> +  sprintf (file_name, "%s/%s", dirname, CTF_DATASTREAM_NAME);
> Hui> +  tcs.datastream_fd = fopen (file_name, "w");
> Hui> +  if (!tcs.datastream_fd)
> Hui> +    error (_("Unable to open file '%s' for saving trace data (%s)"),
> Hui> +     file_name, safe_strerror (errno));
>
> On error these files will be left partially written.
> Is that intentional?

What my thought about this part is even if GDB get error when CTF
save, the data before this error is OK.  So in clean up function
ctf_save_cleanup, it will not remove this file.  And it will write the
data that don't have error, function ctf_save_metadata (tcsp).

>
> Hui> +extern void ctf_save (char *dirname);
>
> Why not const?
> This applies in a few spots in the patch.

Fixed.

>
> Hui> @@ -2465,6 +2466,7 @@ void
> Hui>  mi_cmd_trace_save (char *command, char **argv, int argc)
> [...]
> Hui> +      if (strcmp (argv[0], "-ctf") == 0)
> Hui> +  generate_ctf = 1;
>
> The 'usage' line needs an update.

Fixed.

>
> Hui> +  if (generate_ctf)
> Hui> +    ctf_save (filename);
> Hui> +  else
> Hui> +    trace_save (filename, target_saves);
>
> I don't understand why CTF isn't just an option to trace_save -- share
> the trace-dependent work, separate out the formatting.
>

I separate read and write CTF support function because:
CTF format is a complex format.  I tried to support all of it but I failed.
Then I changed to use libbabeltrace, it works very good with read CTF.
 But it doesn't supply API for CTF write.  And I discussion the
developer of libbabeltrace, they said they don't have plan for that.
So I add CTF write function inside GDB with my hand.  And because CTF
is design for tracepoint support.  So it is really fit with tsave
command.  So I add it as an option.

> Hui>  trace_save_command (char *args, int from_tty)
> Hui>  {
> Tom

Thanks for your comments.  I post a new version.

Best,
Hui
-------------- next part --------------
--- a/Makefile.in
+++ b/Makefile.in
@@ -506,7 +506,8 @@ 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_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o \
+	ctf.o
 
 # This is remote-sim.o if a simulator is to be linked in.
 SIM_OBS = @SIM_OBS@
@@ -751,7 +752,7 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
 	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
 
@@ -834,7 +835,8 @@ gnulib/import/extra/snippet/warn-on-use.
 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/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h gdb_bfd.h
+common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h gdb_bfd.h \
+ctf.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
--- /dev/null
+++ b/ctf.c
@@ -0,0 +1,1237 @@
+/* CTF format support.
+
+   Copyright (C) 2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "ctf.h"
+#include "tracepoint.h"
+#include "regcache.h"
+#include "gdbcmd.h"
+#include "exceptions.h"
+#include "stack.h"
+
+#include <ctype.h>
+
+/* Following part is for tsave.  */
+
+#define CTF_MAGIC		0xC1FC1FC1
+#define CTF_SAVE_MAJOR		1
+#define CTF_SAVE_MINOR		8
+
+#define CTF_METADATA_NAME	"metadata"
+#define CTF_DATASTREAM_NAME	"datastream"
+
+#define CTF_PACKET_SIZE		4096
+
+#define ALIGN_SIZE(size, align) \
+	((((align) + (size) - 1) & (~((align) - 1))) - (size))
+
+struct ctf_save_collect_s
+{
+  struct ctf_save_collect_s *next;
+  char *str;
+  char *ctf_str;
+  int align_size;
+  struct expression *expr;
+  struct type *type;
+  int is_ret;
+};
+
+struct ctf_save_tp_s
+{
+  struct ctf_save_tp_s *next;
+  struct tracepoint *tp;
+  int stepping_frame;
+  struct ctf_save_collect_s *collect;
+  int align_size;
+  int is_variable_length;
+  int is_dropped;
+};
+
+struct ctf_save_type_s
+{
+  struct ctf_save_type_s *next;
+  struct type *type;
+};
+
+struct ctf_save_s
+{
+  FILE *metadata_fd;
+  FILE *datastream_fd;
+
+  /* This is the content size of current packet.  */
+  size_t content_size;
+
+  /* This is the begin offset of current packet.  */
+  long packet_begin;
+
+  /* If true, need check if content_size bigger than CTF_PACKET_SIZE.  */
+  int check_size;
+
+  /* This is the content size of current packet and event that is
+     being written to file.
+     Check size use it.  */
+  size_t current_content_size;
+
+  /* Save the number of trace frame before call ctf_save.
+     Set it back when return ctf_save.  */
+  int old_traceframe_num;
+
+  /* Tracepoint list.  */
+  struct ctf_save_tp_s *tp;
+
+  /* Type list.  */
+  struct ctf_save_type_s *type;
+
+  const char *tab;
+};
+
+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);
+}
+
+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);
+}
+
+static int
+ctf_save_write (struct ctf_save_s *tcsp, const gdb_byte *buf, size_t size)
+{
+  if (tcsp->check_size)
+    {
+      if (tcsp->current_content_size + size > CTF_PACKET_SIZE)
+        return -1;
+    }
+
+  ctf_save_fwrite (tcsp->datastream_fd, buf, size);
+
+  tcsp->current_content_size += size;
+
+  return 0;
+}
+
+static int
+ctf_save_fseek (struct ctf_save_s *tcsp, long offset, int whence)
+{
+  if (whence == SEEK_CUR && tcsp->check_size)
+    {
+      if (tcsp->current_content_size + offset > CTF_PACKET_SIZE)
+        return -1;
+    }
+
+  if (fseek (tcsp->datastream_fd, offset, whence))
+    error (_("Unable to seek file for saving trace data (%s)"),
+	   safe_strerror (errno));
+
+  if (whence == SEEK_CUR)
+    tcsp->current_content_size += offset;
+
+  return 0;
+}
+
+static int
+ctf_save_align_write (struct ctf_save_s *tcsp, const gdb_byte *buf,
+		      size_t size, size_t align_size)
+{
+  if (ctf_save_fseek (tcsp,
+		      ALIGN_SIZE(tcsp->current_content_size, align_size),
+		      SEEK_CUR))
+    return -1;
+
+  if (ctf_save_write (tcsp, buf, size))
+    return -1;
+
+  return 0;
+}
+
+static void ctf_save_type_define_write (struct ctf_save_s *tcsp,
+					struct type *type);
+
+static void
+ctf_save_type_name_write (struct ctf_save_s *tcsp, struct type *type)
+{
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_ARRAY:
+      for (; TYPE_CODE (type) == TYPE_CODE_ARRAY;
+	   type = TYPE_TARGET_TYPE (type))
+	;
+      if (TYPE_NAME (type))
+        ctf_save_fwrite_format (tcsp->metadata_fd, "%s", TYPE_NAME (type));
+      else
+	ctf_save_type_define_write (tcsp, type);
+      break;
+
+    case TYPE_CODE_PTR:
+      if (TYPE_LENGTH (type) == 8)
+	ctf_save_fwrite_format (tcsp->metadata_fd, "uint64_t");
+      else
+	ctf_save_fwrite_format (tcsp->metadata_fd, "uint32_t");
+      break;
+
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_ENUM:
+      if (TYPE_TAG_NAME (type))
+        ctf_save_fwrite_format (tcsp->metadata_fd, "%s %s",
+				TYPE_CODE (type) == TYPE_CODE_STRUCT ?
+				"struct" : "enum",
+				TYPE_TAG_NAME (type));
+      else
+	ctf_save_type_define_write (tcsp, type);
+      break;
+
+    case TYPE_CODE_UNION:
+      {
+	int i, biggest_id, biggest_size = 0;
+
+	for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	  {
+	    if (biggest_size < TYPE_LENGTH (TYPE_FIELD_TYPE (type, i)))
+	      {
+		biggest_size = TYPE_LENGTH (TYPE_FIELD_TYPE (type, i));
+		biggest_id = i;
+	      }
+	  }
+	ctf_save_type_name_write (tcsp, TYPE_FIELD_TYPE (type, biggest_id));
+	break;
+      }
+
+    default:
+      ctf_save_fwrite_format (tcsp->metadata_fd, "%s", TYPE_NAME (type));
+      break;
+    }
+}
+
+static void
+ctf_save_type_size_write (struct ctf_save_s *tcsp, struct type *type)
+{
+  if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
+    {
+      for (; TYPE_CODE (type) == TYPE_CODE_ARRAY;
+	   type = TYPE_TARGET_TYPE (type))
+	ctf_save_fwrite_format (tcsp->metadata_fd, "[%d]",
+				TYPE_LENGTH (type) /
+				TYPE_LENGTH (TYPE_TARGET_TYPE (type)));
+    }
+}
+
+static void
+ctf_save_var_define_write (struct ctf_save_s *tcsp, struct type *type,
+			   const char *name)
+{
+  ctf_save_type_name_write (tcsp, type);
+  ctf_save_fwrite_format (tcsp->metadata_fd, " %s", name);
+  ctf_save_type_size_write (tcsp, type);
+  ctf_save_fwrite_format (tcsp->metadata_fd, ";\n");
+}
+
+/* Write TYPE to TCSP->metadata_fd.  */
+
+static void
+ctf_save_type_define_write (struct ctf_save_s *tcsp, struct type *type)
+{
+  struct ctf_save_type_s *t;
+
+  for (t = tcsp->type; t; t = t->next)
+    {
+      if (t->type == type)
+	return;
+    }
+
+  t = (struct ctf_save_type_s *) xcalloc (1, sizeof (*t));
+  t->type = type;
+
+  t->next = tcsp->type;
+  tcsp->type = t;
+
+  /* Ignore type uint32_t and uint64_t because they have written
+     in function ctf_save_metadata_header.  */
+  if (TYPE_NAME (type) && (strcmp (TYPE_NAME (type), "uint32_t") == 0
+			   || strcmp (TYPE_NAME (type), "uint64_t") == 0))
+    return;
+
+  switch (TYPE_CODE (type))
+    {
+      case TYPE_CODE_TYPEDEF:
+	ctf_save_fwrite_format (tcsp->metadata_fd, "typedef ");
+	ctf_save_var_define_write (tcsp, TYPE_TARGET_TYPE (type),
+				   TYPE_NAME (type));
+	break;
+
+      case TYPE_CODE_INT:
+	ctf_save_fwrite_format (tcsp->metadata_fd, "\
+typealias integer { size = %d; align = %d; signed = %s; } := %s;\n",
+				TYPE_LENGTH(type) * TARGET_CHAR_BIT,
+				TYPE_LENGTH(type) * TARGET_CHAR_BIT,
+				!TYPE_UNSIGNED (type) ? "true" : "false",
+				TYPE_NAME (type));
+	break;
+
+      case TYPE_CODE_STRUCT:
+      case TYPE_CODE_ENUM:
+        {
+	  int i;
+	  char tab[256];
+	  const char *old_tab;
+
+	  ctf_save_fwrite_format (tcsp->metadata_fd, "%s %s {\n",
+				  TYPE_CODE (type) == TYPE_CODE_STRUCT ?
+				  "struct" : "enum",
+				  TYPE_TAG_NAME (type) ? TYPE_TAG_NAME (type)
+						       : "");
+
+	  old_tab = tcsp->tab;
+	  snprintf (tab, 256, "%s\t", old_tab);
+	  tcsp->tab = tab;
+	  for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	    {
+	      if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+	        {
+		  ctf_save_fwrite_format (tcsp->metadata_fd, tcsp->tab);
+		  ctf_save_var_define_write (tcsp, TYPE_FIELD_TYPE (type, i),
+					     TYPE_FIELD_NAME (type, i));
+		}
+	      else
+		ctf_save_fwrite_format (tcsp->metadata_fd, "%s%s = %s,\n",
+					tcsp->tab, TYPE_FIELD_NAME (type, i),
+					plongest (TYPE_FIELD_ENUMVAL (type, i)));
+	    }
+	  tcsp->tab = old_tab;
+	  ctf_save_fwrite_format (tcsp->metadata_fd, "%s}", tcsp->tab);
+	  if (TYPE_TAG_NAME (type))
+	    ctf_save_fwrite_format (tcsp->metadata_fd, ";\n");
+	  break;
+	}
+    }
+}
+
+/* Check if this type is support by GDB.
+   Return the align size.  */
+
+static int
+ctf_save_type_check_and_write (struct ctf_save_s *tcsp, struct type *type)
+{
+  int align_size = 0;
+
+  switch (TYPE_CODE (type))
+    {
+      case TYPE_CODE_TYPEDEF:
+	align_size = ctf_save_type_check_and_write (tcsp,
+						    TYPE_TARGET_TYPE (type));
+	if (align_size < 0)
+	  return align_size;
+	ctf_save_type_define_write (tcsp, type);
+	break;
+
+      case TYPE_CODE_ARRAY:
+	align_size = ctf_save_type_check_and_write (tcsp,
+						    TYPE_TARGET_TYPE (type));
+	if (align_size < 0)
+	  return align_size;
+	break;
+
+      case TYPE_CODE_INT:
+	ctf_save_type_define_write (tcsp, type);
+	align_size = TYPE_LENGTH (type);
+	break;
+
+      case TYPE_CODE_PTR:
+	align_size = TYPE_LENGTH (type);
+	break;
+
+      case TYPE_CODE_STRUCT:
+        {
+	  int i, s_align_size;
+
+	  for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	    {
+	      s_align_size
+		  = ctf_save_type_check_and_write (tcsp,
+						   TYPE_FIELD_TYPE (type, i));
+	      if (s_align_size < 0)
+		return s_align_size;
+
+	      if (align_size < s_align_size)
+		align_size = s_align_size;
+	    }
+	  if (TYPE_TAG_NAME (type))
+	    ctf_save_type_define_write (tcsp, type);
+	  break;
+	}
+
+      case TYPE_CODE_ENUM:
+	align_size = TYPE_LENGTH (type);
+	if (TYPE_TAG_NAME (type))
+	  ctf_save_type_define_write (tcsp, type);
+	break;
+
+      case TYPE_CODE_UNION:
+        {
+	  int i, s_align_size;
+
+	  for (i = 0; i < TYPE_NFIELDS (type); ++i)
+	    {
+	      s_align_size
+		  = ctf_save_type_check_and_write (tcsp,
+						   TYPE_FIELD_TYPE (type, i));
+	      if (s_align_size < 0)
+		return s_align_size;
+
+	      if (align_size < s_align_size)
+		align_size = s_align_size;
+	    }
+	  break;
+	}
+
+      default:
+	align_size = -1;
+	break;
+    }
+
+  return align_size;
+}
+
+static void
+ctf_save_collect_get_1 (struct ctf_save_s *tcsp, struct ctf_save_tp_s *tps,
+			char *str)
+{
+  struct expression *expr;
+  struct ctf_save_collect_s *collect;
+  volatile struct gdb_exception e;
+  struct type *type;
+  int is_ret = 0;
+  int align_size;
+
+  /* Check if action_exp is already exist in tps->collect.  */
+  for (collect = tps->collect; collect; collect = collect->next)
+    {
+      if (strcmp (collect->str, str) == 0)
+	return;
+    }
+
+  if (0 == strncasecmp (str, "$_ret", 5))
+    is_ret = 1;
+
+  TRY_CATCH (e, RETURN_MASK_ERROR)
+    {
+      if (is_ret)
+        {
+	  CORE_ADDR pc;
+	  struct frame_info *frame;
+
+	  frame = get_current_frame ();
+	  if (!frame)
+	    error (_("get current frame fail"));
+	  frame = get_prev_frame (frame);
+	  if (!frame)
+	    error (_("get prev frame fail"));
+
+	  if (!get_frame_pc_if_available (frame, &pc))
+	    error (_("PC unavailable"));
+	}
+      else
+        {
+	  struct cleanup *old_chain;
+	  struct value *val;
+
+	  expr = parse_expression (str);
+	  old_chain = make_cleanup (free_current_contents, &expr);
+	  type = value_type (evaluate_expression (expr));
+	  do_cleanups (old_chain);
+	}
+    }
+  if (e.reason < 0)
+    {
+      warning (_("\
+not save \"%s\" of tracepoint %d to ctf file because get its value fail: %s"),
+	       str, tps->tp->base.number, e.message);
+      return;
+    }
+
+  if (is_ret)
+    align_size = 8;
+  else
+    {
+      align_size = ctf_save_type_check_and_write (tcsp, type);
+      if (align_size < 0)
+	{
+	  warning (_("\
+not save \"%s\" of tracepoint %d to ctf file because its type is not support."),
+		   str, tps->tp->base.number);
+	  return;
+	}
+    }
+
+  collect = (struct ctf_save_collect_s *) xcalloc (1, sizeof (*collect));
+
+  /* Add tp to the list.  */
+  collect->next = tps->collect;
+  tps->collect = collect;
+
+  collect->str = xstrdup (str);
+  collect->is_ret = is_ret;
+
+  if (!is_ret)
+    {
+      collect->type = type;
+
+      collect->align_size = align_size;
+      if (collect->align_size > tps->align_size)
+        tps->align_size = collect->align_size;
+
+      collect->expr = parse_expression (str);
+    }
+}
+
+struct loc_arg_collect_data
+{
+  struct ctf_save_s *tcsp;
+  struct ctf_save_tp_s *tps;
+};
+
+static void
+tsv_save_do_loc_arg_collect (const char *print_name,
+			     struct symbol *sym,
+			     void *cb_data)
+{
+  struct loc_arg_collect_data *p = cb_data;
+  char *name;
+
+  name = alloca (strlen (print_name) + 1);
+  strcpy (name, print_name);
+  ctf_save_collect_get_1 (p->tcsp, p->tps, name);
+}
+
+/* worker function (cleanup) */
+static void
+replace_comma (void *data)
+{
+  char *comma = data;
+  *comma = ',';
+}
+
+/* Get var that want to collect from STR and put them to TPS->collect.  */
+
+static void
+ctf_save_collect_get (struct ctf_save_s *tcsp, struct ctf_save_tp_s *tps,
+		      char *str)
+{
+  char *action_exp = str, *next_comma;
+  struct cleanup *old_chain;
+
+  do
+    {
+      if (*action_exp == ',')
+	action_exp++;
+      while (isspace ((int) *action_exp))
+	action_exp++;
+
+      next_comma = strchr (action_exp, ',');
+      if (next_comma)
+	{
+	  old_chain = make_cleanup (replace_comma, next_comma);
+	  *next_comma = '\0';
+	}
+
+      if (0 == strncasecmp (action_exp, "$reg", 4))
+        {
+	  int i;
+	  struct gdbarch *arch = tps->tp->base.loc->gdbarch;
+
+	  for (i = 0; i < gdbarch_num_regs (arch); i++)
+	    {
+	      const char *name = gdbarch_register_name (arch, i);
+	      int name_size = strlen (name);
+	      char regname[1 + name_size + 1];
+
+	      if (name_size == 0)
+		continue;
+
+	      sprintf (regname, "$%s", name);
+
+	      ctf_save_collect_get_1 (tcsp, tps, regname);
+	    }
+	}
+      else if (0 == strncasecmp (action_exp, "$loc", 4)
+	       || 0 == strncasecmp (action_exp, "$arg", 4))
+	{
+	  CORE_ADDR pc;
+	  struct frame_info *frame;
+	  struct loc_arg_collect_data cb_data;
+	  volatile struct gdb_exception e;
+
+	  cb_data.tcsp = tcsp;
+	  cb_data.tps = tps;
+
+	  TRY_CATCH (e, RETURN_MASK_ERROR)
+	    {
+	      frame = get_selected_frame (_("No frame selected."));
+	      if (!get_frame_pc_if_available (frame, &pc))
+	        error (_("PC unavailable"));
+	    }
+	  if (e.reason < 0)
+	    {
+	      warning (_("\
+not save \"%s\" of tracepoint %d to ctf file because get its value fail: %s"),
+		       action_exp, tps->tp->base.number, e.message);
+	      return;
+	    }
+	  if (0 == strncasecmp (action_exp, "$loc", 4))
+	    {
+	      struct block *block;
+
+	      block = get_frame_block (frame, 0);
+	      if (block == 0)
+		{
+		  warning (_("\
+Not save \"%s\" of tracepoint %d to ctf file because no symbol table info available."),
+			   action_exp, tps->tp->base.number);
+		  continue;
+		}
+
+	      iterate_over_block_local_vars (block,
+					     tsv_save_do_loc_arg_collect,
+					     &cb_data);
+	    }
+	  else
+	    {
+	      struct symbol *func;
+
+	      func = get_frame_function (frame);
+	      if (func == NULL)
+		{
+		  warning (_("\
+not save \"%s\" of tracepoint %d to ctf file because no symbol table info available."),
+			   action_exp, tps->tp->base.number);
+		  continue;
+		}
+
+	      iterate_over_block_arg_vars (SYMBOL_BLOCK_VALUE (func),
+					   tsv_save_do_loc_arg_collect,
+					   &cb_data);
+	    }
+	}
+      else
+	ctf_save_collect_get_1 (tcsp, tps, action_exp);
+
+      if (next_comma)
+	do_cleanups (old_chain);
+      action_exp = next_comma;
+    }
+  while (action_exp && *action_exp == ',');
+}
+
+/* Call function ctf_save_collect_get Add action list ACTION of TPS
+   to TPS->collect.
+   STEPPING_FRAME is true if it is step action.  */
+
+static void
+ctf_save_tp_actions (struct ctf_save_s *tcsp, struct ctf_save_tp_s *tps,
+		     struct command_line *action, int stepping_frame)
+{
+  for (; action != NULL; action = action->next)
+    {
+      char *action_exp;
+      struct cmd_list_element *cmd;
+
+      QUIT;
+      action_exp = action->line;
+      while (isspace ((int) *action_exp))
+	action_exp++;
+      if (*action_exp == '#')
+	continue;
+
+      cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1);
+      if (cmd == 0)
+	error (_("Bad action list item: %s"), action_exp);
+
+      if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand))
+	{
+	  int i;
+
+	  for (i = 0; i < action->body_count; ++i)
+	    ctf_save_tp_actions (tcsp, tps, action->body_list[i], 1);
+	}
+      else if (cmd_cfunc_eq (cmd, collect_pseudocommand))
+	{
+	  if (stepping_frame != tps->stepping_frame)
+	    continue;
+	  if (*action_exp == '/')
+	    action_exp = decode_agent_options (action_exp);
+	  ctf_save_collect_get (tcsp, tps, action_exp);
+	}
+    }
+}
+
+
+/* Try to find the ctf_save_tp_s struct in the TCSP->tp.
+   If cannot find it in the TCSP->tp, make a new one for TP
+   and add it to TCSP->tp.  */
+
+static struct ctf_save_tp_s *
+ctf_save_tp_find (struct ctf_save_s *tcsp, struct tracepoint *tp)
+{
+  struct ctf_save_tp_s *ret;
+  struct bp_location *loc;
+  struct regcache *regcache;
+  int stepping_frame = 0;
+  struct command_line *action;
+  volatile struct gdb_exception e;
+  CORE_ADDR pc;
+
+  TRY_CATCH (e, RETURN_MASK_ERROR)
+    {
+      pc = regcache_read_pc (get_current_regcache ());
+    }
+  if (e.reason >= 0)
+    {
+      stepping_frame = 1;
+      for (loc = tp->base.loc; loc; loc = loc->next)
+	{
+	  if (loc->address == pc)
+            {
+	      stepping_frame = 0;
+	      break;
+	    }
+        }
+    }
+
+  for (ret = tcsp->tp; ret; ret = ret->next)
+    {
+      if (ret->tp == tp && ret->stepping_frame == stepping_frame)
+	return ret;
+    }
+
+  ret = (struct ctf_save_tp_s *) xcalloc (1, sizeof (*ret));
+
+  /* Add tp to the list.  */
+  ret->next = tcsp->tp;
+  tcsp->tp = ret;
+
+  ret->stepping_frame = stepping_frame;
+  ret->tp = tp;
+
+  if (!stepping_frame && *default_collect)
+    ctf_save_collect_get (tcsp, ret, default_collect);
+
+  ctf_save_tp_actions (tcsp, ret, breakpoint_commands (&tp->base), 0);
+
+  return ret;
+}
+
+/* Let TCSP->datastream_fd point to the part for next packet.  */
+
+static void
+ctf_save_next_packet (struct ctf_save_s *tcsp)
+{
+  tcsp->packet_begin += CTF_PACKET_SIZE;
+  ctf_save_fseek (tcsp, tcsp->packet_begin, SEEK_SET);
+  tcsp->content_size = 0;
+}
+
+/* Write the content size to packet header context. */
+
+static void
+ctf_save_write_content_size (struct ctf_save_s *tcsp)
+{
+  uint32_t u32;
+
+  ctf_save_fseek (tcsp, tcsp->packet_begin + 4, SEEK_SET);
+  u32 = tcsp->content_size * TARGET_CHAR_BIT;
+  ctf_save_write (tcsp, (void *)&u32, 4);
+}
+
+/* Write the CTF data packet header context.  */
+
+static void
+ctf_save_write_packet_header_context (struct ctf_save_s *tcsp)
+{
+  uint32_t u32;
+
+  /* magic.  */
+  u32 = CTF_MAGIC;
+  ctf_save_write (tcsp, (void *)&u32, 4);
+  tcsp->content_size += 4;
+
+  /* content_size.  We still don't know the size, write it later.  */
+  ctf_save_fseek (tcsp, 4, SEEK_CUR);
+  tcsp->content_size += 4;
+
+  /* packet_size */
+  u32 = CTF_PACKET_SIZE * TARGET_CHAR_BIT;
+  ctf_save_write (tcsp, (void *)&u32, 4);
+  tcsp->content_size += 4;
+
+  tcsp->current_content_size = tcsp->content_size;
+
+  /* Make this packet all into file.  */
+  ctf_save_fseek (tcsp, tcsp->packet_begin + CTF_PACKET_SIZE - 4, SEEK_SET);
+  u32 = 0;
+  ctf_save_write (tcsp, (void *)&u32, 4);
+  ctf_save_fseek (tcsp, tcsp->packet_begin + tcsp->content_size, SEEK_SET);
+}
+
+/* Change I-th char in CTF_STR to STR.  */
+
+static char *
+ctf_save_metadata_change_char (char *ctf_str, int i, const char *str)
+{
+  char *new;
+
+  ctf_str[i] = '\0';
+  new = xstrprintf ("%s%s%s_%s", ctf_str, (i ? "_" : ""),
+		    str, ctf_str + i + 1);
+  xfree (ctf_str);
+
+  return new;
+}
+
+/* Write the header of CTF's metadata to TCSP->METADATA_FD.  */
+
+static void
+ctf_save_metadata_header (struct ctf_save_s *tcsp)
+{
+  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"
+  "	};\n"
+  "	event.header := struct {\n"
+  "		uint32_t id;\n"
+  "	};\n"
+  "};\n";
+
+  ctf_save_fwrite_format (tcsp->metadata_fd, "/* CTF %d.%d */\n",
+			  CTF_SAVE_MAJOR, CTF_SAVE_MINOR);
+  ctf_save_fwrite_format (tcsp->metadata_fd, "\
+typealias integer { size = 32; align = 32; signed = false; } := uint32_t;\n");
+  ctf_save_fwrite_format (tcsp->metadata_fd, "\
+typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n");
+  ctf_save_fwrite_format (tcsp->metadata_fd, "\n");
+
+  ctf_save_fwrite_format (tcsp->metadata_fd, metadata_fmt,
+			  CTF_SAVE_MAJOR, CTF_SAVE_MINOR,
+			  BYTE_ORDER == LITTLE_ENDIAN ? "le" : "be");
+  ctf_save_fwrite_format (tcsp->metadata_fd, "\n");
+}
+
+/* Write the body of CTF's metadata to TCSP->METADATA_FD.  */
+
+static void
+ctf_save_metadata (struct ctf_save_s *tcsp)
+{
+  struct ctf_save_tp_s *tps;
+  struct ctf_save_collect_s *collect;
+  struct ctf_save_type_s *t;
+
+  ctf_save_fwrite_format (tcsp->metadata_fd, "\n");
+
+  /* Write event.  */
+  for (tps = tcsp->tp; tps; tps = tps->next)
+    {
+      ctf_save_fwrite_format (tcsp->metadata_fd,
+			      "event {\n\tname = \"%s\";\n\tid = %d;\n"
+			      "\tfields := struct { \n",
+			      tps->tp->base.addr_string, tps->tp->base.number);
+      for (collect = tps->collect; collect; collect = collect->next)
+        {
+	  char *tmp;
+	  const char *old_tab;
+
+	  if (collect->is_ret)
+	    collect->ctf_str = xstrdup ("ret_pc");
+	  else
+	    {
+	      char *new;
+	      int need_recheck;
+	      int i;
+
+	      collect->ctf_str = xstrdup (collect->str);
+	      for (i = 0; collect->ctf_str[i] != '\0'; ++i)
+	        {
+		  switch (collect->ctf_str[i])
+		    {
+		    case '$':
+		      collect->ctf_str
+			  = ctf_save_metadata_change_char (collect->ctf_str,
+							   i, "dollar");
+		      break;
+		    case '*':
+		      collect->ctf_str
+			  = ctf_save_metadata_change_char (collect->ctf_str,
+							   i, "star");
+		      break;
+		    case ' ':
+		      collect->ctf_str[i] = '_';
+		      break;
+		    }
+		}
+	    }
+
+	  tmp = alloca (strlen (collect->ctf_str) + 30);
+	  strcpy (tmp, collect->ctf_str);
+	  while (1)
+	    {
+	      struct ctf_save_collect_s *collect2;
+	      int i = 0;
+
+	      for (collect2 = tps->collect; collect2;
+		   collect2 = collect2->next)
+		{
+		  if (collect2->ctf_str
+		      && strcmp (collect2->ctf_str, tmp) == 0)
+		    break;
+		}
+	      if (collect2 == NULL)
+		break;
+
+	      snprintf (tmp, strlen (collect->ctf_str) + 30,
+			"%s_%d", collect->ctf_str, i++);
+	    }
+
+	  if (strcmp (collect->ctf_str, collect->str))
+	    warning (_("\
+\"%s\" of tracepoint %d rename to \"%s\" in CTF file."),
+		       collect->str, tps->tp->base.number,
+		       collect->ctf_str);
+
+	  old_tab = tcsp->tab;
+	  tcsp->tab = "\t\t";
+	  ctf_save_fwrite_format (tcsp->metadata_fd, tcsp->tab);
+	  if (collect->is_ret)
+	    ctf_save_fwrite_format (tcsp->metadata_fd, "uint64_t %s;\n",
+				    collect->ctf_str);
+	  else
+	    ctf_save_var_define_write (tcsp, collect->type, collect->ctf_str);
+	  tcsp->tab = old_tab;
+	}
+      ctf_save_fwrite_format (tcsp->metadata_fd, "\t};\n};\n");
+    }
+}
+
+/* Clean function for ctf_save.  */
+
+static void
+ctf_save_cleanup (void *p)
+{
+  struct ctf_save_s *tcsp = p;
+  struct ctf_save_tp_s *tp, *tmp_tp;
+  struct ctf_save_type_s *type, *tmp_type;
+
+  if (tcsp->metadata_fd)
+    {
+      ctf_save_metadata (tcsp);
+      fclose (tcsp->metadata_fd);
+    }
+
+  if (tcsp->datastream_fd)
+    fclose (tcsp->datastream_fd);
+
+  for (tp = tcsp->tp; tp; tp = tmp_tp)
+    {
+      struct ctf_save_collect_s *collect, *tmp_collect;
+
+      for (collect = tp->collect; collect; collect = tmp_collect)
+        {
+	  if (collect->expr)
+	    free_current_contents (&collect->expr);
+	  if (collect->str)
+	    xfree (collect->str);
+	  if (collect->ctf_str)
+	    xfree (collect->ctf_str);
+	  tmp_collect = collect->next;
+	  xfree (collect);
+        }
+      tmp_tp = tp->next;
+      xfree (tp);
+    }
+
+  for (type = tcsp->type; type; type = tmp_type)
+    {
+      tmp_type = type->next;
+      xfree (type);
+    }
+
+  reinit_frame_cache ();
+  target_dcache_invalidate ();
+  set_current_traceframe (tcsp->old_traceframe_num);
+}
+
+/* Save current trace frame to DIRNAME.  */
+
+void
+ctf_save (const char *dirname)
+{
+  struct ctf_save_s tcs;
+  struct cleanup *old_chain;
+  int frame_num;
+  char *file_name;
+  struct ctf_save_type_s *t;
+  char tab[] = "";
+
+  /* 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 (&tcs, '\0', sizeof (tcs));
+  tcs.old_traceframe_num = get_traceframe_number ();
+  old_chain = make_cleanup (ctf_save_cleanup, &tcs);
+  tcs.tab = tab;
+
+  sprintf (file_name, "%s/%s", dirname, CTF_METADATA_NAME);
+  tcs.metadata_fd = fopen (file_name, "w");
+  if (!tcs.metadata_fd)
+    error (_("Unable to open file '%s' for saving trace data (%s)"),
+	   file_name, safe_strerror (errno));
+  ctf_save_metadata_header (&tcs);
+
+  sprintf (file_name, "%s/%s", dirname, CTF_DATASTREAM_NAME);
+  tcs.datastream_fd = fopen (file_name, "w");
+  if (!tcs.datastream_fd)
+    error (_("Unable to open file '%s' for saving trace data (%s)"),
+	   file_name, safe_strerror (errno));
+
+  ctf_save_write_packet_header_context(&tcs);
+
+  for (frame_num = 0; 1; frame_num ++)
+    {
+      int tnum;
+      struct tracepoint *tp;
+      int try_count;
+      uint32_t u32;
+      struct ctf_save_tp_s *tps;
+      struct ctf_save_collect_s *collect;
+      int traceframe_is_dropped = 0;
+
+      /* Allow user to bail out with ^C.  */
+      QUIT;
+
+      /* Goto traceframe frame_num and set tp.  */
+      frame_num = target_trace_find (tfind_number, frame_num, 0, 0, &tnum);
+      if (frame_num < 0)
+	break;
+      tp = get_tracepoint_by_number_on_target (tnum);
+      if (!tp)
+        {
+	  warning (_("drop traceframe %d because cannot find tracepoint %d."),
+		   frame_num, tnum);
+	  continue;
+	}
+      if (!tp->base.loc)
+        {
+	  warning (_("drop traceframe %d because tracepoint %d is pending."),
+		   frame_num, tp->base.number);
+	  continue;
+	}
+      reinit_frame_cache ();
+      target_dcache_invalidate ();
+      set_current_traceframe (frame_num);
+
+      tps = ctf_save_tp_find (&tcs, tp);
+
+      /* The tp is not variable-length and bigger than CTF_PACKET_SIZE.
+         So drop it.  */
+      if (tps->is_dropped)
+	continue;
+
+      /* Try count for current tp write.
+         If try second time, the event size bigger than a packet.
+         Then drop this event.  */
+      try_count = 0;
+
+re_write_tp:
+      if (try_count > 0)
+        {
+	  /* Handle retry.  */
+	  if (try_count > 1 || 4 + 4 + 4 == tcs.content_size)
+	    {
+	      /* Second retry or packet just write that packet means
+	         this TP is too big.  So drop it.  */
+	      if (tps->is_variable_length)
+	        {
+		  /* The tp is variable-length.  */
+		  warning (_("\
+traceframe %d of tracepoint %d need save data that bigger than packet size %d.\n\
+So it will be dropped."), frame_num, tps->tp->base.number, CTF_PACKET_SIZE);
+		}
+	      else
+	        {
+		  /* The tp is not variable-length.  */
+		  warning (_("\
+tracepoint %d need save data that bigger than packet size %d.\n\
+So all of its traceframes will be dropped."),
+			   tps->tp->base.number, CTF_PACKET_SIZE);
+		  /* Mark this tp to let GDB drop its traceframes.  */
+		  tps->is_dropped = 1;
+		}
+	      continue;
+	    }
+
+	  tcs.check_size = 0;
+	  ctf_save_write_content_size (&tcs);
+          ctf_save_next_packet (&tcs);
+	  ctf_save_write_packet_header_context(&tcs);
+	}
+
+      try_count ++;
+
+      tcs.current_content_size = tcs.content_size;
+      tcs.check_size = 1;
+
+      /* Write event header */
+      if (tps->stepping_frame)
+        u32 = (uint32_t) (-tps->tp->base.number);
+      else
+	u32 = (uint32_t) tps->tp->base.number;
+      if (ctf_save_align_write (&tcs, (void *)&u32, sizeof (u32),
+				sizeof (u32)))
+	goto re_write_tp;
+
+      /* Align.  */
+      if (tps->align_size)
+        {
+	  if (ctf_save_fseek (&tcs,
+			      ALIGN_SIZE(tcs.current_content_size,
+					 tps->align_size),
+			      SEEK_CUR))
+	    goto re_write_tp;
+	}
+
+      for (collect = tps->collect; collect; collect = collect->next)
+        {
+	  volatile struct gdb_exception e;
+
+	  if (collect->is_ret)
+	    {
+	      CORE_ADDR pc;
+	      uint64_t u64;
+
+	      TRY_CATCH (e, RETURN_MASK_ERROR)
+		{
+		  struct frame_info *frame;
+
+		  frame = get_current_frame ();
+		  if (!frame)
+		    error (_("get current frame fail"));
+		  frame = get_prev_frame (frame);
+		  if (!frame)
+		    error (_("get prev frame fail"));
+
+		  if (!get_frame_pc_if_available (frame, &pc))
+		    error (_("PC unavailable"));
+		}
+	      if (e.reason < 0)
+		{
+		  warning (_("\
+traceframe %d is dropped because try to get the value of \"%s\" got error: %s"),
+			   frame_num, collect->str, e.message);
+		  traceframe_is_dropped = 1;
+		  break;
+		}
+
+	      u64 = pc;
+	      if (ctf_save_align_write (&tcs, (gdb_byte *) &u64,
+					sizeof (u64), sizeof (u64)))
+		goto re_write_tp;
+	    }
+	  else
+            {
+	      struct value *val;
+	      struct cleanup *back_chain;
+	      const gdb_byte *content;
+
+	      back_chain = make_cleanup (null_cleanup, NULL);
+	      TRY_CATCH (e, RETURN_MASK_ERROR)
+	        {
+		  val = evaluate_expression (collect->expr);
+		  content = value_contents (val);
+		}
+	      if (e.reason < 0 || collect->type != value_type (val))
+		{
+		  if (e.reason < 0)
+		    warning (_("\
+traceframe %d is dropped because try to get the value of \"%s\" got error: %s"),
+			     frame_num, collect->str, e.message);
+		  else
+		    warning (_("\
+traceframe %d is dropped because type of \"%s\" is wrong."),
+			     frame_num, collect->str);
+		  traceframe_is_dropped = 1;
+		  do_cleanups (back_chain);
+		  break;
+		}
+
+	      /* Write this val according to type.  */
+	      if (ctf_save_align_write (&tcs, content,
+					TYPE_LENGTH (collect->type),
+					collect->align_size))
+		{
+		  do_cleanups (back_chain);
+		  goto re_write_tp;
+		}
+
+	      /* Free the memory that alloc by evaluate_expression.  */
+	      do_cleanups (back_chain);
+	    }
+	}
+
+      if (traceframe_is_dropped)
+	continue;
+
+      tcs.content_size = tcs.current_content_size;
+    }
+  ctf_save_write_content_size (&tcs);
+
+  do_cleanups (old_chain);
+}
--- /dev/null
+++ b/ctf.h
@@ -0,0 +1,25 @@
+/* CTF format support.
+
+   Copyright (C) 2012 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 void ctf_save (const char *dirname);
+
+#endif
--- a/mi/mi-main.c
+++ b/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"
 
@@ -2495,16 +2496,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]);
     }
@@ -2513,7 +2517,10 @@ mi_cmd_trace_save (char *command, char *
       filename = argv[0];
     }
 
-  trace_save (filename, target_saves);
+  if (generate_ctf)
+    ctf_save (filename);
+  else
+    trace_save (filename, target_saves);
 }
 
 void
--- a/tracepoint.c
+++ b/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"
@@ -579,7 +580,7 @@ while_stepping_pseudocommand (char *args
   error (_("This command can only be used in a tracepoint actions list."));
 }
 
-static void
+void
 collect_pseudocommand (char *args, int from_tty)
 {
   error (_("This command can only be used in a tracepoint actions list."));
@@ -3152,6 +3153,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;
@@ -3166,6 +3168,8 @@ trace_save_command (char *args, int from
     {
       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
@@ -3175,10 +3179,18 @@ trace_save_command (char *args, int from
   if (!filename)
     error_no_arg (_("file in which to save trace data"));
 
-  trace_save (filename, target_does_save);
+  if (generate_ctf)
+    {
+      if (target_does_save)
+	error_no_arg (_("-r cannot be used with -ctf."));
+      ctf_save (filename);
+    }
+  else
+    trace_save (filename, 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);
 }
@@ -5194,6 +5206,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,_("\
--- a/tracepoint.h
+++ b/tracepoint.h
@@ -246,6 +246,7 @@ extern void validate_actionline (char **
 
 extern void end_actions_pseudocommand (char *args, int from_tty);
 extern void while_stepping_pseudocommand (char *args, int from_tty);
+extern void collect_pseudocommand (char *args, int from_tty);
 
 extern struct trace_state_variable *find_trace_state_variable (const char *name);
 extern struct trace_state_variable *create_trace_state_variable (const char *name);


More information about the Gdb-patches mailing list