From 7951c4eb08bdb6843989e4dfdddbd7cad5e1fd9c Mon Sep 17 00:00:00 2001 From: Yao Qi Date: Mon, 10 Feb 2014 13:31:12 +0800 Subject: [PATCH] Move trace file writer out of tracepoint.c This patch is a refactor which moves trace file writer related code out of tracepoint.c, which has 6k LOC. It moves general trace file writer to a new file tracefile.c and moves tfile specific writer to tracefile-tfile.c. gdb: 2014-02-23 Yao Qi * Makefile.in (REMOTE_OBS): Append tracefile.o and tracefile-tfile.o. (HFILES_NO_SRCDIR): Add tracefile.h. * ctf.c: Include "tracefile.h". * tracefile.h: New file. * tracefile.c: New file * tracefile-tfile.c: New file. * tracepoint.c: Include "tracefile.h". (free_uploaded_tps, free_uploaded_tsvs): Remove declarations. (stop_reason_names): Add const. (trace_file_writer_xfree): Move it to tracefile.c. (trace_save, trace_save_command, trace_save_tfile): Likewise. (trace_save_ctf): Likewise. (struct tfile_trace_file_writer): Move it to tracefile-tfile.c. (tfile_target_save, tfile_dtor, tfile_start): Likewise. (tfile_write_header, tfile_write_regblock_type): Likewise. (tfile_write_status, tfile_write_uploaded_tsv): Likewise. (tfile_write_uploaded_tp, tfile_write_definition_end): Likewise. (tfile_write_raw_data, tfile_end): Likewise. (tfile_trace_file_writer_new): Likewise. (free_uploaded_tp): Make it extern. (free_uploaded_tsv): Make it extern. (_initialize_tracepoint): Move code to register command 'tsave' to tracefile.c. * tracepoint.h (stop_reason_names): Declare. (struct trace_frame_write_ops): Move it to tracefile.h. (struct trace_file_write_ops): Likewise. (struct trace_file_writer): Likewise. (free_uploaded_tsvs, free_uploaded_tps): Declare. --- gdb/ChangeLog | 32 ++ gdb/Makefile.in | 4 +- gdb/ctf.c | 1 + gdb/tracefile-tfile.c | 327 ++++++++++++++++++++ gdb/tracefile.c | 389 ++++++++++++++++++++++++ gdb/tracefile.h | 114 +++++++ gdb/tracepoint.c | 674 +----------------------------------------- gdb/tracepoint.h | 110 +------ 8 files changed, 874 insertions(+), 777 deletions(-) create mode 100644 gdb/tracefile-tfile.c create mode 100644 gdb/tracefile.c create mode 100644 gdb/tracefile.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f97c5fe7dbe..2004167c53d 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,35 @@ +2014-02-23 Yao Qi + + * Makefile.in (REMOTE_OBS): Append tracefile.o and + tracefile-tfile.o. + (HFILES_NO_SRCDIR): Add tracefile.h. + * ctf.c: Include "tracefile.h". + * tracefile.h: New file. + * tracefile.c: New file + * tracefile-tfile.c: New file. + * tracepoint.c: Include "tracefile.h". + (free_uploaded_tps, free_uploaded_tsvs): Remove declarations. + (stop_reason_names): Add const. + (trace_file_writer_xfree): Move it to tracefile.c. + (trace_save, trace_save_command, trace_save_tfile): Likewise. + (trace_save_ctf): Likewise. + (struct tfile_trace_file_writer): Move it to tracefile-tfile.c. + (tfile_target_save, tfile_dtor, tfile_start): Likewise. + (tfile_write_header, tfile_write_regblock_type): Likewise. + (tfile_write_status, tfile_write_uploaded_tsv): Likewise. + (tfile_write_uploaded_tp, tfile_write_definition_end): Likewise. + (tfile_write_raw_data, tfile_end): Likewise. + (tfile_trace_file_writer_new): Likewise. + (free_uploaded_tp): Make it extern. + (free_uploaded_tsv): Make it extern. + (_initialize_tracepoint): Move code to register command 'tsave' + to tracefile.c. + * tracepoint.h (stop_reason_names): Declare. + (struct trace_frame_write_ops): Move it to tracefile.h. + (struct trace_file_write_ops): Likewise. + (struct trace_file_writer): Likewise. + (free_uploaded_tsvs, free_uploaded_tps): Declare. + 2014-02-21 Jan Kratochvil PR gdb/16594 diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 28847251b25..fe069883c39 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -584,7 +584,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 ctf.o + remote-notif.o ctf.o tracefile.o tracefile-tfile.o # This is remote-sim.o if a simulator is to be linked in. SIM_OBS = @SIM_OBS@ @@ -894,7 +894,7 @@ doublest.h regset.h hppa-tdep.h ppc-linux-tdep.h ppc64-tdep.h \ rs6000-tdep.h rs6000-aix-tdep.h \ common/gdb_locale.h arch-utils.h trad-frame.h gnu-nat.h \ language.h nbsd-tdep.h solib-svr4.h \ -macroexp.h ui-file.h regcache.h tracepoint.h i386-tdep.h \ +macroexp.h ui-file.h regcache.h tracepoint.h tracefile.h i386-tdep.h \ inf-child.h p-lang.h event-top.h gdbtypes.h user-regs.h \ regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \ config/i386/nm-fbsd.h \ diff --git a/gdb/ctf.c b/gdb/ctf.c index 8777014e220..ae075f92e35 100644 --- a/gdb/ctf.c +++ b/gdb/ctf.c @@ -28,6 +28,7 @@ #include "completer.h" #include "inferior.h" #include "gdbthread.h" +#include "tracefile.h" #include diff --git a/gdb/tracefile-tfile.c b/gdb/tracefile-tfile.c new file mode 100644 index 00000000000..8a53e9d6eb7 --- /dev/null +++ b/gdb/tracefile-tfile.c @@ -0,0 +1,327 @@ +/* Trace file TFILE format support in GDB. + + Copyright (C) 1997-2014 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 "tracefile.h" +#include "readline/tilde.h" +#include "filestuff.h" +#include "rsp-low.h" /* bin2hex */ + +/* TFILE trace writer. */ + +struct tfile_trace_file_writer +{ + struct trace_file_writer base; + + /* File pointer to tfile trace file. */ + FILE *fp; + /* Path name of the tfile trace file. */ + char *pathname; +}; + +/* This is the implementation of trace_file_write_ops method + target_save. We just call the generic target + target_save_trace_data to do target-side saving. */ + +static int +tfile_target_save (struct trace_file_writer *self, + const char *filename) +{ + int err = target_save_trace_data (filename); + + return (err >= 0); +} + +/* 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); +} + +/* This is the implementation of trace_file_write_ops method + start. It creates the trace file FILENAME and registers some + cleanups. */ + +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 = gdb_fopen_cloexec (writer->pathname, "wb"); + if (writer->fp == NULL) + error (_("Unable to open file '%s' for saving trace data (%s)"), + writer->pathname, safe_strerror (errno)); +} + +/* 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, writer->fp); + if (written < 1) + perror_with_name (writer->pathname); +} + +/* This is the implementation of trace_file_write_ops method + write_regblock_type. Write the size of register block. */ + +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; + + 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 + || ts->stop_reason == tstop_command) + { + char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1); + + bin2hex ((gdb_byte *) ts->stop_desc, buf, strlen (ts->stop_desc)); + fprintf (writer->fp, ":%s", buf); + } + fprintf (writer->fp, ":%x", ts->stopping_tracepoint); + if (ts->traceframe_count >= 0) + fprintf (writer->fp, ";tframes:%x", ts->traceframe_count); + if (ts->traceframes_created >= 0) + fprintf (writer->fp, ";tcreated:%x", ts->traceframes_created); + if (ts->buffer_free >= 0) + fprintf (writer->fp, ";tfree:%x", ts->buffer_free); + if (ts->buffer_size >= 0) + fprintf (writer->fp, ";tsize:%x", ts->buffer_size); + if (ts->disconnected_tracing) + fprintf (writer->fp, ";disconn:%x", ts->disconnected_tracing); + if (ts->circular_buffer) + fprintf (writer->fp, ";circular:%x", ts->circular_buffer); + if (ts->start_time) + { + fprintf (writer->fp, ";starttime:%s", + phex_nz (ts->start_time, sizeof (ts->start_time))); + } + if (ts->stop_time) + { + fprintf (writer->fp, ";stoptime:%s", + phex_nz (ts->stop_time, sizeof (ts->stop_time))); + } + if (ts->notes != NULL) + { + char *buf = (char *) alloca (strlen (ts->notes) * 2 + 1); + + bin2hex ((gdb_byte *) ts->notes, buf, strlen (ts->notes)); + fprintf (writer->fp, ";notes:%s", buf); + } + if (ts->user_name != NULL) + { + char *buf = (char *) alloca (strlen (ts->user_name) * 2 + 1); + + bin2hex ((gdb_byte *) ts->user_name, buf, strlen (ts->user_name)); + fprintf (writer->fp, ";username:%s", buf); + } + 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, strlen (utsv->name)); + } + + 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; + char 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, +}; + +/* Return a trace writer for TFILE format. */ + +struct trace_file_writer * +tfile_trace_file_writer_new (void) +{ + struct tfile_trace_file_writer *writer + = xmalloc (sizeof (struct tfile_trace_file_writer)); + + writer->base.ops = &tfile_write_ops; + writer->fp = NULL; + writer->pathname = NULL; + + return (struct trace_file_writer *) writer; +} diff --git a/gdb/tracefile.c b/gdb/tracefile.c new file mode 100644 index 00000000000..9945ae5dc0d --- /dev/null +++ b/gdb/tracefile.c @@ -0,0 +1,389 @@ +/* Trace file support in GDB. + + Copyright (C) 1997-2014 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 "tracefile.h" +#include "ctf.h" + +/* 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)) + +/* Free trace file writer. */ + +static void +trace_file_writer_xfree (void *arg) +{ + struct trace_file_writer *writer = arg; + + writer->ops->dtor (writer); + xfree (writer); +} + +/* 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; +#define MAX_TRACE_UPLOAD 2000 + 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 + changed tracepoints in GDB in preparation for a future tracing + run, or maybe just mass-deleted all types of breakpoints as part + of cleaning up. So as not to contaminate the session, leave the + data in its uploaded form, don't make into real tracepoints. */ + + /* Get trace state variables first, they may be checked when parsing + uploaded commands. */ + + target_upload_trace_state_variables (&uploaded_tsvs); + + for (utsv = uploaded_tsvs; utsv; utsv = utsv->next) + writer->ops->write_uploaded_tsv (writer, utsv); + + free_uploaded_tsvs (&uploaded_tsvs); + + target_upload_tracepoints (&uploaded_tps); + + for (utp = uploaded_tps; utp; utp = utp->next) + 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) + { + 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) + { + /* 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; + } + else + { + 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); + } + } + + writer->ops->end (writer); +} + +static void +trace_save_command (char *args, int from_tty) +{ + int target_does_save = 0; + char **argv; + char *filename = NULL; + struct cleanup *back_to; + int generate_ctf = 0; + struct trace_file_writer *writer = NULL; + + if (args == NULL) + error_no_arg (_("file in which to save trace data")); + + argv = gdb_buildargv (args); + back_to = make_cleanup_freeargv (argv); + + for (; *argv; ++argv) + { + 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 + filename = *argv; + } + + if (!filename) + error_no_arg (_("file in which to save trace data")); + + if (generate_ctf) + writer = ctf_trace_file_writer_new (); + else + writer = tfile_trace_file_writer_new (); + + make_cleanup (trace_file_writer_xfree, writer); + + trace_save (filename, writer, target_does_save); + + if (from_tty) + printf_filtered (_("Trace data saved to %s '%s'.\n"), + generate_ctf ? "directory" : "file", filename); + + 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); +} + +/* Save the trace data to dir DIRNAME of ctf format. */ + +void +trace_save_ctf (const char *dirname, int target_does_save) +{ + struct trace_file_writer *writer; + struct cleanup *back_to; + + writer = ctf_trace_file_writer_new (); + back_to = make_cleanup (trace_file_writer_xfree, writer); + + trace_save (dirname, writer, target_does_save); + do_cleanups (back_to); +} + +extern initialize_file_ftype _initialize_tracefile; + +void +_initialize_tracefile (void) +{ + add_com ("tsave", class_trace, trace_save_command, _("\ +Save the trace data to a file.\n\ +Use the '-ctf' option to save the data to CTF format.\n\ +Use the '-r' option to direct the target to save directly to the file,\n\ +using its own filesystem.")); +} diff --git a/gdb/tracefile.h b/gdb/tracefile.h new file mode 100644 index 00000000000..833de5ccd72 --- /dev/null +++ b/gdb/tracefile.h @@ -0,0 +1,114 @@ +#ifndef TRACEFILE_H +#define TRACEFILE_H 1 + +#include "defs.h" +#include "tracepoint.h" + +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 struct trace_file_writer *tfile_trace_file_writer_new (void); + +#endif /* TRACEFILE_H */ diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index de4f6af2524..2b4c13778d3 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -56,6 +56,7 @@ #include "ctf.h" #include "filestuff.h" #include "rsp-low.h" +#include "tracefile.h" /* readline include files */ #include "readline/readline.h" @@ -191,9 +192,6 @@ static char *mem2hex (gdb_byte *, char *, int); static void add_register (struct collection_list *collection, unsigned int regno); -static void free_uploaded_tps (struct uploaded_tp **utpp); -static void free_uploaded_tsvs (struct uploaded_tsv **utsvp); - static struct command_line * all_tracepoint_actions_and_cleanup (struct breakpoint *t); @@ -201,7 +199,7 @@ extern void _initialize_tracepoint (void); static struct trace_status trace_status; -char *stop_reason_names[] = { +const char *stop_reason_names[] = { "tunknown", "tnotrun", "tstop", @@ -3096,664 +3094,6 @@ encode_source_string (int tpnum, ULONGEST addr, return -1; } -/* Free trace file writer. */ - -static void -trace_file_writer_xfree (void *arg) -{ - struct trace_file_writer *writer = arg; - - writer->ops->dtor (writer); - xfree (writer); -} - -/* TFILE trace writer. */ - -struct tfile_trace_file_writer -{ - struct trace_file_writer base; - - /* File pointer to tfile trace file. */ - FILE *fp; - /* Path name of the tfile trace file. */ - char *pathname; -}; - -/* This is the implementation of trace_file_write_ops method - target_save. We just call the generic target - target_save_trace_data to do target-side saving. */ - -static int -tfile_target_save (struct trace_file_writer *self, - const char *filename) -{ - int err = target_save_trace_data (filename); - - return (err >= 0); -} - -/* 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); -} - -/* This is the implementation of trace_file_write_ops method - start. It creates the trace file FILENAME and registers some - cleanups. */ - -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 = gdb_fopen_cloexec (writer->pathname, "wb"); - if (writer->fp == NULL) - error (_("Unable to open file '%s' for saving trace data (%s)"), - writer->pathname, safe_strerror (errno)); -} - -/* 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, writer->fp); - if (written < 1) - perror_with_name (writer->pathname); -} - -/* This is the implementation of trace_file_write_ops method - write_regblock_type. Write the size of register block. */ - -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; - - 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 - || ts->stop_reason == tstop_command) - { - char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1); - - bin2hex ((gdb_byte *) ts->stop_desc, buf, strlen (ts->stop_desc)); - fprintf (writer->fp, ":%s", buf); - } - fprintf (writer->fp, ":%x", ts->stopping_tracepoint); - if (ts->traceframe_count >= 0) - fprintf (writer->fp, ";tframes:%x", ts->traceframe_count); - if (ts->traceframes_created >= 0) - fprintf (writer->fp, ";tcreated:%x", ts->traceframes_created); - if (ts->buffer_free >= 0) - fprintf (writer->fp, ";tfree:%x", ts->buffer_free); - if (ts->buffer_size >= 0) - fprintf (writer->fp, ";tsize:%x", ts->buffer_size); - if (ts->disconnected_tracing) - fprintf (writer->fp, ";disconn:%x", ts->disconnected_tracing); - if (ts->circular_buffer) - fprintf (writer->fp, ";circular:%x", ts->circular_buffer); - if (ts->start_time) - { - fprintf (writer->fp, ";starttime:%s", - phex_nz (ts->start_time, sizeof (ts->start_time))); - } - if (ts->stop_time) - { - fprintf (writer->fp, ";stoptime:%s", - phex_nz (ts->stop_time, sizeof (ts->stop_time))); - } - if (ts->notes != NULL) - { - char *buf = (char *) alloca (strlen (ts->notes) * 2 + 1); - - bin2hex ((gdb_byte *) ts->notes, buf, strlen (ts->notes)); - fprintf (writer->fp, ";notes:%s", buf); - } - if (ts->user_name != NULL) - { - char *buf = (char *) alloca (strlen (ts->user_name) * 2 + 1); - - bin2hex ((gdb_byte *) ts->user_name, buf, strlen (ts->user_name)); - fprintf (writer->fp, ";username:%s", buf); - } - 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, strlen (utsv->name)); - } - - 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; - char 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)) - -/* 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]; -#define MAX_TRACE_UPLOAD 2000 - 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 - changed tracepoints in GDB in preparation for a future tracing - run, or maybe just mass-deleted all types of breakpoints as part - of cleaning up. So as not to contaminate the session, leave the - data in its uploaded form, don't make into real tracepoints. */ - - /* Get trace state variables first, they may be checked when parsing - uploaded commands. */ - - target_upload_trace_state_variables (&uploaded_tsvs); - - for (utsv = uploaded_tsvs; utsv; utsv = utsv->next) - writer->ops->write_uploaded_tsv (writer, utsv); - - free_uploaded_tsvs (&uploaded_tsvs); - - target_upload_tracepoints (&uploaded_tps); - - for (utp = uploaded_tps; utp; utp = utp->next) - 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) - { - 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) - { - /* 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; - } - else - { - 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); - } - } - - writer->ops->end (writer); -} - -/* Return a trace writer for TFILE format. */ - -static struct trace_file_writer * -tfile_trace_file_writer_new (void) -{ - struct tfile_trace_file_writer *writer - = xmalloc (sizeof (struct tfile_trace_file_writer)); - - writer->base.ops = &tfile_write_ops; - writer->fp = NULL; - writer->pathname = NULL; - - return (struct trace_file_writer *) writer; -} - -static void -trace_save_command (char *args, int from_tty) -{ - int target_does_save = 0; - char **argv; - char *filename = NULL; - struct cleanup *back_to; - int generate_ctf = 0; - struct trace_file_writer *writer = NULL; - - if (args == NULL) - error_no_arg (_("file in which to save trace data")); - - argv = gdb_buildargv (args); - back_to = make_cleanup_freeargv (argv); - - for (; *argv; ++argv) - { - 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 - filename = *argv; - } - - if (!filename) - error_no_arg (_("file in which to save trace data")); - - if (generate_ctf) - writer = ctf_trace_file_writer_new (); - else - writer = tfile_trace_file_writer_new (); - - make_cleanup (trace_file_writer_xfree, writer); - - trace_save (filename, writer, target_does_save); - - if (from_tty) - printf_filtered (_("Trace data saved to %s '%s'.\n"), - generate_ctf ? "directory" : "file", filename); - - 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); -} - -/* Save the trace data to dir DIRNAME of ctf format. */ - -void -trace_save_ctf (const char *dirname, int target_does_save) -{ - struct trace_file_writer *writer; - struct cleanup *back_to; - - writer = ctf_trace_file_writer_new (); - back_to = make_cleanup (trace_file_writer_xfree, writer); - - trace_save (dirname, 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. */ @@ -3953,7 +3293,7 @@ get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp) return utp; } -static void +void free_uploaded_tps (struct uploaded_tp **utpp) { struct uploaded_tp *next_one; @@ -3985,7 +3325,7 @@ get_uploaded_tsv (int num, struct uploaded_tsv **utsvp) return utsv; } -static void +void free_uploaded_tsvs (struct uploaded_tsv **utsvp) { struct uploaded_tsv *next_one; @@ -5842,12 +5182,6 @@ _initialize_tracepoint (void) add_com ("tdump", class_trace, trace_dump_command, _("Print everything collected at the current tracepoint.")); - add_com ("tsave", class_trace, trace_save_command, _("\ -Save the trace data to a file.\n\ -Use the '-ctf' option to save the data to CTF format.\n\ -Use the '-r' option to direct the target to save directly to the file,\n\ -using its own filesystem.")); - c = add_com ("tvariable", class_trace, trace_variable_command,_("\ Define a trace state variable.\n\ Argument is a $-prefixed name, optionally followed\n\ diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h index dd9b48bf379..6b00b308542 100644 --- a/gdb/tracepoint.h +++ b/gdb/tracepoint.h @@ -155,6 +155,8 @@ extern char *default_collect; extern int trace_regblock_size; +extern const char *stop_reason_names[]; + /* Struct to collect random info about tracepoints on the target. */ struct uploaded_tp @@ -218,111 +220,6 @@ 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; -}; - struct memrange { /* memrange_absolute for absolute memory range, else basereg @@ -424,8 +321,11 @@ extern void parse_tsv_definition (char *line, struct uploaded_tsv **utsvp); extern struct uploaded_tp *get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp); +extern void free_uploaded_tps (struct uploaded_tp **utpp); + extern struct uploaded_tsv *get_uploaded_tsv (int num, struct uploaded_tsv **utsvp); +extern void free_uploaded_tsvs (struct uploaded_tsv **utsvp); extern struct tracepoint *create_tracepoint_from_upload (struct uploaded_tp *utp); extern void merge_uploaded_tracepoints (struct uploaded_tp **utpp); extern void merge_uploaded_trace_state_variables (struct uploaded_tsv **utsvp); -- 2.30.2