Move tfile target to tracefile-tfile.c
This patch moves tfile target related code from tracepoint.c to tracefile-tfile.c. gdb: 2014-02-23 Yao Qi <yao@codesourcery.com> * tracepoint.c (TFILE_PID): Move it to tracefile-tfile.c. (O_LARGEFILE): Likewise. (tfile_ops): Likewise. (TRACE_HEADER_SIZE): Likewise. (trace_fd, trace_frames_offset, cur_offset): Likewise. (cur_data_size): Likewise. (tfile_read, tfile_open, tfile_interp_line): Likewise. (tfile_close, tfile_files_info): Likewise. (tfile_get_trace_status): Likewise. (tfile_get_tracepoint_status): Likewise. (tfile_get_traceframe_address): Likewise. (tfile_trace_find, match_blocktype): Likewise. (traceframe_walk_blocks, traceframe_find_block_type): Likewise. (tfile_fetch_registers, tfile_xfer_partial): Likewise. (tfile_get_trace_state_variable_value): Likewise. (tfile_has_all_memory, tfile_has_memory): Likewise. (tfile_has_stack, tfile_has_registers): Likewise. (tfile_thread_alive, build_traceframe_info): Likewise. (tfile_traceframe_info, init_tfile_ops): Likewise. (_initialize_tracepoint): Don't call init_tfile_ops and add_target_with_completer. * tracefile-tfile.c: Include regcache.h, inferior.h, gdbthread.h, exec.h, completer.h and filenames.h. (_initialize_tracefile_tfile): New function.
This commit is contained in:
parent
7951c4eb08
commit
1139532379
|
@ -1,3 +1,30 @@
|
|||
2014-02-23 Yao Qi <yao@codesourcery.com>
|
||||
|
||||
* tracepoint.c (TFILE_PID): Move it to tracefile-tfile.c.
|
||||
(O_LARGEFILE): Likewise.
|
||||
(tfile_ops): Likewise.
|
||||
(TRACE_HEADER_SIZE): Likewise.
|
||||
(trace_fd, trace_frames_offset, cur_offset): Likewise.
|
||||
(cur_data_size): Likewise.
|
||||
(tfile_read, tfile_open, tfile_interp_line): Likewise.
|
||||
(tfile_close, tfile_files_info): Likewise.
|
||||
(tfile_get_trace_status): Likewise.
|
||||
(tfile_get_tracepoint_status): Likewise.
|
||||
(tfile_get_traceframe_address): Likewise.
|
||||
(tfile_trace_find, match_blocktype): Likewise.
|
||||
(traceframe_walk_blocks, traceframe_find_block_type): Likewise.
|
||||
(tfile_fetch_registers, tfile_xfer_partial): Likewise.
|
||||
(tfile_get_trace_state_variable_value): Likewise.
|
||||
(tfile_has_all_memory, tfile_has_memory): Likewise.
|
||||
(tfile_has_stack, tfile_has_registers): Likewise.
|
||||
(tfile_thread_alive, build_traceframe_info): Likewise.
|
||||
(tfile_traceframe_info, init_tfile_ops): Likewise.
|
||||
(_initialize_tracepoint): Don't call init_tfile_ops
|
||||
and add_target_with_completer.
|
||||
* tracefile-tfile.c: Include regcache.h, inferior.h, gdbthread.h,
|
||||
exec.h, completer.h and filenames.h.
|
||||
(_initialize_tracefile_tfile): New function.
|
||||
|
||||
2014-02-23 Yao Qi <yao@codesourcery.com>
|
||||
|
||||
* Makefile.in (REMOTE_OBS): Append tracefile.o and
|
||||
|
|
|
@ -22,6 +22,16 @@
|
|||
#include "readline/tilde.h"
|
||||
#include "filestuff.h"
|
||||
#include "rsp-low.h" /* bin2hex */
|
||||
#include "regcache.h"
|
||||
#include "inferior.h"
|
||||
#include "gdbthread.h"
|
||||
#include "exec.h" /* exec_bfd */
|
||||
#include "completer.h"
|
||||
#include "filenames.h"
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
/* TFILE trace writer. */
|
||||
|
||||
|
@ -325,3 +335,820 @@ tfile_trace_file_writer_new (void)
|
|||
|
||||
return (struct trace_file_writer *) writer;
|
||||
}
|
||||
|
||||
/* target tfile command */
|
||||
|
||||
static struct target_ops tfile_ops;
|
||||
|
||||
/* Fill in tfile_ops with its defined operations and properties. */
|
||||
|
||||
#define TRACE_HEADER_SIZE 8
|
||||
|
||||
#define TFILE_PID (1)
|
||||
|
||||
static char *trace_filename;
|
||||
static int trace_fd = -1;
|
||||
static off_t trace_frames_offset;
|
||||
static off_t cur_offset;
|
||||
static int cur_data_size;
|
||||
int trace_regblock_size;
|
||||
|
||||
static void tfile_interp_line (char *line,
|
||||
struct uploaded_tp **utpp,
|
||||
struct uploaded_tsv **utsvp);
|
||||
|
||||
/* Read SIZE bytes into READBUF from the trace frame, starting at
|
||||
TRACE_FD's current position. Note that this call `read'
|
||||
underneath, hence it advances the file's seek position. Throws an
|
||||
error if the `read' syscall fails, or less than SIZE bytes are
|
||||
read. */
|
||||
|
||||
static void
|
||||
tfile_read (gdb_byte *readbuf, int size)
|
||||
{
|
||||
int gotten;
|
||||
|
||||
gotten = read (trace_fd, readbuf, size);
|
||||
if (gotten < 0)
|
||||
perror_with_name (trace_filename);
|
||||
else if (gotten < size)
|
||||
error (_("Premature end of file while reading trace file"));
|
||||
}
|
||||
|
||||
static void
|
||||
tfile_open (char *filename, int from_tty)
|
||||
{
|
||||
volatile struct gdb_exception ex;
|
||||
char *temp;
|
||||
struct cleanup *old_chain;
|
||||
int flags;
|
||||
int scratch_chan;
|
||||
char header[TRACE_HEADER_SIZE];
|
||||
char linebuf[1000]; /* Should be max remote packet size or so. */
|
||||
gdb_byte byte;
|
||||
int bytes, i;
|
||||
struct trace_status *ts;
|
||||
struct uploaded_tp *uploaded_tps = NULL;
|
||||
struct uploaded_tsv *uploaded_tsvs = NULL;
|
||||
|
||||
target_preopen (from_tty);
|
||||
if (!filename)
|
||||
error (_("No trace file specified."));
|
||||
|
||||
filename = tilde_expand (filename);
|
||||
if (!IS_ABSOLUTE_PATH(filename))
|
||||
{
|
||||
temp = concat (current_directory, "/", filename, (char *) NULL);
|
||||
xfree (filename);
|
||||
filename = temp;
|
||||
}
|
||||
|
||||
old_chain = make_cleanup (xfree, filename);
|
||||
|
||||
flags = O_BINARY | O_LARGEFILE;
|
||||
flags |= O_RDONLY;
|
||||
scratch_chan = gdb_open_cloexec (filename, flags, 0);
|
||||
if (scratch_chan < 0)
|
||||
perror_with_name (filename);
|
||||
|
||||
/* Looks semi-reasonable. Toss the old trace file and work on the new. */
|
||||
|
||||
discard_cleanups (old_chain); /* Don't free filename any more. */
|
||||
unpush_target (&tfile_ops);
|
||||
|
||||
trace_filename = xstrdup (filename);
|
||||
trace_fd = scratch_chan;
|
||||
|
||||
bytes = 0;
|
||||
/* Read the file header and test for validity. */
|
||||
tfile_read ((gdb_byte *) &header, TRACE_HEADER_SIZE);
|
||||
|
||||
bytes += TRACE_HEADER_SIZE;
|
||||
if (!(header[0] == 0x7f
|
||||
&& (strncmp (header + 1, "TRACE0\n", 7) == 0)))
|
||||
error (_("File is not a valid trace file."));
|
||||
|
||||
push_target (&tfile_ops);
|
||||
|
||||
trace_regblock_size = 0;
|
||||
ts = current_trace_status ();
|
||||
/* We know we're working with a file. Record its name. */
|
||||
ts->filename = trace_filename;
|
||||
/* Set defaults in case there is no status line. */
|
||||
ts->running_known = 0;
|
||||
ts->stop_reason = trace_stop_reason_unknown;
|
||||
ts->traceframe_count = -1;
|
||||
ts->buffer_free = 0;
|
||||
ts->disconnected_tracing = 0;
|
||||
ts->circular_buffer = 0;
|
||||
|
||||
TRY_CATCH (ex, RETURN_MASK_ALL)
|
||||
{
|
||||
/* Read through a section of newline-terminated lines that
|
||||
define things like tracepoints. */
|
||||
i = 0;
|
||||
while (1)
|
||||
{
|
||||
tfile_read (&byte, 1);
|
||||
|
||||
++bytes;
|
||||
if (byte == '\n')
|
||||
{
|
||||
/* Empty line marks end of the definition section. */
|
||||
if (i == 0)
|
||||
break;
|
||||
linebuf[i] = '\0';
|
||||
i = 0;
|
||||
tfile_interp_line (linebuf, &uploaded_tps, &uploaded_tsvs);
|
||||
}
|
||||
else
|
||||
linebuf[i++] = byte;
|
||||
if (i >= 1000)
|
||||
error (_("Excessively long lines in trace file"));
|
||||
}
|
||||
|
||||
/* Record the starting offset of the binary trace data. */
|
||||
trace_frames_offset = bytes;
|
||||
|
||||
/* If we don't have a blocksize, we can't interpret the
|
||||
traceframes. */
|
||||
if (trace_regblock_size == 0)
|
||||
error (_("No register block size recorded in trace file"));
|
||||
}
|
||||
if (ex.reason < 0)
|
||||
{
|
||||
/* Remove the partially set up target. */
|
||||
unpush_target (&tfile_ops);
|
||||
throw_exception (ex);
|
||||
}
|
||||
|
||||
inferior_appeared (current_inferior (), TFILE_PID);
|
||||
inferior_ptid = pid_to_ptid (TFILE_PID);
|
||||
add_thread_silent (inferior_ptid);
|
||||
|
||||
if (ts->traceframe_count <= 0)
|
||||
warning (_("No traceframes present in this file."));
|
||||
|
||||
/* Add the file's tracepoints and variables into the current mix. */
|
||||
|
||||
/* Get trace state variables first, they may be checked when parsing
|
||||
uploaded commands. */
|
||||
merge_uploaded_trace_state_variables (&uploaded_tsvs);
|
||||
|
||||
merge_uploaded_tracepoints (&uploaded_tps);
|
||||
|
||||
post_create_inferior (&tfile_ops, from_tty);
|
||||
}
|
||||
|
||||
/* Interpret the given line from the definitions part of the trace
|
||||
file. */
|
||||
|
||||
static void
|
||||
tfile_interp_line (char *line, struct uploaded_tp **utpp,
|
||||
struct uploaded_tsv **utsvp)
|
||||
{
|
||||
char *p = line;
|
||||
|
||||
if (strncmp (p, "R ", strlen ("R ")) == 0)
|
||||
{
|
||||
p += strlen ("R ");
|
||||
trace_regblock_size = strtol (p, &p, 16);
|
||||
}
|
||||
else if (strncmp (p, "status ", strlen ("status ")) == 0)
|
||||
{
|
||||
p += strlen ("status ");
|
||||
parse_trace_status (p, current_trace_status ());
|
||||
}
|
||||
else if (strncmp (p, "tp ", strlen ("tp ")) == 0)
|
||||
{
|
||||
p += strlen ("tp ");
|
||||
parse_tracepoint_definition (p, utpp);
|
||||
}
|
||||
else if (strncmp (p, "tsv ", strlen ("tsv ")) == 0)
|
||||
{
|
||||
p += strlen ("tsv ");
|
||||
parse_tsv_definition (p, utsvp);
|
||||
}
|
||||
else
|
||||
warning (_("Ignoring trace file definition \"%s\""), line);
|
||||
}
|
||||
|
||||
/* Close the trace file and generally clean up. */
|
||||
|
||||
static void
|
||||
tfile_close (struct target_ops *self)
|
||||
{
|
||||
int pid;
|
||||
|
||||
if (trace_fd < 0)
|
||||
return;
|
||||
|
||||
pid = ptid_get_pid (inferior_ptid);
|
||||
inferior_ptid = null_ptid; /* Avoid confusion from thread stuff. */
|
||||
exit_inferior_silent (pid);
|
||||
|
||||
close (trace_fd);
|
||||
trace_fd = -1;
|
||||
xfree (trace_filename);
|
||||
trace_filename = NULL;
|
||||
|
||||
trace_reset_local_state ();
|
||||
}
|
||||
|
||||
static void
|
||||
tfile_files_info (struct target_ops *t)
|
||||
{
|
||||
printf_filtered ("\t`%s'\n", trace_filename);
|
||||
}
|
||||
|
||||
/* The trace status for a file is that tracing can never be run. */
|
||||
|
||||
static int
|
||||
tfile_get_trace_status (struct target_ops *self, struct trace_status *ts)
|
||||
{
|
||||
/* Other bits of trace status were collected as part of opening the
|
||||
trace files, so nothing to do here. */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
tfile_get_tracepoint_status (struct target_ops *self,
|
||||
struct breakpoint *tp, struct uploaded_tp *utp)
|
||||
{
|
||||
/* Other bits of trace status were collected as part of opening the
|
||||
trace files, so nothing to do here. */
|
||||
}
|
||||
|
||||
/* Given the position of a traceframe in the file, figure out what
|
||||
address the frame was collected at. This would normally be the
|
||||
value of a collected PC register, but if not available, we
|
||||
improvise. */
|
||||
|
||||
static CORE_ADDR
|
||||
tfile_get_traceframe_address (off_t tframe_offset)
|
||||
{
|
||||
CORE_ADDR addr = 0;
|
||||
short tpnum;
|
||||
struct tracepoint *tp;
|
||||
off_t saved_offset = cur_offset;
|
||||
|
||||
/* FIXME dig pc out of collected registers. */
|
||||
|
||||
/* Fall back to using tracepoint address. */
|
||||
lseek (trace_fd, tframe_offset, SEEK_SET);
|
||||
tfile_read ((gdb_byte *) &tpnum, 2);
|
||||
tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
|
||||
tp = get_tracepoint_by_number_on_target (tpnum);
|
||||
/* FIXME this is a poor heuristic if multiple locations. */
|
||||
if (tp && tp->base.loc)
|
||||
addr = tp->base.loc->address;
|
||||
|
||||
/* Restore our seek position. */
|
||||
cur_offset = saved_offset;
|
||||
lseek (trace_fd, cur_offset, SEEK_SET);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Given a type of search and some parameters, scan the collection of
|
||||
traceframes in the file looking for a match. When found, return
|
||||
both the traceframe and tracepoint number, otherwise -1 for
|
||||
each. */
|
||||
|
||||
static int
|
||||
tfile_trace_find (struct target_ops *self, enum trace_find_type type, int num,
|
||||
CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
|
||||
{
|
||||
short tpnum;
|
||||
int tfnum = 0, found = 0;
|
||||
unsigned int data_size;
|
||||
struct tracepoint *tp;
|
||||
off_t offset, tframe_offset;
|
||||
CORE_ADDR tfaddr;
|
||||
|
||||
if (num == -1)
|
||||
{
|
||||
if (tpp)
|
||||
*tpp = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
lseek (trace_fd, trace_frames_offset, SEEK_SET);
|
||||
offset = trace_frames_offset;
|
||||
while (1)
|
||||
{
|
||||
tframe_offset = offset;
|
||||
tfile_read ((gdb_byte *) &tpnum, 2);
|
||||
tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
offset += 2;
|
||||
if (tpnum == 0)
|
||||
break;
|
||||
tfile_read ((gdb_byte *) &data_size, 4);
|
||||
data_size = (unsigned int) extract_unsigned_integer
|
||||
((gdb_byte *) &data_size, 4,
|
||||
gdbarch_byte_order (target_gdbarch ()));
|
||||
offset += 4;
|
||||
|
||||
if (type == tfind_number)
|
||||
{
|
||||
/* Looking for a specific trace frame. */
|
||||
if (tfnum == num)
|
||||
found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start from the _next_ trace frame. */
|
||||
if (tfnum > get_traceframe_number ())
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case tfind_pc:
|
||||
tfaddr = tfile_get_traceframe_address (tframe_offset);
|
||||
if (tfaddr == addr1)
|
||||
found = 1;
|
||||
break;
|
||||
case tfind_tp:
|
||||
tp = get_tracepoint (num);
|
||||
if (tp && tpnum == tp->number_on_target)
|
||||
found = 1;
|
||||
break;
|
||||
case tfind_range:
|
||||
tfaddr = tfile_get_traceframe_address (tframe_offset);
|
||||
if (addr1 <= tfaddr && tfaddr <= addr2)
|
||||
found = 1;
|
||||
break;
|
||||
case tfind_outside:
|
||||
tfaddr = tfile_get_traceframe_address (tframe_offset);
|
||||
if (!(addr1 <= tfaddr && tfaddr <= addr2))
|
||||
found = 1;
|
||||
break;
|
||||
default:
|
||||
internal_error (__FILE__, __LINE__, _("unknown tfind type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
if (tpp)
|
||||
*tpp = tpnum;
|
||||
cur_offset = offset;
|
||||
cur_data_size = data_size;
|
||||
|
||||
return tfnum;
|
||||
}
|
||||
/* Skip past the traceframe's data. */
|
||||
lseek (trace_fd, data_size, SEEK_CUR);
|
||||
offset += data_size;
|
||||
/* Update our own count of traceframes. */
|
||||
++tfnum;
|
||||
}
|
||||
/* Did not find what we were looking for. */
|
||||
if (tpp)
|
||||
*tpp = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prototype of the callback passed to tframe_walk_blocks. */
|
||||
typedef int (*walk_blocks_callback_func) (char blocktype, void *data);
|
||||
|
||||
/* Callback for traceframe_walk_blocks, used to find a given block
|
||||
type in a traceframe. */
|
||||
|
||||
static int
|
||||
match_blocktype (char blocktype, void *data)
|
||||
{
|
||||
char *wantedp = data;
|
||||
|
||||
if (*wantedp == blocktype)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over all traceframe block starting at POS offset from
|
||||
CUR_OFFSET, and call CALLBACK for each block found, passing in DATA
|
||||
unmodified. If CALLBACK returns true, this returns the position in
|
||||
the traceframe where the block is found, relative to the start of
|
||||
the traceframe (cur_offset). Returns -1 if no callback call
|
||||
returned true, indicating that all blocks have been walked. */
|
||||
|
||||
static int
|
||||
traceframe_walk_blocks (walk_blocks_callback_func callback,
|
||||
int pos, void *data)
|
||||
{
|
||||
/* Iterate through a traceframe's blocks, looking for a block of the
|
||||
requested type. */
|
||||
|
||||
lseek (trace_fd, cur_offset + pos, SEEK_SET);
|
||||
while (pos < cur_data_size)
|
||||
{
|
||||
unsigned short mlen;
|
||||
char block_type;
|
||||
|
||||
tfile_read ((gdb_byte *) &block_type, 1);
|
||||
|
||||
++pos;
|
||||
|
||||
if ((*callback) (block_type, data))
|
||||
return pos;
|
||||
|
||||
switch (block_type)
|
||||
{
|
||||
case 'R':
|
||||
lseek (trace_fd, cur_offset + pos + trace_regblock_size, SEEK_SET);
|
||||
pos += trace_regblock_size;
|
||||
break;
|
||||
case 'M':
|
||||
lseek (trace_fd, cur_offset + pos + 8, SEEK_SET);
|
||||
tfile_read ((gdb_byte *) &mlen, 2);
|
||||
mlen = (unsigned short)
|
||||
extract_unsigned_integer ((gdb_byte *) &mlen, 2,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
lseek (trace_fd, mlen, SEEK_CUR);
|
||||
pos += (8 + 2 + mlen);
|
||||
break;
|
||||
case 'V':
|
||||
lseek (trace_fd, cur_offset + pos + 4 + 8, SEEK_SET);
|
||||
pos += (4 + 8);
|
||||
break;
|
||||
default:
|
||||
error (_("Unknown block type '%c' (0x%x) in trace frame"),
|
||||
block_type, block_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Convenience wrapper around traceframe_walk_blocks. Looks for the
|
||||
position offset of a block of type TYPE_WANTED in the current trace
|
||||
frame, starting at POS. Returns -1 if no such block was found. */
|
||||
|
||||
static int
|
||||
traceframe_find_block_type (char type_wanted, int pos)
|
||||
{
|
||||
return traceframe_walk_blocks (match_blocktype, pos, &type_wanted);
|
||||
}
|
||||
|
||||
/* Look for a block of saved registers in the traceframe, and get the
|
||||
requested register from it. */
|
||||
|
||||
static void
|
||||
tfile_fetch_registers (struct target_ops *ops,
|
||||
struct regcache *regcache, int regno)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||
int offset, regn, regsize, pc_regno;
|
||||
gdb_byte *regs;
|
||||
|
||||
/* An uninitialized reg size says we're not going to be
|
||||
successful at getting register blocks. */
|
||||
if (!trace_regblock_size)
|
||||
return;
|
||||
|
||||
regs = alloca (trace_regblock_size);
|
||||
|
||||
if (traceframe_find_block_type ('R', 0) >= 0)
|
||||
{
|
||||
tfile_read (regs, trace_regblock_size);
|
||||
|
||||
/* Assume the block is laid out in GDB register number order,
|
||||
each register with the size that it has in GDB. */
|
||||
offset = 0;
|
||||
for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
|
||||
{
|
||||
regsize = register_size (gdbarch, regn);
|
||||
/* Make sure we stay within block bounds. */
|
||||
if (offset + regsize >= trace_regblock_size)
|
||||
break;
|
||||
if (regcache_register_status (regcache, regn) == REG_UNKNOWN)
|
||||
{
|
||||
if (regno == regn)
|
||||
{
|
||||
regcache_raw_supply (regcache, regno, regs + offset);
|
||||
break;
|
||||
}
|
||||
else if (regno == -1)
|
||||
{
|
||||
regcache_raw_supply (regcache, regn, regs + offset);
|
||||
}
|
||||
}
|
||||
offset += regsize;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* We get here if no register data has been found. Mark registers
|
||||
as unavailable. */
|
||||
for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
|
||||
regcache_raw_supply (regcache, regn, NULL);
|
||||
|
||||
/* We can often usefully guess that the PC is going to be the same
|
||||
as the address of the tracepoint. */
|
||||
pc_regno = gdbarch_pc_regnum (gdbarch);
|
||||
|
||||
/* XXX This guessing code below only works if the PC register isn't
|
||||
a pseudo-register. The value of a pseudo-register isn't stored
|
||||
in the (non-readonly) regcache -- instead it's recomputed
|
||||
(probably from some other cached raw register) whenever the
|
||||
register is read. This guesswork should probably move to some
|
||||
higher layer. */
|
||||
if (pc_regno < 0 || pc_regno >= gdbarch_num_regs (gdbarch))
|
||||
return;
|
||||
|
||||
if (regno == -1 || regno == pc_regno)
|
||||
{
|
||||
struct tracepoint *tp = get_tracepoint (get_tracepoint_number ());
|
||||
|
||||
if (tp && tp->base.loc)
|
||||
{
|
||||
/* But don't try to guess if tracepoint is multi-location... */
|
||||
if (tp->base.loc->next)
|
||||
{
|
||||
warning (_("Tracepoint %d has multiple "
|
||||
"locations, cannot infer $pc"),
|
||||
tp->base.number);
|
||||
return;
|
||||
}
|
||||
/* ... or does while-stepping. */
|
||||
if (tp->step_count > 0)
|
||||
{
|
||||
warning (_("Tracepoint %d does while-stepping, "
|
||||
"cannot infer $pc"),
|
||||
tp->base.number);
|
||||
return;
|
||||
}
|
||||
|
||||
store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
|
||||
gdbarch_byte_order (gdbarch),
|
||||
tp->base.loc->address);
|
||||
regcache_raw_supply (regcache, pc_regno, regs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum target_xfer_status
|
||||
tfile_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
const char *annex, gdb_byte *readbuf,
|
||||
const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
|
||||
ULONGEST *xfered_len)
|
||||
{
|
||||
/* We're only doing regular memory for now. */
|
||||
if (object != TARGET_OBJECT_MEMORY)
|
||||
return TARGET_XFER_E_IO;
|
||||
|
||||
if (readbuf == NULL)
|
||||
error (_("tfile_xfer_partial: trace file is read-only"));
|
||||
|
||||
if (get_traceframe_number () != -1)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
/* Iterate through the traceframe's blocks, looking for
|
||||
memory. */
|
||||
while ((pos = traceframe_find_block_type ('M', pos)) >= 0)
|
||||
{
|
||||
ULONGEST maddr, amt;
|
||||
unsigned short mlen;
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
|
||||
|
||||
tfile_read ((gdb_byte *) &maddr, 8);
|
||||
maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
|
||||
byte_order);
|
||||
tfile_read ((gdb_byte *) &mlen, 2);
|
||||
mlen = (unsigned short)
|
||||
extract_unsigned_integer ((gdb_byte *) &mlen, 2, byte_order);
|
||||
|
||||
/* If the block includes the first part of the desired
|
||||
range, return as much it has; GDB will re-request the
|
||||
remainder, which might be in a different block of this
|
||||
trace frame. */
|
||||
if (maddr <= offset && offset < (maddr + mlen))
|
||||
{
|
||||
amt = (maddr + mlen) - offset;
|
||||
if (amt > len)
|
||||
amt = len;
|
||||
|
||||
if (maddr != offset)
|
||||
lseek (trace_fd, offset - maddr, SEEK_CUR);
|
||||
tfile_read (readbuf, amt);
|
||||
*xfered_len = amt;
|
||||
return TARGET_XFER_OK;
|
||||
}
|
||||
|
||||
/* Skip over this block. */
|
||||
pos += (8 + 2 + mlen);
|
||||
}
|
||||
}
|
||||
|
||||
/* It's unduly pedantic to refuse to look at the executable for
|
||||
read-only pieces; so do the equivalent of readonly regions aka
|
||||
QTro packet. */
|
||||
/* FIXME account for relocation at some point. */
|
||||
if (exec_bfd)
|
||||
{
|
||||
asection *s;
|
||||
bfd_size_type size;
|
||||
bfd_vma vma;
|
||||
|
||||
for (s = exec_bfd->sections; s; s = s->next)
|
||||
{
|
||||
if ((s->flags & SEC_LOAD) == 0
|
||||
|| (s->flags & SEC_READONLY) == 0)
|
||||
continue;
|
||||
|
||||
vma = s->vma;
|
||||
size = bfd_get_section_size (s);
|
||||
if (vma <= offset && offset < (vma + size))
|
||||
{
|
||||
ULONGEST amt;
|
||||
|
||||
amt = (vma + size) - offset;
|
||||
if (amt > len)
|
||||
amt = len;
|
||||
|
||||
*xfered_len = bfd_get_section_contents (exec_bfd, s,
|
||||
readbuf, offset - vma, amt);
|
||||
return TARGET_XFER_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Indicate failure to find the requested memory block. */
|
||||
return TARGET_XFER_E_IO;
|
||||
}
|
||||
|
||||
/* Iterate through the blocks of a trace frame, looking for a 'V'
|
||||
block with a matching tsv number. */
|
||||
|
||||
static int
|
||||
tfile_get_trace_state_variable_value (struct target_ops *self,
|
||||
int tsvnum, LONGEST *val)
|
||||
{
|
||||
int pos;
|
||||
int found = 0;
|
||||
|
||||
/* Iterate over blocks in current frame and find the last 'V'
|
||||
block in which tsv number is TSVNUM. In one trace frame, there
|
||||
may be multiple 'V' blocks created for a given trace variable,
|
||||
and the last matched 'V' block contains the updated value. */
|
||||
pos = 0;
|
||||
while ((pos = traceframe_find_block_type ('V', pos)) >= 0)
|
||||
{
|
||||
int vnum;
|
||||
|
||||
tfile_read ((gdb_byte *) &vnum, 4);
|
||||
vnum = (int) extract_signed_integer ((gdb_byte *) &vnum, 4,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
if (tsvnum == vnum)
|
||||
{
|
||||
tfile_read ((gdb_byte *) val, 8);
|
||||
*val = extract_signed_integer ((gdb_byte *) val, 8,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
found = 1;
|
||||
}
|
||||
pos += (4 + 8);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_all_memory (struct target_ops *ops)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_memory (struct target_ops *ops)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_stack (struct target_ops *ops)
|
||||
{
|
||||
return get_traceframe_number () != -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_registers (struct target_ops *ops)
|
||||
{
|
||||
return get_traceframe_number () != -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_thread_alive (struct target_ops *ops, ptid_t ptid)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Callback for traceframe_walk_blocks. Builds a traceframe_info
|
||||
object for the tfile target's current traceframe. */
|
||||
|
||||
static int
|
||||
build_traceframe_info (char blocktype, void *data)
|
||||
{
|
||||
struct traceframe_info *info = data;
|
||||
|
||||
switch (blocktype)
|
||||
{
|
||||
case 'M':
|
||||
{
|
||||
struct mem_range *r;
|
||||
ULONGEST maddr;
|
||||
unsigned short mlen;
|
||||
|
||||
tfile_read ((gdb_byte *) &maddr, 8);
|
||||
maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
tfile_read ((gdb_byte *) &mlen, 2);
|
||||
mlen = (unsigned short)
|
||||
extract_unsigned_integer ((gdb_byte *) &mlen,
|
||||
2, gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
|
||||
r = VEC_safe_push (mem_range_s, info->memory, NULL);
|
||||
|
||||
r->start = maddr;
|
||||
r->length = mlen;
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
{
|
||||
int vnum;
|
||||
|
||||
tfile_read ((gdb_byte *) &vnum, 4);
|
||||
VEC_safe_push (int, info->tvars, vnum);
|
||||
}
|
||||
case 'R':
|
||||
case 'S':
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
warning (_("Unhandled trace block type (%d) '%c ' "
|
||||
"while building trace frame info."),
|
||||
blocktype, blocktype);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct traceframe_info *
|
||||
tfile_traceframe_info (struct target_ops *self)
|
||||
{
|
||||
struct traceframe_info *info = XCNEW (struct traceframe_info);
|
||||
|
||||
traceframe_walk_blocks (build_traceframe_info, 0, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
init_tfile_ops (void)
|
||||
{
|
||||
tfile_ops.to_shortname = "tfile";
|
||||
tfile_ops.to_longname = "Local trace dump file";
|
||||
tfile_ops.to_doc
|
||||
= "Use a trace file as a target. Specify the filename of the trace file.";
|
||||
tfile_ops.to_open = tfile_open;
|
||||
tfile_ops.to_close = tfile_close;
|
||||
tfile_ops.to_fetch_registers = tfile_fetch_registers;
|
||||
tfile_ops.to_xfer_partial = tfile_xfer_partial;
|
||||
tfile_ops.to_files_info = tfile_files_info;
|
||||
tfile_ops.to_get_trace_status = tfile_get_trace_status;
|
||||
tfile_ops.to_get_tracepoint_status = tfile_get_tracepoint_status;
|
||||
tfile_ops.to_trace_find = tfile_trace_find;
|
||||
tfile_ops.to_get_trace_state_variable_value
|
||||
= tfile_get_trace_state_variable_value;
|
||||
tfile_ops.to_stratum = process_stratum;
|
||||
tfile_ops.to_has_all_memory = tfile_has_all_memory;
|
||||
tfile_ops.to_has_memory = tfile_has_memory;
|
||||
tfile_ops.to_has_stack = tfile_has_stack;
|
||||
tfile_ops.to_has_registers = tfile_has_registers;
|
||||
tfile_ops.to_traceframe_info = tfile_traceframe_info;
|
||||
tfile_ops.to_thread_alive = tfile_thread_alive;
|
||||
tfile_ops.to_magic = OPS_MAGIC;
|
||||
}
|
||||
|
||||
extern initialize_file_ftype _initialize_tracefile_tfile;
|
||||
|
||||
void
|
||||
_initialize_tracefile_tfile (void)
|
||||
{
|
||||
init_tfile_ops ();
|
||||
|
||||
add_target_with_completer (&tfile_ops, filename_completer);
|
||||
}
|
||||
|
|
815
gdb/tracepoint.c
815
gdb/tracepoint.c
|
@ -67,10 +67,6 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef O_LARGEFILE
|
||||
#define O_LARGEFILE 0
|
||||
#endif
|
||||
|
||||
/* Maximum length of an agent aexpression.
|
||||
This accounts for the fact that packets are limited to 400 bytes
|
||||
(which includes everything -- including the checksum), and assumes
|
||||
|
@ -81,8 +77,6 @@
|
|||
large. (400 - 31)/2 == 184 */
|
||||
#define MAX_AGENT_EXPR_LEN 184
|
||||
|
||||
#define TFILE_PID (1)
|
||||
|
||||
/* A hook used to notify the UI of tracepoint operations. */
|
||||
|
||||
void (*deprecated_trace_find_hook) (char *arg, int from_tty);
|
||||
|
@ -3571,201 +3565,6 @@ merge_uploaded_trace_state_variables (struct uploaded_tsv **uploaded_tsvs)
|
|||
free_uploaded_tsvs (uploaded_tsvs);
|
||||
}
|
||||
|
||||
/* target tfile command */
|
||||
|
||||
static struct target_ops tfile_ops;
|
||||
|
||||
/* Fill in tfile_ops with its defined operations and properties. */
|
||||
|
||||
#define TRACE_HEADER_SIZE 8
|
||||
|
||||
static char *trace_filename;
|
||||
static int trace_fd = -1;
|
||||
static off_t trace_frames_offset;
|
||||
static off_t cur_offset;
|
||||
static int cur_data_size;
|
||||
int trace_regblock_size;
|
||||
|
||||
static void tfile_interp_line (char *line,
|
||||
struct uploaded_tp **utpp,
|
||||
struct uploaded_tsv **utsvp);
|
||||
|
||||
/* Read SIZE bytes into READBUF from the trace frame, starting at
|
||||
TRACE_FD's current position. Note that this call `read'
|
||||
underneath, hence it advances the file's seek position. Throws an
|
||||
error if the `read' syscall fails, or less than SIZE bytes are
|
||||
read. */
|
||||
|
||||
static void
|
||||
tfile_read (gdb_byte *readbuf, int size)
|
||||
{
|
||||
int gotten;
|
||||
|
||||
gotten = read (trace_fd, readbuf, size);
|
||||
if (gotten < 0)
|
||||
perror_with_name (trace_filename);
|
||||
else if (gotten < size)
|
||||
error (_("Premature end of file while reading trace file"));
|
||||
}
|
||||
|
||||
static void
|
||||
tfile_open (char *filename, int from_tty)
|
||||
{
|
||||
volatile struct gdb_exception ex;
|
||||
char *temp;
|
||||
struct cleanup *old_chain;
|
||||
int flags;
|
||||
int scratch_chan;
|
||||
char header[TRACE_HEADER_SIZE];
|
||||
char linebuf[1000]; /* Should be max remote packet size or so. */
|
||||
gdb_byte byte;
|
||||
int bytes, i;
|
||||
struct trace_status *ts;
|
||||
struct uploaded_tp *uploaded_tps = NULL;
|
||||
struct uploaded_tsv *uploaded_tsvs = NULL;
|
||||
|
||||
target_preopen (from_tty);
|
||||
if (!filename)
|
||||
error (_("No trace file specified."));
|
||||
|
||||
filename = tilde_expand (filename);
|
||||
if (!IS_ABSOLUTE_PATH(filename))
|
||||
{
|
||||
temp = concat (current_directory, "/", filename, (char *) NULL);
|
||||
xfree (filename);
|
||||
filename = temp;
|
||||
}
|
||||
|
||||
old_chain = make_cleanup (xfree, filename);
|
||||
|
||||
flags = O_BINARY | O_LARGEFILE;
|
||||
flags |= O_RDONLY;
|
||||
scratch_chan = gdb_open_cloexec (filename, flags, 0);
|
||||
if (scratch_chan < 0)
|
||||
perror_with_name (filename);
|
||||
|
||||
/* Looks semi-reasonable. Toss the old trace file and work on the new. */
|
||||
|
||||
discard_cleanups (old_chain); /* Don't free filename any more. */
|
||||
unpush_target (&tfile_ops);
|
||||
|
||||
trace_filename = xstrdup (filename);
|
||||
trace_fd = scratch_chan;
|
||||
|
||||
bytes = 0;
|
||||
/* Read the file header and test for validity. */
|
||||
tfile_read ((gdb_byte *) &header, TRACE_HEADER_SIZE);
|
||||
|
||||
bytes += TRACE_HEADER_SIZE;
|
||||
if (!(header[0] == 0x7f
|
||||
&& (strncmp (header + 1, "TRACE0\n", 7) == 0)))
|
||||
error (_("File is not a valid trace file."));
|
||||
|
||||
push_target (&tfile_ops);
|
||||
|
||||
trace_regblock_size = 0;
|
||||
ts = current_trace_status ();
|
||||
/* We know we're working with a file. Record its name. */
|
||||
ts->filename = trace_filename;
|
||||
/* Set defaults in case there is no status line. */
|
||||
ts->running_known = 0;
|
||||
ts->stop_reason = trace_stop_reason_unknown;
|
||||
ts->traceframe_count = -1;
|
||||
ts->buffer_free = 0;
|
||||
ts->disconnected_tracing = 0;
|
||||
ts->circular_buffer = 0;
|
||||
|
||||
TRY_CATCH (ex, RETURN_MASK_ALL)
|
||||
{
|
||||
/* Read through a section of newline-terminated lines that
|
||||
define things like tracepoints. */
|
||||
i = 0;
|
||||
while (1)
|
||||
{
|
||||
tfile_read (&byte, 1);
|
||||
|
||||
++bytes;
|
||||
if (byte == '\n')
|
||||
{
|
||||
/* Empty line marks end of the definition section. */
|
||||
if (i == 0)
|
||||
break;
|
||||
linebuf[i] = '\0';
|
||||
i = 0;
|
||||
tfile_interp_line (linebuf, &uploaded_tps, &uploaded_tsvs);
|
||||
}
|
||||
else
|
||||
linebuf[i++] = byte;
|
||||
if (i >= 1000)
|
||||
error (_("Excessively long lines in trace file"));
|
||||
}
|
||||
|
||||
/* Record the starting offset of the binary trace data. */
|
||||
trace_frames_offset = bytes;
|
||||
|
||||
/* If we don't have a blocksize, we can't interpret the
|
||||
traceframes. */
|
||||
if (trace_regblock_size == 0)
|
||||
error (_("No register block size recorded in trace file"));
|
||||
}
|
||||
if (ex.reason < 0)
|
||||
{
|
||||
/* Remove the partially set up target. */
|
||||
unpush_target (&tfile_ops);
|
||||
throw_exception (ex);
|
||||
}
|
||||
|
||||
inferior_appeared (current_inferior (), TFILE_PID);
|
||||
inferior_ptid = pid_to_ptid (TFILE_PID);
|
||||
add_thread_silent (inferior_ptid);
|
||||
|
||||
if (ts->traceframe_count <= 0)
|
||||
warning (_("No traceframes present in this file."));
|
||||
|
||||
/* Add the file's tracepoints and variables into the current mix. */
|
||||
|
||||
/* Get trace state variables first, they may be checked when parsing
|
||||
uploaded commands. */
|
||||
merge_uploaded_trace_state_variables (&uploaded_tsvs);
|
||||
|
||||
merge_uploaded_tracepoints (&uploaded_tps);
|
||||
|
||||
post_create_inferior (&tfile_ops, from_tty);
|
||||
}
|
||||
|
||||
/* Interpret the given line from the definitions part of the trace
|
||||
file. */
|
||||
|
||||
static void
|
||||
tfile_interp_line (char *line, struct uploaded_tp **utpp,
|
||||
struct uploaded_tsv **utsvp)
|
||||
{
|
||||
char *p = line;
|
||||
|
||||
if (strncmp (p, "R ", strlen ("R ")) == 0)
|
||||
{
|
||||
p += strlen ("R ");
|
||||
trace_regblock_size = strtol (p, &p, 16);
|
||||
}
|
||||
else if (strncmp (p, "status ", strlen ("status ")) == 0)
|
||||
{
|
||||
p += strlen ("status ");
|
||||
parse_trace_status (p, current_trace_status ());
|
||||
}
|
||||
else if (strncmp (p, "tp ", strlen ("tp ")) == 0)
|
||||
{
|
||||
p += strlen ("tp ");
|
||||
parse_tracepoint_definition (p, utpp);
|
||||
}
|
||||
else if (strncmp (p, "tsv ", strlen ("tsv ")) == 0)
|
||||
{
|
||||
p += strlen ("tsv ");
|
||||
parse_tsv_definition (p, utsvp);
|
||||
}
|
||||
else
|
||||
warning (_("Ignoring trace file definition \"%s\""), line);
|
||||
}
|
||||
|
||||
/* Parse the part of trace status syntax that is shared between
|
||||
the remote protocol and the trace file reader. */
|
||||
|
||||
|
@ -4093,616 +3892,6 @@ parse_tsv_definition (char *line, struct uploaded_tsv **utsvp)
|
|||
utsv->name = xstrdup (buf);
|
||||
}
|
||||
|
||||
/* Close the trace file and generally clean up. */
|
||||
|
||||
static void
|
||||
tfile_close (struct target_ops *self)
|
||||
{
|
||||
int pid;
|
||||
|
||||
if (trace_fd < 0)
|
||||
return;
|
||||
|
||||
pid = ptid_get_pid (inferior_ptid);
|
||||
inferior_ptid = null_ptid; /* Avoid confusion from thread stuff. */
|
||||
exit_inferior_silent (pid);
|
||||
|
||||
close (trace_fd);
|
||||
trace_fd = -1;
|
||||
xfree (trace_filename);
|
||||
trace_filename = NULL;
|
||||
|
||||
trace_reset_local_state ();
|
||||
}
|
||||
|
||||
static void
|
||||
tfile_files_info (struct target_ops *t)
|
||||
{
|
||||
printf_filtered ("\t`%s'\n", trace_filename);
|
||||
}
|
||||
|
||||
/* The trace status for a file is that tracing can never be run. */
|
||||
|
||||
static int
|
||||
tfile_get_trace_status (struct target_ops *self, struct trace_status *ts)
|
||||
{
|
||||
/* Other bits of trace status were collected as part of opening the
|
||||
trace files, so nothing to do here. */
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
tfile_get_tracepoint_status (struct target_ops *self,
|
||||
struct breakpoint *tp, struct uploaded_tp *utp)
|
||||
{
|
||||
/* Other bits of trace status were collected as part of opening the
|
||||
trace files, so nothing to do here. */
|
||||
}
|
||||
|
||||
/* Given the position of a traceframe in the file, figure out what
|
||||
address the frame was collected at. This would normally be the
|
||||
value of a collected PC register, but if not available, we
|
||||
improvise. */
|
||||
|
||||
static CORE_ADDR
|
||||
tfile_get_traceframe_address (off_t tframe_offset)
|
||||
{
|
||||
CORE_ADDR addr = 0;
|
||||
short tpnum;
|
||||
struct tracepoint *tp;
|
||||
off_t saved_offset = cur_offset;
|
||||
|
||||
/* FIXME dig pc out of collected registers. */
|
||||
|
||||
/* Fall back to using tracepoint address. */
|
||||
lseek (trace_fd, tframe_offset, SEEK_SET);
|
||||
tfile_read ((gdb_byte *) &tpnum, 2);
|
||||
tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
|
||||
tp = get_tracepoint_by_number_on_target (tpnum);
|
||||
/* FIXME this is a poor heuristic if multiple locations. */
|
||||
if (tp && tp->base.loc)
|
||||
addr = tp->base.loc->address;
|
||||
|
||||
/* Restore our seek position. */
|
||||
cur_offset = saved_offset;
|
||||
lseek (trace_fd, cur_offset, SEEK_SET);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Given a type of search and some parameters, scan the collection of
|
||||
traceframes in the file looking for a match. When found, return
|
||||
both the traceframe and tracepoint number, otherwise -1 for
|
||||
each. */
|
||||
|
||||
static int
|
||||
tfile_trace_find (struct target_ops *self, enum trace_find_type type, int num,
|
||||
CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
|
||||
{
|
||||
short tpnum;
|
||||
int tfnum = 0, found = 0;
|
||||
unsigned int data_size;
|
||||
struct tracepoint *tp;
|
||||
off_t offset, tframe_offset;
|
||||
CORE_ADDR tfaddr;
|
||||
|
||||
if (num == -1)
|
||||
{
|
||||
if (tpp)
|
||||
*tpp = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
lseek (trace_fd, trace_frames_offset, SEEK_SET);
|
||||
offset = trace_frames_offset;
|
||||
while (1)
|
||||
{
|
||||
tframe_offset = offset;
|
||||
tfile_read ((gdb_byte *) &tpnum, 2);
|
||||
tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
offset += 2;
|
||||
if (tpnum == 0)
|
||||
break;
|
||||
tfile_read ((gdb_byte *) &data_size, 4);
|
||||
data_size = (unsigned int) extract_unsigned_integer
|
||||
((gdb_byte *) &data_size, 4,
|
||||
gdbarch_byte_order (target_gdbarch ()));
|
||||
offset += 4;
|
||||
|
||||
if (type == tfind_number)
|
||||
{
|
||||
/* Looking for a specific trace frame. */
|
||||
if (tfnum == num)
|
||||
found = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start from the _next_ trace frame. */
|
||||
if (tfnum > traceframe_number)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case tfind_pc:
|
||||
tfaddr = tfile_get_traceframe_address (tframe_offset);
|
||||
if (tfaddr == addr1)
|
||||
found = 1;
|
||||
break;
|
||||
case tfind_tp:
|
||||
tp = get_tracepoint (num);
|
||||
if (tp && tpnum == tp->number_on_target)
|
||||
found = 1;
|
||||
break;
|
||||
case tfind_range:
|
||||
tfaddr = tfile_get_traceframe_address (tframe_offset);
|
||||
if (addr1 <= tfaddr && tfaddr <= addr2)
|
||||
found = 1;
|
||||
break;
|
||||
case tfind_outside:
|
||||
tfaddr = tfile_get_traceframe_address (tframe_offset);
|
||||
if (!(addr1 <= tfaddr && tfaddr <= addr2))
|
||||
found = 1;
|
||||
break;
|
||||
default:
|
||||
internal_error (__FILE__, __LINE__, _("unknown tfind type"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
if (tpp)
|
||||
*tpp = tpnum;
|
||||
cur_offset = offset;
|
||||
cur_data_size = data_size;
|
||||
|
||||
return tfnum;
|
||||
}
|
||||
/* Skip past the traceframe's data. */
|
||||
lseek (trace_fd, data_size, SEEK_CUR);
|
||||
offset += data_size;
|
||||
/* Update our own count of traceframes. */
|
||||
++tfnum;
|
||||
}
|
||||
/* Did not find what we were looking for. */
|
||||
if (tpp)
|
||||
*tpp = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Prototype of the callback passed to tframe_walk_blocks. */
|
||||
typedef int (*walk_blocks_callback_func) (char blocktype, void *data);
|
||||
|
||||
/* Callback for traceframe_walk_blocks, used to find a given block
|
||||
type in a traceframe. */
|
||||
|
||||
static int
|
||||
match_blocktype (char blocktype, void *data)
|
||||
{
|
||||
char *wantedp = data;
|
||||
|
||||
if (*wantedp == blocktype)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Walk over all traceframe block starting at POS offset from
|
||||
CUR_OFFSET, and call CALLBACK for each block found, passing in DATA
|
||||
unmodified. If CALLBACK returns true, this returns the position in
|
||||
the traceframe where the block is found, relative to the start of
|
||||
the traceframe (cur_offset). Returns -1 if no callback call
|
||||
returned true, indicating that all blocks have been walked. */
|
||||
|
||||
static int
|
||||
traceframe_walk_blocks (walk_blocks_callback_func callback,
|
||||
int pos, void *data)
|
||||
{
|
||||
/* Iterate through a traceframe's blocks, looking for a block of the
|
||||
requested type. */
|
||||
|
||||
lseek (trace_fd, cur_offset + pos, SEEK_SET);
|
||||
while (pos < cur_data_size)
|
||||
{
|
||||
unsigned short mlen;
|
||||
char block_type;
|
||||
|
||||
tfile_read ((gdb_byte *) &block_type, 1);
|
||||
|
||||
++pos;
|
||||
|
||||
if ((*callback) (block_type, data))
|
||||
return pos;
|
||||
|
||||
switch (block_type)
|
||||
{
|
||||
case 'R':
|
||||
lseek (trace_fd, cur_offset + pos + trace_regblock_size, SEEK_SET);
|
||||
pos += trace_regblock_size;
|
||||
break;
|
||||
case 'M':
|
||||
lseek (trace_fd, cur_offset + pos + 8, SEEK_SET);
|
||||
tfile_read ((gdb_byte *) &mlen, 2);
|
||||
mlen = (unsigned short)
|
||||
extract_unsigned_integer ((gdb_byte *) &mlen, 2,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
lseek (trace_fd, mlen, SEEK_CUR);
|
||||
pos += (8 + 2 + mlen);
|
||||
break;
|
||||
case 'V':
|
||||
lseek (trace_fd, cur_offset + pos + 4 + 8, SEEK_SET);
|
||||
pos += (4 + 8);
|
||||
break;
|
||||
default:
|
||||
error (_("Unknown block type '%c' (0x%x) in trace frame"),
|
||||
block_type, block_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Convenience wrapper around traceframe_walk_blocks. Looks for the
|
||||
position offset of a block of type TYPE_WANTED in the current trace
|
||||
frame, starting at POS. Returns -1 if no such block was found. */
|
||||
|
||||
static int
|
||||
traceframe_find_block_type (char type_wanted, int pos)
|
||||
{
|
||||
return traceframe_walk_blocks (match_blocktype, pos, &type_wanted);
|
||||
}
|
||||
|
||||
/* Look for a block of saved registers in the traceframe, and get the
|
||||
requested register from it. */
|
||||
|
||||
static void
|
||||
tfile_fetch_registers (struct target_ops *ops,
|
||||
struct regcache *regcache, int regno)
|
||||
{
|
||||
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
||||
int offset, regn, regsize, pc_regno;
|
||||
gdb_byte *regs;
|
||||
|
||||
/* An uninitialized reg size says we're not going to be
|
||||
successful at getting register blocks. */
|
||||
if (!trace_regblock_size)
|
||||
return;
|
||||
|
||||
regs = alloca (trace_regblock_size);
|
||||
|
||||
if (traceframe_find_block_type ('R', 0) >= 0)
|
||||
{
|
||||
tfile_read (regs, trace_regblock_size);
|
||||
|
||||
/* Assume the block is laid out in GDB register number order,
|
||||
each register with the size that it has in GDB. */
|
||||
offset = 0;
|
||||
for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
|
||||
{
|
||||
regsize = register_size (gdbarch, regn);
|
||||
/* Make sure we stay within block bounds. */
|
||||
if (offset + regsize >= trace_regblock_size)
|
||||
break;
|
||||
if (regcache_register_status (regcache, regn) == REG_UNKNOWN)
|
||||
{
|
||||
if (regno == regn)
|
||||
{
|
||||
regcache_raw_supply (regcache, regno, regs + offset);
|
||||
break;
|
||||
}
|
||||
else if (regno == -1)
|
||||
{
|
||||
regcache_raw_supply (regcache, regn, regs + offset);
|
||||
}
|
||||
}
|
||||
offset += regsize;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* We get here if no register data has been found. Mark registers
|
||||
as unavailable. */
|
||||
for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
|
||||
regcache_raw_supply (regcache, regn, NULL);
|
||||
|
||||
/* We can often usefully guess that the PC is going to be the same
|
||||
as the address of the tracepoint. */
|
||||
pc_regno = gdbarch_pc_regnum (gdbarch);
|
||||
|
||||
/* XXX This guessing code below only works if the PC register isn't
|
||||
a pseudo-register. The value of a pseudo-register isn't stored
|
||||
in the (non-readonly) regcache -- instead it's recomputed
|
||||
(probably from some other cached raw register) whenever the
|
||||
register is read. This guesswork should probably move to some
|
||||
higher layer. */
|
||||
if (pc_regno < 0 || pc_regno >= gdbarch_num_regs (gdbarch))
|
||||
return;
|
||||
|
||||
if (regno == -1 || regno == pc_regno)
|
||||
{
|
||||
struct tracepoint *tp = get_tracepoint (tracepoint_number);
|
||||
|
||||
if (tp && tp->base.loc)
|
||||
{
|
||||
/* But don't try to guess if tracepoint is multi-location... */
|
||||
if (tp->base.loc->next)
|
||||
{
|
||||
warning (_("Tracepoint %d has multiple "
|
||||
"locations, cannot infer $pc"),
|
||||
tp->base.number);
|
||||
return;
|
||||
}
|
||||
/* ... or does while-stepping. */
|
||||
if (tp->step_count > 0)
|
||||
{
|
||||
warning (_("Tracepoint %d does while-stepping, "
|
||||
"cannot infer $pc"),
|
||||
tp->base.number);
|
||||
return;
|
||||
}
|
||||
|
||||
store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
|
||||
gdbarch_byte_order (gdbarch),
|
||||
tp->base.loc->address);
|
||||
regcache_raw_supply (regcache, pc_regno, regs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum target_xfer_status
|
||||
tfile_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
const char *annex, gdb_byte *readbuf,
|
||||
const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
|
||||
ULONGEST *xfered_len)
|
||||
{
|
||||
/* We're only doing regular memory for now. */
|
||||
if (object != TARGET_OBJECT_MEMORY)
|
||||
return TARGET_XFER_E_IO;
|
||||
|
||||
if (readbuf == NULL)
|
||||
error (_("tfile_xfer_partial: trace file is read-only"));
|
||||
|
||||
if (traceframe_number != -1)
|
||||
{
|
||||
int pos = 0;
|
||||
|
||||
/* Iterate through the traceframe's blocks, looking for
|
||||
memory. */
|
||||
while ((pos = traceframe_find_block_type ('M', pos)) >= 0)
|
||||
{
|
||||
ULONGEST maddr, amt;
|
||||
unsigned short mlen;
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
|
||||
|
||||
tfile_read ((gdb_byte *) &maddr, 8);
|
||||
maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
|
||||
byte_order);
|
||||
tfile_read ((gdb_byte *) &mlen, 2);
|
||||
mlen = (unsigned short)
|
||||
extract_unsigned_integer ((gdb_byte *) &mlen, 2, byte_order);
|
||||
|
||||
/* If the block includes the first part of the desired
|
||||
range, return as much it has; GDB will re-request the
|
||||
remainder, which might be in a different block of this
|
||||
trace frame. */
|
||||
if (maddr <= offset && offset < (maddr + mlen))
|
||||
{
|
||||
amt = (maddr + mlen) - offset;
|
||||
if (amt > len)
|
||||
amt = len;
|
||||
|
||||
if (maddr != offset)
|
||||
lseek (trace_fd, offset - maddr, SEEK_CUR);
|
||||
tfile_read (readbuf, amt);
|
||||
*xfered_len = amt;
|
||||
return TARGET_XFER_OK;
|
||||
}
|
||||
|
||||
/* Skip over this block. */
|
||||
pos += (8 + 2 + mlen);
|
||||
}
|
||||
}
|
||||
|
||||
/* It's unduly pedantic to refuse to look at the executable for
|
||||
read-only pieces; so do the equivalent of readonly regions aka
|
||||
QTro packet. */
|
||||
/* FIXME account for relocation at some point. */
|
||||
if (exec_bfd)
|
||||
{
|
||||
asection *s;
|
||||
bfd_size_type size;
|
||||
bfd_vma vma;
|
||||
|
||||
for (s = exec_bfd->sections; s; s = s->next)
|
||||
{
|
||||
if ((s->flags & SEC_LOAD) == 0
|
||||
|| (s->flags & SEC_READONLY) == 0)
|
||||
continue;
|
||||
|
||||
vma = s->vma;
|
||||
size = bfd_get_section_size (s);
|
||||
if (vma <= offset && offset < (vma + size))
|
||||
{
|
||||
ULONGEST amt;
|
||||
|
||||
amt = (vma + size) - offset;
|
||||
if (amt > len)
|
||||
amt = len;
|
||||
|
||||
*xfered_len = bfd_get_section_contents (exec_bfd, s,
|
||||
readbuf, offset - vma, amt);
|
||||
return TARGET_XFER_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Indicate failure to find the requested memory block. */
|
||||
return TARGET_XFER_E_IO;
|
||||
}
|
||||
|
||||
/* Iterate through the blocks of a trace frame, looking for a 'V'
|
||||
block with a matching tsv number. */
|
||||
|
||||
static int
|
||||
tfile_get_trace_state_variable_value (struct target_ops *self,
|
||||
int tsvnum, LONGEST *val)
|
||||
{
|
||||
int pos;
|
||||
int found = 0;
|
||||
|
||||
/* Iterate over blocks in current frame and find the last 'V'
|
||||
block in which tsv number is TSVNUM. In one trace frame, there
|
||||
may be multiple 'V' blocks created for a given trace variable,
|
||||
and the last matched 'V' block contains the updated value. */
|
||||
pos = 0;
|
||||
while ((pos = traceframe_find_block_type ('V', pos)) >= 0)
|
||||
{
|
||||
int vnum;
|
||||
|
||||
tfile_read ((gdb_byte *) &vnum, 4);
|
||||
vnum = (int) extract_signed_integer ((gdb_byte *) &vnum, 4,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
if (tsvnum == vnum)
|
||||
{
|
||||
tfile_read ((gdb_byte *) val, 8);
|
||||
*val = extract_signed_integer ((gdb_byte *) val, 8,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
found = 1;
|
||||
}
|
||||
pos += (4 + 8);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_all_memory (struct target_ops *ops)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_memory (struct target_ops *ops)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_stack (struct target_ops *ops)
|
||||
{
|
||||
return traceframe_number != -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_has_registers (struct target_ops *ops)
|
||||
{
|
||||
return traceframe_number != -1;
|
||||
}
|
||||
|
||||
static int
|
||||
tfile_thread_alive (struct target_ops *ops, ptid_t ptid)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Callback for traceframe_walk_blocks. Builds a traceframe_info
|
||||
object for the tfile target's current traceframe. */
|
||||
|
||||
static int
|
||||
build_traceframe_info (char blocktype, void *data)
|
||||
{
|
||||
struct traceframe_info *info = data;
|
||||
|
||||
switch (blocktype)
|
||||
{
|
||||
case 'M':
|
||||
{
|
||||
struct mem_range *r;
|
||||
ULONGEST maddr;
|
||||
unsigned short mlen;
|
||||
|
||||
tfile_read ((gdb_byte *) &maddr, 8);
|
||||
maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
|
||||
gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
tfile_read ((gdb_byte *) &mlen, 2);
|
||||
mlen = (unsigned short)
|
||||
extract_unsigned_integer ((gdb_byte *) &mlen,
|
||||
2, gdbarch_byte_order
|
||||
(target_gdbarch ()));
|
||||
|
||||
r = VEC_safe_push (mem_range_s, info->memory, NULL);
|
||||
|
||||
r->start = maddr;
|
||||
r->length = mlen;
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
{
|
||||
int vnum;
|
||||
|
||||
tfile_read ((gdb_byte *) &vnum, 4);
|
||||
VEC_safe_push (int, info->tvars, vnum);
|
||||
}
|
||||
case 'R':
|
||||
case 'S':
|
||||
{
|
||||
break;
|
||||
}
|
||||
default:
|
||||
warning (_("Unhandled trace block type (%d) '%c ' "
|
||||
"while building trace frame info."),
|
||||
blocktype, blocktype);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct traceframe_info *
|
||||
tfile_traceframe_info (struct target_ops *self)
|
||||
{
|
||||
struct traceframe_info *info = XCNEW (struct traceframe_info);
|
||||
|
||||
traceframe_walk_blocks (build_traceframe_info, 0, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
init_tfile_ops (void)
|
||||
{
|
||||
tfile_ops.to_shortname = "tfile";
|
||||
tfile_ops.to_longname = "Local trace dump file";
|
||||
tfile_ops.to_doc
|
||||
= "Use a trace file as a target. Specify the filename of the trace file.";
|
||||
tfile_ops.to_open = tfile_open;
|
||||
tfile_ops.to_close = tfile_close;
|
||||
tfile_ops.to_fetch_registers = tfile_fetch_registers;
|
||||
tfile_ops.to_xfer_partial = tfile_xfer_partial;
|
||||
tfile_ops.to_files_info = tfile_files_info;
|
||||
tfile_ops.to_get_trace_status = tfile_get_trace_status;
|
||||
tfile_ops.to_get_tracepoint_status = tfile_get_tracepoint_status;
|
||||
tfile_ops.to_trace_find = tfile_trace_find;
|
||||
tfile_ops.to_get_trace_state_variable_value
|
||||
= tfile_get_trace_state_variable_value;
|
||||
tfile_ops.to_stratum = process_stratum;
|
||||
tfile_ops.to_has_all_memory = tfile_has_all_memory;
|
||||
tfile_ops.to_has_memory = tfile_has_memory;
|
||||
tfile_ops.to_has_stack = tfile_has_stack;
|
||||
tfile_ops.to_has_registers = tfile_has_registers;
|
||||
tfile_ops.to_traceframe_info = tfile_traceframe_info;
|
||||
tfile_ops.to_thread_alive = tfile_thread_alive;
|
||||
tfile_ops.to_magic = OPS_MAGIC;
|
||||
}
|
||||
|
||||
void
|
||||
free_current_marker (void *arg)
|
||||
{
|
||||
|
@ -5364,8 +4553,4 @@ Set notes string to use for future tstop commands"), _("\
|
|||
Show the notes string to use for future tstop commands"), NULL,
|
||||
set_trace_stop_notes, NULL,
|
||||
&setlist, &showlist);
|
||||
|
||||
init_tfile_ops ();
|
||||
|
||||
add_target_with_completer (&tfile_ops, filename_completer);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue