1/* Tracing support for CGEN-based simulators.
2   Copyright (C) 1996, 1997, 1998, 1999, 2007 Free Software Foundation, Inc.
3   Contributed by Cygnus Support.
4
5This file is part of GDB, the GNU debugger.
6
7This program is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3 of the License, or
10(at your option) any later version.
11
12This program is distributed in the hope that it will be useful,
13but WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15GNU General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include <errno.h>
21#include "dis-asm.h"
22#include "bfd.h"
23#include "sim-main.h"
24#include "sim-fpu.h"
25
26#undef min
27#define min(a,b) ((a) < (b) ? (a) : (b))
28
29#ifndef SIZE_INSTRUCTION
30#define SIZE_INSTRUCTION 16
31#endif
32
33#ifndef SIZE_LOCATION
34#define SIZE_LOCATION 20
35#endif
36
37#ifndef SIZE_PC
38#define SIZE_PC 6
39#endif
40
41#ifndef SIZE_LINE_NUMBER
42#define SIZE_LINE_NUMBER 4
43#endif
44
45#ifndef SIZE_CYCLE_COUNT
46#define SIZE_CYCLE_COUNT 2
47#endif
48
49#ifndef SIZE_TOTAL_CYCLE_COUNT
50#define SIZE_TOTAL_CYCLE_COUNT 9
51#endif
52
53#ifndef SIZE_TRACE_BUF
54#define SIZE_TRACE_BUF 1024
55#endif
56
57/* Text is queued in TRACE_BUF because we want to output the insn's cycle
58   count first but that isn't known until after the insn has executed.
59   This also handles the queueing of trace results, TRACE_RESULT may be
60   called multiple times for one insn.  */
61static char trace_buf[SIZE_TRACE_BUF];
62/* If NULL, output to stdout directly.  */
63static char *bufptr;
64
65/* Non-zero if this is the first insn in a set of parallel insns.  */
66static int first_insn_p;
67
68/* For communication between trace_insn and trace_result.  */
69static int printed_result_p;
70
71/* Insn and its extracted fields.
72   Set by trace_insn, used by trace_insn_fini.
73   ??? Move to SIM_CPU to support heterogeneous multi-cpu case.  */
74static const struct cgen_insn *current_insn;
75static const struct argbuf *current_abuf;
76
77void
78trace_insn_init (SIM_CPU *cpu, int first_p)
79{
80  bufptr = trace_buf;
81  *bufptr = 0;
82  first_insn_p = first_p;
83
84  /* Set to NULL so trace_insn_fini can know if trace_insn was called.  */
85  current_insn = NULL;
86  current_abuf = NULL;
87}
88
89void
90trace_insn_fini (SIM_CPU *cpu, const struct argbuf *abuf, int last_p)
91{
92  SIM_DESC sd = CPU_STATE (cpu);
93
94  /* Was insn traced?  It might not be if trace ranges are in effect.  */
95  if (current_insn == NULL)
96    return;
97
98  /* The first thing printed is current and total cycle counts.  */
99
100  if (PROFILE_MODEL_P (cpu)
101      && ARGBUF_PROFILE_P (current_abuf))
102    {
103      unsigned long total = PROFILE_MODEL_TOTAL_CYCLES (CPU_PROFILE_DATA (cpu));
104      unsigned long this_insn = PROFILE_MODEL_CUR_INSN_CYCLES (CPU_PROFILE_DATA (cpu));
105
106      if (last_p)
107	{
108	  trace_printf (sd, cpu, "%-*ld %-*ld ",
109			SIZE_CYCLE_COUNT, this_insn,
110			SIZE_TOTAL_CYCLE_COUNT, total);
111	}
112      else
113	{
114	  trace_printf (sd, cpu, "%-*ld %-*s ",
115			SIZE_CYCLE_COUNT, this_insn,
116			SIZE_TOTAL_CYCLE_COUNT, "---");
117	}
118    }
119
120  /* Print the disassembled insn.  */
121
122  trace_printf (sd, cpu, "%s", TRACE_PREFIX (CPU_TRACE_DATA (cpu)));
123
124#if 0
125  /* Print insn results.  */
126  {
127    const CGEN_OPINST *opinst = CGEN_INSN_OPERANDS (current_insn);
128
129    if (opinst)
130      {
131	int i;
132	int indices[MAX_OPERAND_INSTANCES];
133
134	/* Fetch the operands used by the insn.  */
135	/* FIXME: Add fn ptr to CGEN_CPU_DESC.  */
136	CGEN_SYM (get_insn_operands) (CPU_CPU_DESC (cpu), current_insn,
137				      0, CGEN_FIELDS_BITSIZE (&insn_fields),
138				      indices);
139
140	for (i = 0;
141	     CGEN_OPINST_TYPE (opinst) != CGEN_OPINST_END;
142	     ++i, ++opinst)
143	  {
144	    if (CGEN_OPINST_TYPE (opinst) == CGEN_OPINST_OUTPUT)
145	      trace_result (cpu, current_insn, opinst, indices[i]);
146	  }
147      }
148  }
149#endif
150
151  /* Print anything else requested.  */
152
153  if (*trace_buf)
154    trace_printf (sd, cpu, " %s\n", trace_buf);
155  else
156    trace_printf (sd, cpu, "\n");
157}
158
159void
160trace_insn (SIM_CPU *cpu, const struct cgen_insn *opcode,
161	    const struct argbuf *abuf, IADDR pc)
162{
163  char disasm_buf[50];
164
165  printed_result_p = 0;
166  current_insn = opcode;
167  current_abuf = abuf;
168
169  if (CGEN_INSN_VIRTUAL_P (opcode))
170    {
171      trace_prefix (CPU_STATE (cpu), cpu, NULL_CIA, pc, 0,
172		    NULL, 0, CGEN_INSN_NAME (opcode));
173      return;
174    }
175
176  CPU_DISASSEMBLER (cpu) (cpu, opcode, abuf, pc, disasm_buf);
177  trace_prefix (CPU_STATE (cpu), cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu),
178		NULL, 0,
179		"%s%-*s",
180		first_insn_p ? " " : "|",
181		SIZE_INSTRUCTION, disasm_buf);
182}
183
184void
185trace_extract (SIM_CPU *cpu, IADDR pc, char *name, ...)
186{
187  va_list args;
188  int printed_one_p = 0;
189  char *fmt;
190
191  va_start (args, name);
192
193  trace_printf (CPU_STATE (cpu), cpu, "Extract: 0x%.*lx: %s ",
194		SIZE_PC, pc, name);
195
196  do {
197    int type,ival;
198
199    fmt = va_arg (args, char *);
200
201    if (fmt)
202      {
203	if (printed_one_p)
204	  trace_printf (CPU_STATE (cpu), cpu, ", ");
205	printed_one_p = 1;
206	type = va_arg (args, int);
207	switch (type)
208	  {
209	  case 'x' :
210	    ival = va_arg (args, int);
211	    trace_printf (CPU_STATE (cpu), cpu, fmt, ival);
212	    break;
213	  default :
214	    abort ();
215	  }
216      }
217  } while (fmt);
218
219  va_end (args);
220  trace_printf (CPU_STATE (cpu), cpu, "\n");
221}
222
223void
224trace_result (SIM_CPU *cpu, char *name, int type, ...)
225{
226  va_list args;
227
228  va_start (args, type);
229  if (printed_result_p)
230    cgen_trace_printf (cpu, ", ");
231
232  switch (type)
233    {
234    case 'x' :
235    default :
236      cgen_trace_printf (cpu, "%s <- 0x%x", name, va_arg (args, int));
237      break;
238    case 'f':
239      {
240	DI di;
241	sim_fpu f;
242
243	/* this is separated from previous line for sunos cc */
244	di = va_arg (args, DI);
245	sim_fpu_64to (&f, di);
246
247	cgen_trace_printf (cpu, "%s <- ", name);
248	sim_fpu_printn_fpu (&f, (sim_fpu_print_func *) cgen_trace_printf, 4, cpu);
249	break;
250      }
251    case 'D' :
252      {
253	DI di;
254	/* this is separated from previous line for sunos cc */
255	di = va_arg (args, DI);
256	cgen_trace_printf (cpu, "%s <- 0x%x%08x", name,
257			   GETHIDI(di), GETLODI (di));
258	break;
259      }
260    }
261
262  printed_result_p = 1;
263  va_end (args);
264}
265
266/* Print trace output to BUFPTR if active, otherwise print normally.
267   This is only for tracing semantic code.  */
268
269void
270cgen_trace_printf (SIM_CPU *cpu, char *fmt, ...)
271{
272  va_list args;
273
274  va_start (args, fmt);
275
276  if (bufptr == NULL)
277    {
278      if (TRACE_FILE (CPU_TRACE_DATA (cpu)) == NULL)
279	(* STATE_CALLBACK (CPU_STATE (cpu))->evprintf_filtered)
280	  (STATE_CALLBACK (CPU_STATE (cpu)), fmt, args);
281      else
282	vfprintf (TRACE_FILE (CPU_TRACE_DATA (cpu)), fmt, args);
283    }
284  else
285    {
286      vsprintf (bufptr, fmt, args);
287      bufptr += strlen (bufptr);
288      /* ??? Need version of SIM_ASSERT that is always enabled.  */
289      if (bufptr - trace_buf > SIZE_TRACE_BUF)
290	abort ();
291    }
292
293  va_end (args);
294}
295
296/* Disassembly support.  */
297
298/* sprintf to a "stream" */
299
300int
301sim_disasm_sprintf VPARAMS ((SFILE *f, const char *format, ...))
302{
303#ifndef __STDC__
304  SFILE *f;
305  const char *format;
306#endif
307  int n;
308  va_list args;
309
310  VA_START (args, format);
311#ifndef __STDC__
312  f = va_arg (args, SFILE *);
313  format = va_arg (args, char *);
314#endif
315  vsprintf (f->current, format, args);
316  f->current += n = strlen (f->current);
317  va_end (args);
318  return n;
319}
320
321/* Memory read support for an opcodes disassembler.  */
322
323int
324sim_disasm_read_memory (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length,
325			struct disassemble_info *info)
326{
327  SIM_CPU *cpu = (SIM_CPU *) info->application_data;
328  SIM_DESC sd = CPU_STATE (cpu);
329  unsigned length_read;
330
331  length_read = sim_core_read_buffer (sd, cpu, read_map, myaddr, memaddr,
332				      length);
333  if (length_read != length)
334    return EIO;
335  return 0;
336}
337
338/* Memory error support for an opcodes disassembler.  */
339
340void
341sim_disasm_perror_memory (int status, bfd_vma memaddr,
342			  struct disassemble_info *info)
343{
344  if (status != EIO)
345    /* Can't happen.  */
346    info->fprintf_func (info->stream, "Unknown error %d.", status);
347  else
348    /* Actually, address between memaddr and memaddr + len was
349       out of bounds.  */
350    info->fprintf_func (info->stream,
351			"Address 0x%x is out of bounds.",
352			(int) memaddr);
353}
354
355/* Disassemble using the CGEN opcode table.
356   ??? While executing an instruction, the insn has been decoded and all its
357   fields have been extracted.  It is certainly possible to do the disassembly
358   with that data.  This seems simpler, but maybe in the future the already
359   extracted fields will be used.  */
360
361void
362sim_cgen_disassemble_insn (SIM_CPU *cpu, const CGEN_INSN *insn,
363			   const ARGBUF *abuf, IADDR pc, char *buf)
364{
365  unsigned int length;
366  unsigned int base_length;
367  unsigned long insn_value;
368  struct disassemble_info disasm_info;
369  SFILE sfile;
370  union {
371    unsigned8 bytes[CGEN_MAX_INSN_SIZE];
372    unsigned16 shorts[8];
373    unsigned32 words[4];
374  } insn_buf;
375  SIM_DESC sd = CPU_STATE (cpu);
376  CGEN_CPU_DESC cd = CPU_CPU_DESC (cpu);
377  CGEN_EXTRACT_INFO ex_info;
378  CGEN_FIELDS *fields = alloca (CGEN_CPU_SIZEOF_FIELDS (cd));
379  int insn_bit_length = CGEN_INSN_BITSIZE (insn);
380  int insn_length = insn_bit_length / 8;
381
382  sfile.buffer = sfile.current = buf;
383  INIT_DISASSEMBLE_INFO (disasm_info, (FILE *) &sfile,
384			 (fprintf_ftype) sim_disasm_sprintf);
385  disasm_info.endian =
386    (bfd_big_endian (STATE_PROG_BFD (sd)) ? BFD_ENDIAN_BIG
387     : bfd_little_endian (STATE_PROG_BFD (sd)) ? BFD_ENDIAN_LITTLE
388     : BFD_ENDIAN_UNKNOWN);
389
390  length = sim_core_read_buffer (sd, cpu, read_map, &insn_buf, pc,
391				 insn_length);
392
393  if (length != insn_length)
394  {
395    sim_io_error (sd, "unable to read address %x", pc);
396  }
397
398  /* If the entire insn will fit into an integer, then do it. Otherwise, just
399     use the bits of the base_insn.  */
400  if (insn_bit_length <= 32)
401    base_length = insn_bit_length;
402  else
403    base_length = min (cd->base_insn_bitsize, insn_bit_length);
404  switch (base_length)
405    {
406    case 0 : return; /* fake insn, typically "compile" (aka "invalid") */
407    case 8 : insn_value = insn_buf.bytes[0]; break;
408    case 16 : insn_value = T2H_2 (insn_buf.shorts[0]); break;
409    case 32 : insn_value = T2H_4 (insn_buf.words[0]); break;
410    default: abort ();
411    }
412
413  disasm_info.buffer_vma = pc;
414  disasm_info.buffer = insn_buf.bytes;
415  disasm_info.buffer_length = length;
416
417  ex_info.dis_info = (PTR) &disasm_info;
418  ex_info.valid = (1 << length) - 1;
419  ex_info.insn_bytes = insn_buf.bytes;
420
421  length = (*CGEN_EXTRACT_FN (cd, insn)) (cd, insn, &ex_info, insn_value, fields, pc);
422  /* Result of extract fn is in bits.  */
423  /* ??? This assumes that each instruction has a fixed length (and thus
424     for insns with multiple versions of variable lengths they would each
425     have their own table entry).  */
426  if (length == insn_bit_length)
427    {
428      (*CGEN_PRINT_FN (cd, insn)) (cd, &disasm_info, insn, fields, pc, length);
429    }
430  else
431    {
432      /* This shouldn't happen, but aborting is too drastic.  */
433      strcpy (buf, "***unknown***");
434    }
435}
436