--- a/Makefile.in +++ b/Makefile.in @@ -508,7 +508,7 @@ SER_HARDWIRE = @SER_HARDWIRE@ # The `remote' debugging target is supported for most architectures, # but not all (e.g. 960) REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o \ - remote-notif.o + remote-notif.o ctf.o # This is remote-sim.o if a simulator is to be linked in. SIM_OBS = @SIM_OBS@ @@ -754,7 +754,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 @@ -830,7 +830,7 @@ gnulib/import/stddef.in.h gnulib/import/ common/common-utils.h common/xml-utils.h common/buffer.h common/ptid.h \ common/format.h common/host-defs.h utils.h common/queue.h \ common/linux-osdata.h gdb-dlfcn.h auto-load.h probe.h stap-probe.h \ -gdb_bfd.h sparc-ravenscar-thread.h ppc-ravenscar-thread.h +gdb_bfd.h sparc-ravenscar-thread.h ppc-ravenscar-thread.h ctf.h # Header files that already have srcdir in them, or which are in objdir. --- /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 . */ + +#include "defs.h" +#include "ctf.h" +#include "tracepoint.h" +#include "regcache.h" +#include "gdbcmd.h" +#include "exceptions.h" +#include "stack.h" + +#include + +/* 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 . */ + +#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.")); @@ -3157,6 +3158,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; @@ -3171,6 +3173,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 @@ -3180,10 +3184,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); } @@ -5227,6 +5239,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);