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 <yao@codesourcery.com> * 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.
This commit is contained in:
parent
cb5111bcea
commit
7951c4eb08
8 changed files with 874 additions and 777 deletions
|
@ -1,3 +1,35 @@
|
||||||
|
2014-02-23 Yao Qi <yao@codesourcery.com>
|
||||||
|
|
||||||
|
* 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 <jan.kratochvil@redhat.com>
|
2014-02-21 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
PR gdb/16594
|
PR gdb/16594
|
||||||
|
|
|
@ -584,7 +584,7 @@ SER_HARDWIRE = @SER_HARDWIRE@
|
||||||
# The `remote' debugging target is supported for most architectures,
|
# The `remote' debugging target is supported for most architectures,
|
||||||
# but not all (e.g. 960)
|
# 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 \
|
||||||
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.
|
# This is remote-sim.o if a simulator is to be linked in.
|
||||||
SIM_OBS = @SIM_OBS@
|
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 \
|
rs6000-tdep.h rs6000-aix-tdep.h \
|
||||||
common/gdb_locale.h arch-utils.h trad-frame.h gnu-nat.h \
|
common/gdb_locale.h arch-utils.h trad-frame.h gnu-nat.h \
|
||||||
language.h nbsd-tdep.h solib-svr4.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 \
|
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 \
|
regformats/regdef.h config/alpha/nm-osf3.h config/i386/nm-i386gnu.h \
|
||||||
config/i386/nm-fbsd.h \
|
config/i386/nm-fbsd.h \
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "completer.h"
|
#include "completer.h"
|
||||||
#include "inferior.h"
|
#include "inferior.h"
|
||||||
#include "gdbthread.h"
|
#include "gdbthread.h"
|
||||||
|
#include "tracefile.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
327
gdb/tracefile-tfile.c
Normal file
327
gdb/tracefile-tfile.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
389
gdb/tracefile.c
Normal file
389
gdb/tracefile.c
Normal file
|
@ -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 <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#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."));
|
||||||
|
}
|
114
gdb/tracefile.h
Normal file
114
gdb/tracefile.h
Normal file
|
@ -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 */
|
674
gdb/tracepoint.c
674
gdb/tracepoint.c
|
@ -56,6 +56,7 @@
|
||||||
#include "ctf.h"
|
#include "ctf.h"
|
||||||
#include "filestuff.h"
|
#include "filestuff.h"
|
||||||
#include "rsp-low.h"
|
#include "rsp-low.h"
|
||||||
|
#include "tracefile.h"
|
||||||
|
|
||||||
/* readline include files */
|
/* readline include files */
|
||||||
#include "readline/readline.h"
|
#include "readline/readline.h"
|
||||||
|
@ -191,9 +192,6 @@ static char *mem2hex (gdb_byte *, char *, int);
|
||||||
static void add_register (struct collection_list *collection,
|
static void add_register (struct collection_list *collection,
|
||||||
unsigned int regno);
|
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 *
|
static struct command_line *
|
||||||
all_tracepoint_actions_and_cleanup (struct breakpoint *t);
|
all_tracepoint_actions_and_cleanup (struct breakpoint *t);
|
||||||
|
|
||||||
|
@ -201,7 +199,7 @@ extern void _initialize_tracepoint (void);
|
||||||
|
|
||||||
static struct trace_status trace_status;
|
static struct trace_status trace_status;
|
||||||
|
|
||||||
char *stop_reason_names[] = {
|
const char *stop_reason_names[] = {
|
||||||
"tunknown",
|
"tunknown",
|
||||||
"tnotrun",
|
"tnotrun",
|
||||||
"tstop",
|
"tstop",
|
||||||
|
@ -3096,664 +3094,6 @@ encode_source_string (int tpnum, ULONGEST addr,
|
||||||
return -1;
|
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
|
/* Tell the target what to do with an ongoing tracing run if GDB
|
||||||
disconnects for some reason. */
|
disconnects for some reason. */
|
||||||
|
|
||||||
|
@ -3953,7 +3293,7 @@ get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp)
|
||||||
return utp;
|
return utp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
free_uploaded_tps (struct uploaded_tp **utpp)
|
free_uploaded_tps (struct uploaded_tp **utpp)
|
||||||
{
|
{
|
||||||
struct uploaded_tp *next_one;
|
struct uploaded_tp *next_one;
|
||||||
|
@ -3985,7 +3325,7 @@ get_uploaded_tsv (int num, struct uploaded_tsv **utsvp)
|
||||||
return utsv;
|
return utsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
free_uploaded_tsvs (struct uploaded_tsv **utsvp)
|
free_uploaded_tsvs (struct uploaded_tsv **utsvp)
|
||||||
{
|
{
|
||||||
struct uploaded_tsv *next_one;
|
struct uploaded_tsv *next_one;
|
||||||
|
@ -5842,12 +5182,6 @@ _initialize_tracepoint (void)
|
||||||
add_com ("tdump", class_trace, trace_dump_command,
|
add_com ("tdump", class_trace, trace_dump_command,
|
||||||
_("Print everything collected at the current tracepoint."));
|
_("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,_("\
|
c = add_com ("tvariable", class_trace, trace_variable_command,_("\
|
||||||
Define a trace state variable.\n\
|
Define a trace state variable.\n\
|
||||||
Argument is a $-prefixed name, optionally followed\n\
|
Argument is a $-prefixed name, optionally followed\n\
|
||||||
|
|
110
gdb/tracepoint.h
110
gdb/tracepoint.h
|
@ -155,6 +155,8 @@ extern char *default_collect;
|
||||||
|
|
||||||
extern int trace_regblock_size;
|
extern int trace_regblock_size;
|
||||||
|
|
||||||
|
extern const char *stop_reason_names[];
|
||||||
|
|
||||||
/* Struct to collect random info about tracepoints on the target. */
|
/* Struct to collect random info about tracepoints on the target. */
|
||||||
|
|
||||||
struct uploaded_tp
|
struct uploaded_tp
|
||||||
|
@ -218,111 +220,6 @@ struct static_tracepoint_marker
|
||||||
char *extra;
|
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
|
struct memrange
|
||||||
{
|
{
|
||||||
/* memrange_absolute for absolute memory range, else basereg
|
/* 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,
|
extern struct uploaded_tp *get_uploaded_tp (int num, ULONGEST addr,
|
||||||
struct uploaded_tp **utpp);
|
struct uploaded_tp **utpp);
|
||||||
|
extern void free_uploaded_tps (struct uploaded_tp **utpp);
|
||||||
|
|
||||||
extern struct uploaded_tsv *get_uploaded_tsv (int num,
|
extern struct uploaded_tsv *get_uploaded_tsv (int num,
|
||||||
struct uploaded_tsv **utsvp);
|
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 struct tracepoint *create_tracepoint_from_upload (struct uploaded_tp *utp);
|
||||||
extern void merge_uploaded_tracepoints (struct uploaded_tp **utpp);
|
extern void merge_uploaded_tracepoints (struct uploaded_tp **utpp);
|
||||||
extern void merge_uploaded_trace_state_variables (struct uploaded_tsv **utsvp);
|
extern void merge_uploaded_trace_state_variables (struct uploaded_tsv **utsvp);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue