diff options
Diffstat (limited to 'sim/common/cgen-trace.c')
-rw-r--r-- | sim/common/cgen-trace.c | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/sim/common/cgen-trace.c b/sim/common/cgen-trace.c new file mode 100644 index 0000000..9b7d1fa --- /dev/null +++ b/sim/common/cgen-trace.c @@ -0,0 +1,414 @@ +/* Tracing support for CGEN-based simulators. + Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + Contributed by Cygnus Support. + +This file is part of GDB, the GNU debugger. + +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 2, 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, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include <errno.h> +#include "dis-asm.h" +#include "bfd.h" +#include "sim-main.h" + +#undef min +#define min(a,b) ((a) < (b) ? (a) : (b)) + +#ifndef SIZE_INSTRUCTION +#define SIZE_INSTRUCTION 16 +#endif + +#ifndef SIZE_LOCATION +#define SIZE_LOCATION 20 +#endif + +#ifndef SIZE_PC +#define SIZE_PC 6 +#endif + +#ifndef SIZE_LINE_NUMBER +#define SIZE_LINE_NUMBER 4 +#endif + +#ifndef SIZE_CYCLE_COUNT +#define SIZE_CYCLE_COUNT 2 +#endif + +#ifndef SIZE_TOTAL_CYCLE_COUNT +#define SIZE_TOTAL_CYCLE_COUNT 9 +#endif + +#ifndef SIZE_TRACE_BUF +#define SIZE_TRACE_BUF 256 +#endif + +static void +disassemble_insn (SIM_CPU *, const CGEN_INSN *, + const struct argbuf *, IADDR, char *); + +/* Text is queued in TRACE_BUF because we want to output the insn's cycle + count first but that isn't known until after the insn has executed. + This also handles the queueing of trace results, TRACE_RESULT may be + called multiple times for one insn. */ +static char trace_buf[SIZE_TRACE_BUF]; +/* If NULL, output to stdout directly. */ +static char *bufptr; + +/* Non-zero if this is the first insn in a set of parallel insns. */ +static int first_insn_p; + +/* For communication between trace_insn and trace_result. */ +static int printed_result_p; + +/* Insn and its extracted fields. + Set by trace_insn, used by trace_insn_fini. + ??? Move to SIM_CPU to support heterogeneous multi-cpu case. */ +static const struct cgen_insn *current_insn; +static const struct argbuf *current_abuf; + +void +trace_insn_init (SIM_CPU *cpu, int first_p) +{ + bufptr = trace_buf; + *bufptr = 0; + first_insn_p = first_p; + + /* Set to NULL so trace_insn_fini can know if trace_insn was called. */ + current_insn = NULL; + current_abuf = NULL; +} + +void +trace_insn_fini (SIM_CPU *cpu, const struct argbuf *abuf, int last_p) +{ + SIM_DESC sd = CPU_STATE (cpu); + + /* Was insn traced? It might not be if trace ranges are in effect. */ + if (current_insn == NULL) + return; + + /* The first thing printed is current and total cycle counts. */ + + if (PROFILE_MODEL_P (cpu) + && ARGBUF_PROFILE_P (current_abuf)) + { + unsigned long total = PROFILE_MODEL_TOTAL_CYCLES (CPU_PROFILE_DATA (cpu)); + unsigned long this_insn = PROFILE_MODEL_CUR_INSN_CYCLES (CPU_PROFILE_DATA (cpu)); + + if (last_p) + { + trace_printf (sd, cpu, "%-*ld %-*ld ", + SIZE_CYCLE_COUNT, this_insn, + SIZE_TOTAL_CYCLE_COUNT, total); + } + else + { + trace_printf (sd, cpu, "%-*ld %-*s ", + SIZE_CYCLE_COUNT, this_insn, + SIZE_TOTAL_CYCLE_COUNT, "---"); + } + } + + /* Print the disassembled insn. */ + + trace_printf (sd, cpu, "%s", TRACE_PREFIX (CPU_TRACE_DATA (cpu))); + +#if 0 + /* Print insn results. */ + { + const CGEN_OPINST *opinst = CGEN_INSN_OPERANDS (current_insn); + + if (opinst) + { + int i; + int indices[MAX_OPERAND_INSTANCES]; + + /* Fetch the operands used by the insn. */ + /* FIXME: Add fn ptr to CGEN_CPU_DESC. */ + CGEN_SYM (get_insn_operands) (CPU_CPU_DESC (cpu), current_insn, + 0, CGEN_FIELDS_BITSIZE (&insn_fields), + indices); + + for (i = 0; + CGEN_OPINST_TYPE (opinst) != CGEN_OPINST_END; + ++i, ++opinst) + { + if (CGEN_OPINST_TYPE (opinst) == CGEN_OPINST_OUTPUT) + trace_result (cpu, current_insn, opinst, indices[i]); + } + } + } +#endif + + /* Print anything else requested. */ + + if (*trace_buf) + trace_printf (sd, cpu, " %s\n", trace_buf); + else + trace_printf (sd, cpu, "\n"); +} + +void +trace_insn (SIM_CPU *cpu, const struct cgen_insn *opcode, + const struct argbuf *abuf, IADDR pc) +{ + char disasm_buf[50]; + + printed_result_p = 0; + current_insn = opcode; + current_abuf = abuf; + + if (CGEN_INSN_VIRTUAL_P (opcode)) + { + trace_prefix (CPU_STATE (cpu), cpu, NULL_CIA, pc, 0, + NULL, 0, CGEN_INSN_NAME (opcode)); + return; + } + + CPU_DISASSEMBLER (cpu) (cpu, opcode, abuf, pc, disasm_buf); + trace_prefix (CPU_STATE (cpu), cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu), + NULL, 0, + "%s%-*s", + first_insn_p ? " " : "|", + SIZE_INSTRUCTION, disasm_buf); +} + +void +trace_extract (SIM_CPU *cpu, IADDR pc, char *name, ...) +{ + va_list args; + int printed_one_p = 0; + char *fmt; + + va_start (args, name); + + trace_printf (CPU_STATE (cpu), cpu, "Extract: 0x%.*lx: %s ", + SIZE_PC, pc, name); + + do { + int type,ival; + + fmt = va_arg (args, char *); + + if (fmt) + { + if (printed_one_p) + trace_printf (CPU_STATE (cpu), cpu, ", "); + printed_one_p = 1; + type = va_arg (args, int); + switch (type) + { + case 'x' : + ival = va_arg (args, int); + trace_printf (CPU_STATE (cpu), cpu, fmt, ival); + break; + default : + abort (); + } + } + } while (fmt); + + va_end (args); + trace_printf (CPU_STATE (cpu), cpu, "\n"); +} + +void +trace_result (SIM_CPU *cpu, char *name, int type, ...) +{ + va_list args; + + va_start (args, type); + if (printed_result_p) + cgen_trace_printf (cpu, ", "); + + switch (type) + { + case 'x' : + default : + cgen_trace_printf (cpu, "%s <- 0x%x", name, va_arg (args, int)); + break; + case 'D' : + { + DI di; + /* this is separated from previous line for sunos cc */ + di = va_arg (args, DI); + cgen_trace_printf (cpu, "%s <- 0x%x%08x", name, + GETHIDI(di), GETLODI (di)); + break; + } + } + + printed_result_p = 1; + va_end (args); +} + +/* Print trace output to BUFPTR if active, otherwise print normally. + This is only for tracing semantic code. */ + +void +cgen_trace_printf (SIM_CPU *cpu, char *fmt, ...) +{ + va_list args; + + va_start (args, fmt); + + if (bufptr == NULL) + { + if (TRACE_FILE (CPU_TRACE_DATA (cpu)) == NULL) + (* STATE_CALLBACK (CPU_STATE (cpu))->evprintf_filtered) + (STATE_CALLBACK (CPU_STATE (cpu)), fmt, args); + else + vfprintf (TRACE_FILE (CPU_TRACE_DATA (cpu)), fmt, args); + } + else + { + vsprintf (bufptr, fmt, args); + bufptr += strlen (bufptr); + /* ??? Need version of SIM_ASSERT that is always enabled. */ + if (bufptr - trace_buf > SIZE_TRACE_BUF) + abort (); + } + + va_end (args); +} + +/* Disassembly support. */ + +/* sprintf to a "stream" */ + +int +sim_disasm_sprintf VPARAMS ((SFILE *f, const char *format, ...)) +{ +#ifndef __STDC__ + SFILE *f; + const char *format; +#endif + int n; + va_list args; + + VA_START (args, format); +#ifndef __STDC__ + f = va_arg (args, SFILE *); + format = va_arg (args, char *); +#endif + vsprintf (f->current, format, args); + f->current += n = strlen (f->current); + va_end (args); + return n; +} + +/* Memory read support for an opcodes disassembler. */ + +int +sim_disasm_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length, + struct disassemble_info *info) +{ + SIM_CPU *cpu = (SIM_CPU *) info->application_data; + SIM_DESC sd = CPU_STATE (cpu); + int length_read; + + length_read = sim_core_read_buffer (sd, cpu, read_map, myaddr, memaddr, + length); + if (length_read != length) + return EIO; + return 0; +} + +/* Memory error support for an opcodes disassembler. */ + +void +sim_disasm_perror_memory (int status, bfd_vma memaddr, + struct disassemble_info *info) +{ + if (status != EIO) + /* Can't happen. */ + info->fprintf_func (info->stream, "Unknown error %d.", status); + else + /* Actually, address between memaddr and memaddr + len was + out of bounds. */ + info->fprintf_func (info->stream, + "Address 0x%x is out of bounds.", + (int) memaddr); +} + +/* Disassemble using the CGEN opcode table. + ??? While executing an instruction, the insn has been decoded and all its + fields have been extracted. It is certainly possible to do the disassembly + with that data. This seems simpler, but maybe in the future the already + extracted fields will be used. */ + +void +sim_cgen_disassemble_insn (SIM_CPU *cpu, const CGEN_INSN *insn, + const ARGBUF *abuf, IADDR pc, char *buf) +{ + unsigned int length; + unsigned long insn_value; + struct disassemble_info disasm_info; + SFILE sfile; + union { + unsigned8 bytes[CGEN_MAX_INSN_SIZE]; + unsigned16 shorts[8]; + unsigned32 words[4]; + } insn_buf; + SIM_DESC sd = CPU_STATE (cpu); + CGEN_CPU_DESC cd = CPU_CPU_DESC (cpu); + CGEN_EXTRACT_INFO ex_info; + CGEN_FIELDS *fields = alloca (CGEN_CPU_SIZEOF_FIELDS (cd)); + int insn_bit_length = CGEN_INSN_BITSIZE (insn); + int insn_length = insn_bit_length / 8; + + sfile.buffer = sfile.current = buf; + INIT_DISASSEMBLE_INFO (disasm_info, (FILE *) &sfile, + (fprintf_ftype) sim_disasm_sprintf); + disasm_info.endian = + (bfd_big_endian (STATE_PROG_BFD (sd)) ? BFD_ENDIAN_BIG + : bfd_little_endian (STATE_PROG_BFD (sd)) ? BFD_ENDIAN_LITTLE + : BFD_ENDIAN_UNKNOWN); + + length = sim_core_read_buffer (sd, cpu, read_map, &insn_buf, pc, + insn_length); + + switch (min (CGEN_BASE_INSN_SIZE, insn_length)) + { + case 0 : return; /* fake insn, typically "compile" (aka "invalid") */ + case 1 : insn_value = insn_buf.bytes[0]; break; + case 2 : insn_value = T2H_2 (insn_buf.shorts[0]); break; + case 4 : insn_value = T2H_4 (insn_buf.words[0]); break; + default: abort (); + } + + disasm_info.buffer_vma = pc; + disasm_info.buffer = insn_buf.bytes; + disasm_info.buffer_length = length; + + ex_info.dis_info = (PTR) &disasm_info; + ex_info.valid = (1 << length) - 1; + ex_info.insn_bytes = insn_buf.bytes; + + length = (*CGEN_EXTRACT_FN (cd, insn)) (cd, insn, &ex_info, insn_value, fields, pc); + /* Result of extract fn is in bits. */ + /* ??? This assumes that each instruction has a fixed length (and thus + for insns with multiple versions of variable lengths they would each + have their own table entry). */ + if (length == insn_bit_length) + { + (*CGEN_PRINT_FN (cd, insn)) (cd, &disasm_info, insn, fields, pc, length); + } + else + { + /* This shouldn't happen, but aborting is too drastic. */ + strcpy (buf, "***unknown***"); + } +} |