mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
ac7936dfd0
This patches removes get_regcache_arch, and use regache->arch () instead. The motivation of this change is that I am going to move some basic stuff into a base class of regcache. I don't need to update "client" code regcache->arch (). On the other hand, this patch shortens the code a little bit. gdb: 2017-10-25 Yao Qi <yao.qi@linaro.org> * aarch32-linux-nat.c (aarch32_gp_regcache_supply): Use regcache->arch () instead get_regcache_arch. * aarch64-fbsd-nat.c (aarch64_fbsd_fetch_inferior_registers): Likewise. (aarch64_fbsd_store_inferior_registers): Likewise. * aarch64-linux-nat.c (fetch_gregs_from_thread): Likewise. (store_gregs_to_thread): Likewise. (fetch_fpregs_from_thread): Likewise. (store_fpregs_to_thread): Likewise. * aarch64-tdep.c (aarch64_extract_return_value): Likewise. (aarch64_store_return_value): Likewise. (aarch64_software_single_step): Likewise. * aix-thread.c (aix_thread_wait): Likewise. (supply_reg32): Likewise. (supply_sprs64): Likewise. (supply_sprs32): Likewise. (fill_gprs64): Likewise. (fill_gprs32): Likewise. (fill_sprs64): Likewise. (fill_sprs32): Likewise. (store_regs_user_thread): Likewise. (store_regs_kernel_thread): Likewise. * alpha-bsd-nat.c (alphabsd_fetch_inferior_registers): Likewise. (alphabsd_store_inferior_registers): Likewise. * alpha-tdep.c (alpha_extract_return_value): Likewise. (alpha_store_return_value): Likewise. (alpha_deal_with_atomic_sequence): Likewise. (alpha_next_pc): Likewise. (alpha_software_single_step): Likewise. * amd64-bsd-nat.c (amd64bsd_fetch_inferior_registers): Likewise. (amd64bsd_store_inferior_registers): Likewise. * amd64-linux-nat.c (amd64_linux_fetch_inferior_registers): Likewise. (amd64_linux_store_inferior_registers): Likewise. * amd64-nat.c (amd64_supply_native_gregset): Likewise. (amd64_collect_native_gregset): Likewise. * amd64-obsd-tdep.c (amd64obsd_supply_uthread): Likewise. (amd64obsd_collect_uthread): Likewise. * amd64-tdep.c (amd64_supply_fpregset): Likewise. (amd64_collect_fpregset): Likewise. (amd64_supply_fxsave): Likewise. (amd64_supply_xsave): Likewise. (amd64_collect_fxsave): Likewise. (amd64_collect_xsave): Likewise. * arc-tdep.c (arc_write_pc): Likewise. * arch-utils.c (default_skip_permanent_breakpoint): Likewise. * arm-fbsd-nat.c (arm_fbsd_fetch_inferior_registers): Likewise. (arm_fbsd_store_inferior_registers): Likewise. * arm-linux-nat.c (fetch_vfp_regs): Likewise. (store_vfp_regs): Likewise. (arm_linux_fetch_inferior_registers): Likewise. (arm_linux_store_inferior_registers): Likewise. * arm-linux-tdep.c (arm_linux_supply_gregset): Likewise. (arm_linux_sigreturn_next_pc): Likewise. (arm_linux_get_next_pcs_syscall_next_pc): Likewise. * arm-nbsd-nat.c (arm_supply_gregset): Likewise. (fetch_register): Likewise. (store_register): Likewise. * arm-tdep.c (arm_is_thumb): Likewise. (displaced_in_arm_mode): Likewise. (bx_write_pc): Likewise. (arm_get_next_pcs_addr_bits_remove): Likewise. (arm_software_single_step): Likewise. (arm_extract_return_value): Likewise. (arm_store_return_value): Likewise. (arm_write_pc): Likewise. * bfin-tdep.c (bfin_extract_return_value): Likewise. * bsd-uthread.c (bsd_uthread_fetch_registers): Likewise. (bsd_uthread_store_registers): Likewise. * core-regset.c (fetch_core_registers): Likewise. * corelow.c (get_core_registers): Likewise. * cris-tdep.c (cris_store_return_value): Likewise. (cris_extract_return_value): Likewise. (find_step_target): Likewise. (find_step_target): Likewise. (cris_software_single_step): Likewise. * ctf.c (ctf_fetch_registers): Likewise. * darwin-nat.c (cancel_breakpoint): Likewise. * fbsd-tdep.c (fbsd_collect_thread_registers): Likewise. * frv-tdep.c (frv_extract_return_value): Likewise. * ft32-tdep.c (ft32_store_return_value): Likewise. (ft32_extract_return_value): Likewise. * go32-nat.c (fetch_register): Likewise. (go32_fetch_registers): Likewise. (go32_store_registers): Likewise. (store_register): Likewise. * h8300-tdep.c (h8300_extract_return_value): Likewise. (h8300_store_return_value): Likewise. * hppa-linux-nat.c (fetch_register): Likewise. (store_register): Likewise. (hppa_linux_fetch_inferior_registers): Likewise. (hppa_linux_store_inferior_registers): Likewise. * i386-darwin-nat.c (i386_darwin_fetch_inferior_registers): Likewise. (i386_darwin_store_inferior_registers): Likewise. * i386-gnu-nat.c (gnu_fetch_registers): Likewise. (gnu_store_registers): Likewise. * i386-linux-nat.c (fetch_register): Likewise. (store_register): Likewise. (supply_gregset): Likewise. (fill_gregset): Likewise. (i386_linux_fetch_inferior_registers): Likewise. (i386_linux_store_inferior_registers): Likewise. (i386_linux_resume): Likewise. * i386-linux-tdep.c (i386_linux_get_syscall_number_from_regcache): Likewise. * i386-nto-tdep.c (i386nto_supply_gregset): Likewise. * i386-obsd-nat.c (i386obsd_supply_pcb): Likewise. * i386-obsd-tdep.c (i386obsd_supply_uthread): Likewise. (i386obsd_collect_uthread): Likewise. * i386-tdep.c (i386_mmx_regnum_to_fp_regnum): Likewise. (i386_supply_gregset): Likewise. (i386_collect_gregset): Likewise. (i386_supply_fpregset): Likewise. (i386_collect_fpregset): Likewise. (i386_mpx_bd_base): Likewise. * i386-v4-nat.c (supply_fpregset): Likewise. (fill_fpregset): Likewise. * i387-tdep.c (i387_supply_fsave): Likewise. (i387_collect_fsave): Likewise. (i387_supply_fxsave): Likewise. (i387_collect_fxsave): Likewise. (i387_supply_xsave): Likewise. (i387_collect_xsave): Likewise. * ia64-linux-nat.c (ia64_linux_fetch_registers): Likewise. (ia64_linux_store_registers): Likewise. * ia64-tdep.c (ia64_access_rse_reg): Likewise. (ia64_extract_return_value): Likewise. (ia64_store_return_value): Likewise. (find_func_descr): Likewise. * inf-child.c (inf_child_fetch_inferior_registers): Likewise. * inf-ptrace.c (inf_ptrace_fetch_registers): Likewise. (inf_ptrace_store_registers): Likewise. * infrun.c (use_displaced_stepping): Likewise. (displaced_step_prepare_throw): Likewise. (resume): Likewise. (proceed): Likewise. (do_target_wait): Likewise. (adjust_pc_after_break): Likewise. (handle_inferior_event_1): Likewise. (handle_signal_stop): Likewise. (save_infcall_suspend_state): Likewise. (restore_infcall_suspend_state): Likewise. * iq2000-tdep.c (iq2000_extract_return_value): Likewise. * jit.c (jit_frame_prev_register): Likewise. * linux-nat.c (save_stop_reason): Likewise. (linux_nat_wait_1): Likewise. (resume_stopped_resumed_lwps): Likewise. * linux-record.c (record_linux_sockaddr): Likewise. (record_linux_msghdr): Likewise. (record_linux_system_call): Likewise. * linux-tdep.c (linux_collect_thread_registers): Likewise. * lm32-tdep.c (lm32_extract_return_value): Likewise. (lm32_store_return_value): Likewise. * m32c-tdep.c (m32c_read_flg): Likewise. (m32c_pseudo_register_read): Likewise. (m32c_pseudo_register_write): Likewise. * m32r-linux-tdep.c (m32r_linux_supply_gregset): Likewise. (m32r_linux_collect_gregset): Likewise. * m32r-tdep.c (m32r_store_return_value): Likewise. (m32r_extract_return_value): Likewise. * m68k-bsd-nat.c (m68kbsd_supply_fpregset): Likewise. (m68kbsd_collect_fpregset): Likewise. * m68k-bsd-tdep.c (m68kbsd_supply_fpregset): Likewise. * m68k-linux-nat.c (fetch_register): Likewise. (old_fetch_inferior_registers): Likewise. (old_store_inferior_registers): Likewise. (store_regs): Likewise. * m68k-tdep.c (m68k_svr4_extract_return_value): Likewise. (m68k_svr4_store_return_value): Likewise. * m88k-tdep.c (m88k_store_arguments): Likewise. * mi/mi-main.c (mi_cmd_data_list_changed_registers): Likewise. (mi_cmd_data_write_register_values): Likewise. * mips-fbsd-nat.c (mips_fbsd_fetch_inferior_registers): Likewise. (mips_fbsd_store_inferior_registers): Likewise. * mips-fbsd-tdep.c (mips_fbsd_supply_fpregs): Likewise. (mips_fbsd_supply_gregs): Likewise. (mips_fbsd_collect_fpregs): Likewise. (mips_fbsd_collect_gregs): Likewise. (mips_fbsd_supply_fpregset): Likewise. (mips_fbsd_collect_fpregset): Likewise. (mips_fbsd_supply_gregset): Likewise. (mips_fbsd_collect_gregset): Likewise. * mips-linux-nat.c (supply_gregset): Likewise. (fill_gregset): Likewise. (supply_fpregset): Likewise. (fill_fpregset): Likewise. * mips-linux-tdep.c (mips_supply_gregset): Likewise. (mips_fill_gregset): Likewise. (mips_supply_fpregset): Likewise. (mips_fill_fpregset): Likewise. (mips64_supply_gregset): Likewise. (micromips_linux_sigframe_validate): Likewise. * mips-nbsd-nat.c (mipsnbsd_fetch_inferior_registers): Likewise. (mipsnbsd_fetch_inferior_registers): Likewise. (mipsnbsd_store_inferior_registers): Likewise. * mips-nbsd-tdep.c (mipsnbsd_supply_fpregset): Likewise. (mipsnbsd_supply_gregset): Likewise. (mipsnbsd_iterate_over_regset_sections): Likewise. (mipsnbsd_supply_reg): Likewise. (mipsnbsd_supply_fpreg): Likewise. * mips-tdep.c (mips_in_frame_stub): Likewise. (mips_dummy_id): Likewise. (is_octeon_bbit_op): Likewise. (micromips_bc1_pc): Likewise. (extended_mips16_next_pc): Likewise. (mips16_next_pc): Likewise. (deal_with_atomic_sequence): Likewise. * moxie-tdep.c (moxie_process_readu): Likewise. * nios2-tdep.c (nios2_get_next_pc): Likewise. * nto-procfs.c (procfs_store_registers): Likewise. * ppc-fbsd-nat.c (ppcfbsd_fetch_inferior_registers): Likewise. (ppcfbsd_store_inferior_registers): Likewise. * ppc-linux-nat.c (fetch_vsx_register): Likewise. (fetch_altivec_register): Likewise. (get_spe_registers): Likewise. (fetch_spe_register): Likewise. (fetch_altivec_registers): Likewise. (fetch_all_gp_regs): Likewise. (fetch_all_fp_regs): Likewise. (store_vsx_register): Likewise. (store_altivec_register): Likewise. (set_spe_registers): Likewise. (store_spe_register): Likewise. (store_altivec_registers): Likewise. (store_all_gp_regs): Likewise. (store_all_fp_regs): Likewise. * ppc-linux-tdep.c (ppc_linux_supply_gregset): Likewise. (ppc_linux_collect_gregset): Likewise. (ppc_canonicalize_syscall): Likewise. (ppc_linux_record_signal): Likewise. (ppu2spu_prev_register): Likewise. * ppc-nbsd-nat.c (ppcnbsd_supply_pcb): Likewise. * ppc-obsd-nat.c (ppcobsd_fetch_registers): Likewise. (ppcobsd_store_registers): Likewise. * ppc-ravenscar-thread.c (ppc_ravenscar_generic_fetch_registers): Likewise. (ppc_ravenscar_generic_store_registers): Likewise. * procfs.c (procfs_fetch_registers): Likewise. (procfs_store_registers): Likewise. * ravenscar-thread.c (ravenscar_fetch_registers): Likewise. (ravenscar_store_registers): Likewise. (ravenscar_prepare_to_store): Likewise. * record-btrace.c (record_btrace_fetch_registers): Likewise. * record-full.c (record_full_wait_1): Likewise. (record_full_registers_change): Likewise. (record_full_store_registers): Likewise. (record_full_core_fetch_registers): Likewise. (record_full_save): Likewise. (record_full_goto_insn): Likewise. * regcache.c (regcache_register_size): Likewise. (get_regcache_arch): Remove. (regcache_read_pc): Likewise. * regcache.h (get_regcache_arch): Remove. * remote-sim.c (gdbsim_fetch_register): Likewise. (gdbsim_store_register): Likewise. * remote.c (fetch_register_using_p): Likewise. (send_g_packet): Likewise. (remote_prepare_to_store): Likewise. (store_registers_using_G): Likewise. * reverse.c (save_bookmark_command): Likewise. (goto_bookmark_command): Likewise. * rs6000-aix-tdep.c (branch_dest): Likewise. * rs6000-nat.c (rs6000_ptrace64): Likewise. (fetch_register): Likewise. * rs6000-tdep.c (ppc_supply_reg): Likewise. (ppc_collect_reg): Likewise. (ppc_collect_gregset): Likewise. (ppc_collect_fpregset): Likewise. (ppc_collect_vsxregset): Likewise. (ppc_collect_vrregset): Likewise. (ppc_displaced_step_hw_singlestep): Likewise. (rs6000_pseudo_register_read): Likewise. (rs6000_pseudo_register_write): Likewise. * s390-linux-nat.c (supply_gregset): Likewise. (fill_gregset): Likewise. (s390_linux_fetch_inferior_registers): Likewise. * s390-linux-tdep.c (s390_write_pc): Likewise. (s390_software_single_step): Likewise. (s390_all_but_pc_registers_record): Likewise. (s390_linux_syscall_record): Likewise. * sentinel-frame.c (sentinel_frame_prev_arch): Likewise. * sh-nbsd-nat.c (shnbsd_fetch_inferior_registers): Likewise. (shnbsd_store_inferior_registers): Likewise. * sh-tdep.c (sh_extract_return_value_nofpu): Likewise. (sh_extract_return_value_fpu): Likewise. (sh_store_return_value_nofpu): Likewise. (sh_corefile_supply_regset): Likewise. (sh_corefile_collect_regset): Likewise. * sh64-tdep.c (sh64_extract_return_value): Likewise. (sh64_store_return_value): Likewise. * sparc-linux-tdep.c (sparc32_linux_collect_core_fpregset): Likewise. * sparc-nat.c (sparc_fetch_inferior_registers): Likewise. (sparc_store_inferior_registers): Likewise. * sparc-ravenscar-thread.c (register_in_thread_descriptor_p): Likewise. (sparc_ravenscar_prepare_to_store): Likewise. * sparc-tdep.c (sparc32_store_arguments): Likewise. (sparc_analyze_control_transfer): Likewise. (sparc_step_trap): Likewise. (sparc_software_single_step): Likewise. (sparc32_gdbarch_init): Likewise. (sparc_supply_rwindow): Likewise. (sparc_collect_rwindow): Likewise. * sparc64-linux-tdep.c (sparc64_linux_collect_core_fpregset): Likewise. * sparc64-nbsd-nat.c (sparc64nbsd_supply_gregset): Likewise. (sparc64nbsd_collect_gregset): Likewise. (sparc64nbsd_supply_fpregset): Likewise. (sparc64nbsd_collect_fpregset): Likewise. * sparc64-tdep.c (sparc64_store_arguments): Likewise. (sparc64_supply_gregset): Likewise. (sparc64_collect_gregset): Likewise. (sparc64_supply_fpregset): Likewise. (sparc64_collect_fpregset): Likewise. * spu-linux-nat.c (spu_fetch_inferior_registers): Likewise. * spu-tdep.c (spu_unwind_sp): Likewise. (spu2ppu_prev_register): Likewise. (spu_memory_remove_breakpoint): Likewise. * stack.c (return_command): Likewise. * tic6x-tdep.c (tic6x_extract_signed_field): Likewise. * tracefile-tfile.c (tfile_fetch_registers): Likewise. * tracefile.c (trace_save_ctf): Likewise. * windows-nat.c (do_windows_fetch_inferior_registers): Likewise. (do_windows_store_inferior_registers): Likewise. (windows_resume): Likewise. * xtensa-linux-nat.c (fill_gregset): Likewise. (supply_gregset_reg): Likewise. * xtensa-tdep.c (xtensa_register_write_masked): Likewise. (xtensa_register_read_masked): Likewise. (xtensa_supply_gregset): Likewise. (xtensa_extract_return_value): Likewise. (xtensa_store_return_value): Likewise.
1139 lines
30 KiB
C
1139 lines
30 KiB
C
/* Trace file TFILE format support in GDB.
|
|
|
|
Copyright (C) 1997-2017 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 */
|
|
#include "regcache.h"
|
|
#include "inferior.h"
|
|
#include "gdbthread.h"
|
|
#include "exec.h" /* exec_bfd */
|
|
#include "completer.h"
|
|
#include "filenames.h"
|
|
#include "remote.h"
|
|
#include "xml-tdesc.h"
|
|
#include "target-descriptions.h"
|
|
#include "buffer.h"
|
|
#include <algorithm>
|
|
|
|
#ifndef O_LARGEFILE
|
|
#define O_LARGEFILE 0
|
|
#endif
|
|
|
|
/* 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").release ();
|
|
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 == trace_stop_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 = NULL;
|
|
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 != NULL ? 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_tdesc. */
|
|
|
|
static void
|
|
tfile_write_tdesc (struct trace_file_writer *self)
|
|
{
|
|
struct tfile_trace_file_writer *writer
|
|
= (struct tfile_trace_file_writer *) self;
|
|
|
|
gdb::optional<std::string> tdesc
|
|
= target_fetch_description_xml (¤t_target);
|
|
|
|
if (!tdesc)
|
|
return;
|
|
|
|
const char *ptr = tdesc->c_str ();
|
|
|
|
/* Write tdesc line by line, prefixing each line with "tdesc ". */
|
|
while (ptr != NULL)
|
|
{
|
|
const char *next = strchr (ptr, '\n');
|
|
if (next != NULL)
|
|
{
|
|
fprintf (writer->fp, "tdesc %.*s\n", (int) (next - ptr), ptr);
|
|
/* Skip the \n. */
|
|
next++;
|
|
}
|
|
else if (*ptr != '\0')
|
|
{
|
|
/* Last line, doesn't have a newline. */
|
|
fprintf (writer->fp, "tdesc %s\n", ptr);
|
|
}
|
|
ptr = next;
|
|
}
|
|
}
|
|
|
|
/* 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_tdesc,
|
|
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
|
|
= XNEW (struct tfile_trace_file_writer);
|
|
|
|
writer->base.ops = &tfile_write_ops;
|
|
writer->fp = NULL;
|
|
writer->pathname = NULL;
|
|
|
|
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 struct buffer trace_tdesc;
|
|
|
|
static void tfile_append_tdesc_line (const char *line);
|
|
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 (const char *arg, int from_tty)
|
|
{
|
|
char *temp;
|
|
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 (!arg)
|
|
error (_("No trace file specified."));
|
|
|
|
gdb::unique_xmalloc_ptr<char> filename (tilde_expand (arg));
|
|
if (!IS_ABSOLUTE_PATH (filename.get ()))
|
|
filename.reset (concat (current_directory, "/", filename.get (),
|
|
(char *) NULL));
|
|
|
|
flags = O_BINARY | O_LARGEFILE;
|
|
flags |= O_RDONLY;
|
|
scratch_chan = gdb_open_cloexec (filename.get (), flags, 0);
|
|
if (scratch_chan < 0)
|
|
perror_with_name (filename.get ());
|
|
|
|
/* Looks semi-reasonable. Toss the old trace file and work on the new. */
|
|
|
|
unpush_target (&tfile_ops);
|
|
|
|
trace_filename = filename.release ();
|
|
trace_fd = scratch_chan;
|
|
|
|
/* Make sure this is clear. */
|
|
buffer_free (&trace_tdesc);
|
|
|
|
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
|
|
&& (startswith (header + 1, "TRACE0\n"))))
|
|
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
|
|
{
|
|
/* 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"));
|
|
}
|
|
|
|
/* By now, tdesc lines have been read from tfile - let's parse them. */
|
|
target_find_description ();
|
|
|
|
/* 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"));
|
|
}
|
|
CATCH (ex, RETURN_MASK_ALL)
|
|
{
|
|
/* Remove the partially set up target. */
|
|
unpush_target (&tfile_ops);
|
|
throw_exception (ex);
|
|
}
|
|
END_CATCH
|
|
|
|
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 (startswith (p, "R "))
|
|
{
|
|
p += strlen ("R ");
|
|
trace_regblock_size = strtol (p, &p, 16);
|
|
}
|
|
else if (startswith (p, "status "))
|
|
{
|
|
p += strlen ("status ");
|
|
parse_trace_status (p, current_trace_status ());
|
|
}
|
|
else if (startswith (p, "tp "))
|
|
{
|
|
p += strlen ("tp ");
|
|
parse_tracepoint_definition (p, utpp);
|
|
}
|
|
else if (startswith (p, "tsv "))
|
|
{
|
|
p += strlen ("tsv ");
|
|
parse_tsv_definition (p, utsvp);
|
|
}
|
|
else if (startswith (p, "tdesc "))
|
|
{
|
|
p += strlen ("tdesc ");
|
|
tfile_append_tdesc_line (p);
|
|
}
|
|
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;
|
|
buffer_free (&trace_tdesc);
|
|
|
|
trace_reset_local_state ();
|
|
}
|
|
|
|
static void
|
|
tfile_files_info (struct target_ops *t)
|
|
{
|
|
printf_filtered ("\t`%s'\n", trace_filename);
|
|
}
|
|
|
|
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->loc)
|
|
addr = tp->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 = (char *) 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 = regcache->arch ();
|
|
int offset, regn, regsize, dummy;
|
|
|
|
/* An uninitialized reg size says we're not going to be
|
|
successful at getting register blocks. */
|
|
if (!trace_regblock_size)
|
|
return;
|
|
|
|
if (traceframe_find_block_type ('R', 0) >= 0)
|
|
{
|
|
gdb_byte *regs = (gdb_byte *) alloca (trace_regblock_size);
|
|
|
|
tfile_read (regs, trace_regblock_size);
|
|
|
|
for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
|
|
{
|
|
if (!remote_register_number_and_offset (regcache->arch (),
|
|
regn, &dummy, &offset))
|
|
continue;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
tracefile_fetch_registers (regcache, regno);
|
|
}
|
|
|
|
static enum target_xfer_status
|
|
tfile_xfer_partial_features (struct target_ops *ops, const char *annex,
|
|
gdb_byte *readbuf, const gdb_byte *writebuf,
|
|
ULONGEST offset, ULONGEST len,
|
|
ULONGEST *xfered_len)
|
|
{
|
|
if (strcmp (annex, "target.xml"))
|
|
return TARGET_XFER_E_IO;
|
|
|
|
if (readbuf == NULL)
|
|
error (_("tfile_xfer_partial: tdesc is read-only"));
|
|
|
|
if (trace_tdesc.used_size == 0)
|
|
return TARGET_XFER_E_IO;
|
|
|
|
if (offset >= trace_tdesc.used_size)
|
|
return TARGET_XFER_EOF;
|
|
|
|
if (len > trace_tdesc.used_size - offset)
|
|
len = trace_tdesc.used_size - offset;
|
|
|
|
memcpy (readbuf, trace_tdesc.buffer + offset, len);
|
|
*xfered_len = len;
|
|
|
|
return TARGET_XFER_OK;
|
|
}
|
|
|
|
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 and tdesc for now. */
|
|
if (object == TARGET_OBJECT_AVAILABLE_FEATURES)
|
|
return tfile_xfer_partial_features (ops, annex, readbuf, writebuf,
|
|
offset, len, xfered_len);
|
|
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;
|
|
enum target_xfer_status res;
|
|
/* Records the lowest available address of all blocks that
|
|
intersects the requested range. */
|
|
ULONGEST low_addr_available = 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;
|
|
}
|
|
|
|
if (offset < maddr && maddr < (offset + len))
|
|
if (low_addr_available == 0 || low_addr_available > maddr)
|
|
low_addr_available = maddr;
|
|
|
|
/* Skip over this block. */
|
|
pos += (8 + 2 + mlen);
|
|
}
|
|
|
|
/* Requested memory is unavailable in the context of traceframes,
|
|
and this address falls within a read-only section, fallback
|
|
to reading from executable, up to LOW_ADDR_AVAILABLE. */
|
|
if (offset < low_addr_available)
|
|
len = std::min (len, low_addr_available - offset);
|
|
res = exec_read_partial_read_only (readbuf, offset, len, xfered_len);
|
|
|
|
if (res == TARGET_XFER_OK)
|
|
return TARGET_XFER_OK;
|
|
else
|
|
{
|
|
/* No use trying further, we know some memory starting
|
|
at MEMADDR isn't available. */
|
|
*xfered_len = len;
|
|
return TARGET_XFER_UNAVAILABLE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Fallback to reading from read-only sections. */
|
|
return section_table_read_available_memory (readbuf, offset, len,
|
|
xfered_len);
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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 = (struct traceframe_info *) data;
|
|
|
|
switch (blocktype)
|
|
{
|
|
case 'M':
|
|
{
|
|
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 ()));
|
|
|
|
info->memory.emplace_back (maddr, mlen);
|
|
break;
|
|
}
|
|
case 'V':
|
|
{
|
|
int vnum;
|
|
|
|
tfile_read ((gdb_byte *) &vnum, 4);
|
|
info->tvars.push_back (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 traceframe_info_up
|
|
tfile_traceframe_info (struct target_ops *self)
|
|
{
|
|
traceframe_info_up info (new traceframe_info);
|
|
|
|
traceframe_walk_blocks (build_traceframe_info, 0, info.get ());
|
|
|
|
return info;
|
|
}
|
|
|
|
/* Handles tdesc lines from tfile by appending the payload to
|
|
a global trace_tdesc variable. */
|
|
|
|
static void
|
|
tfile_append_tdesc_line (const char *line)
|
|
{
|
|
buffer_grow_str (&trace_tdesc, line);
|
|
buffer_grow_str (&trace_tdesc, "\n");
|
|
}
|
|
|
|
static void
|
|
init_tfile_ops (void)
|
|
{
|
|
init_tracefile_ops (&tfile_ops);
|
|
|
|
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_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_traceframe_info = tfile_traceframe_info;
|
|
}
|
|
|
|
void
|
|
_initialize_tracefile_tfile (void)
|
|
{
|
|
init_tfile_ops ();
|
|
|
|
add_target_with_completer (&tfile_ops, filename_completer);
|
|
}
|