1/* Default profiling support.
2   Copyright (C) 1996, 1997, 1998, 2000, 2001, 2007, 2008, 2009, 2010, 2011
3   Free Software Foundation, Inc.
4   Contributed by Cygnus Support.
5
6This file is part of GDB, the GNU debugger.
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 3 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
20
21#include "sim-main.h"
22#include "sim-io.h"
23#include "sim-options.h"
24#include "sim-assert.h"
25
26#ifdef HAVE_STDLIB_H
27#include <stdlib.h>
28#endif
29
30#ifdef HAVE_STRING_H
31#include <string.h>
32#else
33#ifdef HAVE_STRINGS_H
34#include <strings.h>
35#endif
36#endif
37#include <ctype.h>
38
39#if !WITH_PROFILE_PC_P
40static unsigned int _profile_stub;
41# define PROFILE_PC_FREQ(p) _profile_stub
42# define PROFILE_PC_NR_BUCKETS(p) _profile_stub
43# define PROFILE_PC_SHIFT(p) _profile_stub
44# define PROFILE_PC_START(p) _profile_stub
45# define PROFILE_PC_END(p) _profile_stub
46# define PROFILE_INSN_COUNT(p) &_profile_stub
47#endif
48
49#define COMMAS(n) sim_add_commas (comma_buf, sizeof (comma_buf), (n))
50
51static MODULE_INIT_FN profile_init;
52static MODULE_UNINSTALL_FN profile_uninstall;
53
54static DECLARE_OPTION_HANDLER (profile_option_handler);
55
56enum {
57  OPTION_PROFILE_INSN = OPTION_START,
58  OPTION_PROFILE_MEMORY,
59  OPTION_PROFILE_MODEL,
60  OPTION_PROFILE_FILE,
61  OPTION_PROFILE_CORE,
62  OPTION_PROFILE_CPU_FREQUENCY,
63  OPTION_PROFILE_PC,
64  OPTION_PROFILE_PC_RANGE,
65  OPTION_PROFILE_PC_GRANULARITY,
66  OPTION_PROFILE_RANGE,
67  OPTION_PROFILE_FUNCTION
68};
69
70static const OPTION profile_options[] = {
71  { {"profile", optional_argument, NULL, 'p'},
72      'p', "on|off", "Perform profiling",
73      profile_option_handler, NULL },
74  { {"profile-insn", optional_argument, NULL, OPTION_PROFILE_INSN},
75      '\0', "on|off", "Perform instruction profiling",
76      profile_option_handler, NULL },
77  { {"profile-memory", optional_argument, NULL, OPTION_PROFILE_MEMORY},
78      '\0', "on|off", "Perform memory profiling",
79      profile_option_handler, NULL },
80  { {"profile-core", optional_argument, NULL, OPTION_PROFILE_CORE},
81      '\0', "on|off", "Perform CORE profiling",
82      profile_option_handler, NULL },
83  { {"profile-model", optional_argument, NULL, OPTION_PROFILE_MODEL},
84      '\0', "on|off", "Perform model profiling",
85      profile_option_handler, NULL },
86  { {"profile-cpu-frequency", required_argument, NULL,
87     OPTION_PROFILE_CPU_FREQUENCY},
88      '\0', "CPU FREQUENCY", "Specify the speed of the simulated cpu clock",
89      profile_option_handler, NULL },
90
91  { {"profile-file", required_argument, NULL, OPTION_PROFILE_FILE},
92      '\0', "FILE NAME", "Specify profile output file",
93      profile_option_handler, NULL },
94
95  { {"profile-pc", optional_argument, NULL, OPTION_PROFILE_PC},
96      '\0', "on|off", "Perform PC profiling",
97      profile_option_handler, NULL },
98  { {"profile-pc-frequency", required_argument, NULL, 'F'},
99      'F', "PC PROFILE FREQUENCY", "Specified PC profiling frequency",
100      profile_option_handler, NULL },
101  { {"profile-pc-size", required_argument, NULL, 'S'},
102      'S', "PC PROFILE SIZE", "Specify PC profiling size",
103      profile_option_handler, NULL },
104  { {"profile-pc-granularity", required_argument, NULL, OPTION_PROFILE_PC_GRANULARITY},
105      '\0', "PC PROFILE GRANULARITY", "Specify PC profiling sample coverage",
106      profile_option_handler, NULL },
107  { {"profile-pc-range", required_argument, NULL, OPTION_PROFILE_PC_RANGE},
108      '\0', "BASE,BOUND", "Specify PC profiling address range",
109      profile_option_handler, NULL },
110
111#ifdef SIM_HAVE_ADDR_RANGE
112  { {"profile-range", required_argument, NULL, OPTION_PROFILE_RANGE},
113      '\0', "START,END", "Specify range of addresses for instruction and model profiling",
114      profile_option_handler, NULL },
115#if 0 /*wip*/
116  { {"profile-function", required_argument, NULL, OPTION_PROFILE_FUNCTION},
117      '\0', "FUNCTION", "Specify function to profile",
118      profile_option_handler, NULL },
119#endif
120#endif
121
122  { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL }
123};
124
125/* Set/reset the profile options indicated in MASK.  */
126
127SIM_RC
128set_profile_option_mask (SIM_DESC sd, const char *name, int mask, const char *arg)
129{
130  int profile_nr;
131  int cpu_nr;
132  int profile_val = 1;
133
134  if (arg != NULL)
135    {
136      if (strcmp (arg, "yes") == 0
137	  || strcmp (arg, "on") == 0
138	  || strcmp (arg, "1") == 0)
139	profile_val = 1;
140      else if (strcmp (arg, "no") == 0
141	       || strcmp (arg, "off") == 0
142	       || strcmp (arg, "0") == 0)
143	profile_val = 0;
144      else
145	{
146	  sim_io_eprintf (sd, "Argument `%s' for `--profile%s' invalid, one of `on', `off', `yes', `no' expected\n", arg, name);
147	  return SIM_RC_FAIL;
148	}
149    }
150
151  /* update applicable profile bits */
152  for (profile_nr = 0; profile_nr < MAX_PROFILE_VALUES; ++profile_nr)
153    {
154      if ((mask & (1 << profile_nr)) == 0)
155	continue;
156
157#if 0 /* see sim-trace.c, set flags in STATE here if/when there are any */
158      /* Set non-cpu specific values.  */
159      switch (profile_nr)
160	{
161	case ??? :
162	  break;
163	}
164#endif
165
166      /* Set cpu values.  */
167      for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++)
168	{
169	  CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[profile_nr] = profile_val;
170	}
171    }
172
173  /* Re-compute the cpu profile summary.  */
174  if (profile_val)
175    {
176      for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++)
177	CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))->profile_any_p = 1;
178    }
179  else
180    {
181      for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; cpu_nr++)
182	{
183	  CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))->profile_any_p = 0;
184	  for (profile_nr = 0; profile_nr < MAX_PROFILE_VALUES; ++profile_nr)
185	    {
186	      if (CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[profile_nr])
187		{
188		  CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))->profile_any_p = 1;
189		  break;
190		}
191	    }
192	}
193    }
194
195  return SIM_RC_OK;
196}
197
198/* Set one profile option based on its IDX value.
199   Not static as cgen-scache.c uses it.  */
200
201SIM_RC
202sim_profile_set_option (SIM_DESC sd, const char *name, int idx, const char *arg)
203{
204  return set_profile_option_mask (sd, name, 1 << idx, arg);
205}
206
207static SIM_RC
208parse_frequency (SIM_DESC sd, const char *arg, unsigned long *freq)
209{
210  const char *ch;
211  /* First, parse a decimal number.  */
212  *freq = 0;
213  ch = arg;
214  if (isdigit (*arg))
215    {
216      for (/**/; *ch != '\0'; ++ch)
217	{
218	  if (! isdigit (*ch))
219	    break;
220	  *freq = *freq * 10 + (*ch - '0');
221	}
222
223      /* Accept KHz, MHz or Hz as a suffix.  */
224      if (tolower (*ch) == 'm')
225	{
226	  *freq *= 1000000;
227	  ++ch;
228	}
229      else if (tolower (*ch) == 'k')
230	{
231	  *freq *= 1000;
232	  ++ch;
233	}
234
235      if (tolower (*ch) == 'h')
236	{
237	  ++ch;
238	  if (tolower (*ch) == 'z')
239	    ++ch;
240	}
241    }
242
243  if (*ch != '\0')
244    {
245      sim_io_eprintf (sd, "Invalid argument for --profile-cpu-frequency: %s\n",
246		      arg);
247      *freq = 0;
248      return SIM_RC_FAIL;
249    }
250
251  return SIM_RC_OK;
252}
253
254static SIM_RC
255profile_option_handler (SIM_DESC sd,
256			sim_cpu *cpu,
257			int opt,
258			char *arg,
259			int is_command)
260{
261  int cpu_nr;
262
263  /* FIXME: Need to handle `cpu' arg.  */
264
265  switch (opt)
266    {
267    case 'p' :
268      if (! WITH_PROFILE)
269	sim_io_eprintf (sd, "Profiling not compiled in, `-p' ignored\n");
270      else
271	return set_profile_option_mask (sd, "profile", PROFILE_USEFUL_MASK,
272					arg);
273      break;
274
275    case OPTION_PROFILE_INSN :
276      if (WITH_PROFILE_INSN_P)
277	return sim_profile_set_option (sd, "-insn", PROFILE_INSN_IDX, arg);
278      else
279	sim_io_eprintf (sd, "Instruction profiling not compiled in, `--profile-insn' ignored\n");
280      break;
281
282    case OPTION_PROFILE_MEMORY :
283      if (WITH_PROFILE_MEMORY_P)
284	return sim_profile_set_option (sd, "-memory", PROFILE_MEMORY_IDX, arg);
285      else
286	sim_io_eprintf (sd, "Memory profiling not compiled in, `--profile-memory' ignored\n");
287      break;
288
289    case OPTION_PROFILE_CORE :
290      if (WITH_PROFILE_CORE_P)
291	return sim_profile_set_option (sd, "-core", PROFILE_CORE_IDX, arg);
292      else
293	sim_io_eprintf (sd, "CORE profiling not compiled in, `--profile-core' ignored\n");
294      break;
295
296    case OPTION_PROFILE_MODEL :
297      if (WITH_PROFILE_MODEL_P)
298	return sim_profile_set_option (sd, "-model", PROFILE_MODEL_IDX, arg);
299      else
300	sim_io_eprintf (sd, "Model profiling not compiled in, `--profile-model' ignored\n");
301      break;
302
303    case OPTION_PROFILE_CPU_FREQUENCY :
304      {
305	unsigned long val;
306	SIM_RC rc = parse_frequency (sd, arg, &val);
307	if (rc == SIM_RC_OK)
308	  {
309	    for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
310	      PROFILE_CPU_FREQ (CPU_PROFILE_DATA (STATE_CPU (sd,cpu_nr))) = val;
311	  }
312	return rc;
313      }
314
315    case OPTION_PROFILE_FILE :
316      /* FIXME: Might want this to apply to pc profiling only,
317	 or have two profile file options.  */
318      if (! WITH_PROFILE)
319	sim_io_eprintf (sd, "Profiling not compiled in, `--profile-file' ignored\n");
320      else
321	{
322	  FILE *f = fopen (arg, "w");
323
324	  if (f == NULL)
325	    {
326	      sim_io_eprintf (sd, "Unable to open profile output file `%s'\n", arg);
327	      return SIM_RC_FAIL;
328	    }
329	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
330	    PROFILE_FILE (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = f;
331	}
332      break;
333
334    case OPTION_PROFILE_PC:
335      if (WITH_PROFILE_PC_P)
336	return sim_profile_set_option (sd, "-pc", PROFILE_PC_IDX, arg);
337      else
338	sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc' ignored\n");
339      break;
340
341    case 'F' :
342      if (WITH_PROFILE_PC_P)
343	{
344	  /* FIXME: Validate arg.  */
345	  int val = atoi (arg);
346	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
347	    PROFILE_PC_FREQ (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = val;
348	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
349	    CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1;
350	}
351      else
352	sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-frequency' ignored\n");
353      break;
354
355    case 'S' :
356      if (WITH_PROFILE_PC_P)
357	{
358	  /* FIXME: Validate arg.  */
359	  int val = atoi (arg);
360	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
361	    PROFILE_PC_NR_BUCKETS (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = val;
362	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
363	    CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1;
364	}
365      else
366	sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-size' ignored\n");
367      break;
368
369    case OPTION_PROFILE_PC_GRANULARITY:
370      if (WITH_PROFILE_PC_P)
371	{
372	  int shift;
373	  int val = atoi (arg);
374	  /* check that the granularity is a power of two */
375	  shift = 0;
376	  while (val > (1 << shift))
377	    {
378	      shift += 1;
379	    }
380	  if (val != (1 << shift))
381	    {
382	      sim_io_eprintf (sd, "PC profiling granularity not a power of two\n");
383	      return SIM_RC_FAIL;
384	    }
385	  if (shift == 0)
386	    {
387	      sim_io_eprintf (sd, "PC profiling granularity too small");
388	      return SIM_RC_FAIL;
389	    }
390	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
391	    PROFILE_PC_SHIFT (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = shift;
392	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
393	    CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1;
394	}
395      else
396	sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-granularity' ignored\n");
397      break;
398
399    case OPTION_PROFILE_PC_RANGE:
400      if (WITH_PROFILE_PC_P)
401	{
402	  /* FIXME: Validate args */
403	  char *chp = arg;
404	  unsigned long base;
405	  unsigned long bound;
406	  base = strtoul (chp, &chp, 0);
407	  if (*chp != ',')
408	    {
409	      sim_io_eprintf (sd, "--profile-pc-range missing BOUND argument\n");
410	      return SIM_RC_FAIL;
411	    }
412	  bound = strtoul (chp + 1, NULL, 0);
413	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
414	    {
415	      PROFILE_PC_START (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = base;
416	      PROFILE_PC_END (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))) = bound;
417	    }
418	  for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
419	    CPU_PROFILE_FLAGS (STATE_CPU (sd, cpu_nr))[PROFILE_PC_IDX] = 1;
420	}
421      else
422	sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-range' ignored\n");
423      break;
424
425#ifdef SIM_HAVE_ADDR_RANGE
426    case OPTION_PROFILE_RANGE :
427      if (WITH_PROFILE)
428	{
429	  char *chp = arg;
430	  unsigned long start,end;
431	  start = strtoul (chp, &chp, 0);
432	  if (*chp != ',')
433	    {
434	      sim_io_eprintf (sd, "--profile-range missing END argument\n");
435	      return SIM_RC_FAIL;
436	    }
437	  end = strtoul (chp + 1, NULL, 0);
438	  /* FIXME: Argument validation.  */
439	  if (cpu != NULL)
440	    sim_addr_range_add (PROFILE_RANGE (CPU_PROFILE_DATA (cpu)),
441				start, end);
442	  else
443	    for (cpu_nr = 0; cpu_nr < MAX_NR_PROCESSORS; ++cpu_nr)
444	      sim_addr_range_add (PROFILE_RANGE (CPU_PROFILE_DATA (STATE_CPU (sd, cpu_nr))),
445				  start, end);
446	}
447      else
448	sim_io_eprintf (sd, "Profiling not compiled in, `--profile-range' ignored\n");
449      break;
450
451    case OPTION_PROFILE_FUNCTION :
452      if (WITH_PROFILE)
453	{
454	  /*wip: need to compute function range given name*/
455	}
456      else
457	sim_io_eprintf (sd, "Profiling not compiled in, `--profile-function' ignored\n");
458      break;
459#endif /* SIM_HAVE_ADDR_RANGE */
460    }
461
462  return SIM_RC_OK;
463}
464
465/* Profiling output hooks.  */
466
467static void
468profile_vprintf (SIM_DESC sd, sim_cpu *cpu, const char *fmt, va_list ap)
469{
470  FILE *fp = PROFILE_FILE (CPU_PROFILE_DATA (cpu));
471
472  /* If an output file was given, redirect output to that.  */
473  if (fp != NULL)
474    vfprintf (fp, fmt, ap);
475  else
476    sim_io_evprintf (sd, fmt, ap);
477}
478
479__attribute__ ((format (printf, 3, 4)))
480static void
481profile_printf (SIM_DESC sd, sim_cpu *cpu, const char *fmt, ...)
482{
483  va_list ap;
484
485  va_start (ap, fmt);
486  profile_vprintf (sd, cpu, fmt, ap);
487  va_end (ap);
488}
489
490/* PC profiling support */
491
492#if WITH_PROFILE_PC_P
493
494static void
495profile_pc_cleanup (SIM_DESC sd)
496{
497  int n;
498  for (n = 0; n < MAX_NR_PROCESSORS; n++)
499    {
500      sim_cpu *cpu = STATE_CPU (sd, n);
501      PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
502      if (PROFILE_PC_COUNT (data) != NULL)
503	free (PROFILE_PC_COUNT (data));
504      PROFILE_PC_COUNT (data) = NULL;
505      if (PROFILE_PC_EVENT (data) != NULL)
506	sim_events_deschedule (sd, PROFILE_PC_EVENT (data));
507      PROFILE_PC_EVENT (data) = NULL;
508    }
509}
510
511
512static void
513profile_pc_uninstall (SIM_DESC sd)
514{
515  profile_pc_cleanup (sd);
516}
517
518static void
519profile_pc_event (SIM_DESC sd,
520		  void *data)
521{
522  sim_cpu *cpu = (sim_cpu*) data;
523  PROFILE_DATA *profile = CPU_PROFILE_DATA (cpu);
524  address_word pc;
525  unsigned i;
526  switch (STATE_WATCHPOINTS (sd)->sizeof_pc)
527    {
528    case 2: pc = *(unsigned_2*)(STATE_WATCHPOINTS (sd)->pc) ; break;
529    case 4: pc = *(unsigned_4*)(STATE_WATCHPOINTS (sd)->pc) ; break;
530    case 8: pc = *(unsigned_8*)(STATE_WATCHPOINTS (sd)->pc) ; break;
531    default: pc = 0;
532    }
533  i = (pc - PROFILE_PC_START (profile)) >> PROFILE_PC_SHIFT (profile);
534  if (i < PROFILE_PC_NR_BUCKETS (profile))
535    PROFILE_PC_COUNT (profile) [i] += 1; /* Overflow? */
536  else
537    PROFILE_PC_COUNT (profile) [PROFILE_PC_NR_BUCKETS (profile)] += 1;
538  PROFILE_PC_EVENT (profile) =
539    sim_events_schedule (sd, PROFILE_PC_FREQ (profile), profile_pc_event, cpu);
540}
541
542static SIM_RC
543profile_pc_init (SIM_DESC sd)
544{
545  int n;
546  profile_pc_cleanup (sd);
547  for (n = 0; n < MAX_NR_PROCESSORS; n++)
548    {
549      sim_cpu *cpu = STATE_CPU (sd, n);
550      PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
551      if (CPU_PROFILE_FLAGS (STATE_CPU (sd, n))[PROFILE_PC_IDX]
552	  && STATE_WATCHPOINTS (sd)->pc != NULL)
553	{
554	  int bucket_size;
555	  /* fill in the frequency if not specified */
556	  if (PROFILE_PC_FREQ (data) == 0)
557	    PROFILE_PC_FREQ (data) = 257;
558	  /* fill in the start/end if not specified */
559	  if (PROFILE_PC_END (data) == 0)
560	    {
561	      PROFILE_PC_START (data) = STATE_TEXT_START (sd);
562	      PROFILE_PC_END (data) = STATE_TEXT_END (sd);
563	    }
564	  /* Compute the number of buckets if not specified. */
565	  if (PROFILE_PC_NR_BUCKETS (data) == 0)
566	    {
567	      if (PROFILE_PC_BUCKET_SIZE (data) == 0)
568		PROFILE_PC_NR_BUCKETS (data) = 16;
569	      else
570		{
571		  if (PROFILE_PC_END (data) == 0)
572		    {
573		      /* nr_buckets = (full-address-range / 2) / (bucket_size / 2) */
574		      PROFILE_PC_NR_BUCKETS (data) =
575			((1 << (STATE_WATCHPOINTS (sd)->sizeof_pc) * (8 - 1))
576			 / (PROFILE_PC_BUCKET_SIZE (data) / 2));
577		    }
578		  else
579		    {
580		      PROFILE_PC_NR_BUCKETS (data) =
581			((PROFILE_PC_END (data)
582			  - PROFILE_PC_START (data)
583			  + PROFILE_PC_BUCKET_SIZE (data) - 1)
584			 / PROFILE_PC_BUCKET_SIZE (data));
585		    }
586		}
587	    }
588	  /* Compute the bucket size if not specified.  Ensure that it
589             is rounded up to the next power of two */
590	  if (PROFILE_PC_BUCKET_SIZE (data) == 0)
591	    {
592	      if (PROFILE_PC_END (data) == 0)
593		/* bucket_size = (full-address-range / 2) / (nr_buckets / 2) */
594		bucket_size = ((1 << ((STATE_WATCHPOINTS (sd)->sizeof_pc * 8) - 1))
595			       / (PROFILE_PC_NR_BUCKETS (data) / 2));
596	      else
597		bucket_size = ((PROFILE_PC_END (data)
598				- PROFILE_PC_START (data)
599				+ PROFILE_PC_NR_BUCKETS (data) - 1)
600			       / PROFILE_PC_NR_BUCKETS (data));
601	      PROFILE_PC_SHIFT (data) = 0;
602	      while (bucket_size > PROFILE_PC_BUCKET_SIZE (data))
603		{
604		  PROFILE_PC_SHIFT (data) += 1;
605		}
606	    }
607	  /* Align the end address with bucket size */
608	  if (PROFILE_PC_END (data) != 0)
609	    PROFILE_PC_END (data) = (PROFILE_PC_START (data)
610				     + (PROFILE_PC_BUCKET_SIZE (data)
611					* PROFILE_PC_NR_BUCKETS (data)));
612	  /* create the relevant buffers */
613	  PROFILE_PC_COUNT (data) =
614	    NZALLOC (unsigned, PROFILE_PC_NR_BUCKETS (data) + 1);
615	  PROFILE_PC_EVENT (data) =
616	    sim_events_schedule (sd,
617				 PROFILE_PC_FREQ (data),
618				 profile_pc_event,
619				 cpu);
620	}
621    }
622  return SIM_RC_OK;
623}
624
625static void
626profile_print_pc (sim_cpu *cpu, int verbose)
627{
628  SIM_DESC sd = CPU_STATE (cpu);
629  PROFILE_DATA *profile = CPU_PROFILE_DATA (cpu);
630  char comma_buf[20];
631  unsigned max_val;
632  unsigned total;
633  unsigned i;
634
635  if (PROFILE_PC_COUNT (profile) == 0)
636    return;
637
638  profile_printf (sd, cpu, "Program Counter Statistics:\n\n");
639
640  /* First pass over data computes various things.  */
641  max_val = 0;
642  total = 0;
643  for (i = 0; i <= PROFILE_PC_NR_BUCKETS (profile); ++i)
644    {
645      total += PROFILE_PC_COUNT (profile) [i];
646      if (PROFILE_PC_COUNT (profile) [i] > max_val)
647	max_val = PROFILE_PC_COUNT (profile) [i];
648    }
649
650  profile_printf (sd, cpu, "  Total samples: %s\n",
651		  COMMAS (total));
652  profile_printf (sd, cpu, "  Granularity: %s bytes per bucket\n",
653		  COMMAS (PROFILE_PC_BUCKET_SIZE (profile)));
654  profile_printf (sd, cpu, "  Size: %s buckets\n",
655		  COMMAS (PROFILE_PC_NR_BUCKETS (profile)));
656  profile_printf (sd, cpu, "  Frequency: %s cycles per sample\n",
657		  COMMAS (PROFILE_PC_FREQ (profile)));
658
659  if (PROFILE_PC_END (profile) != 0)
660    profile_printf (sd, cpu, "  Range: 0x%lx 0x%lx\n",
661		    (long) PROFILE_PC_START (profile),
662		   (long) PROFILE_PC_END (profile));
663
664  if (verbose && max_val != 0)
665    {
666      /* Now we can print the histogram.  */
667      profile_printf (sd, cpu, "\n");
668      for (i = 0; i <= PROFILE_PC_NR_BUCKETS (profile); ++i)
669	{
670	  if (PROFILE_PC_COUNT (profile) [i] != 0)
671	    {
672	      profile_printf (sd, cpu, "  ");
673	      if (i == PROFILE_PC_NR_BUCKETS (profile))
674		profile_printf (sd, cpu, "%10s:", "overflow");
675	      else
676		profile_printf (sd, cpu, "0x%08lx:",
677				(long) (PROFILE_PC_START (profile)
678					+ (i * PROFILE_PC_BUCKET_SIZE (profile))));
679	      profile_printf (sd, cpu, " %*s",
680			      max_val < 10000 ? 5 : 10,
681			      COMMAS (PROFILE_PC_COUNT (profile) [i]));
682	      profile_printf (sd, cpu, " %4.1f",
683			      (PROFILE_PC_COUNT (profile) [i] * 100.0) / total);
684	      profile_printf (sd, cpu, ": ");
685	      sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH,
686				     PROFILE_PC_COUNT (profile) [i],
687				     max_val);
688	      profile_printf (sd, cpu, "\n");
689	    }
690	}
691    }
692
693  /* dump the histogram to the file "gmon.out" using BSD's gprof file
694     format */
695  /* Since a profile data file is in the native format of the host on
696     which the profile is being, endian issues are not considered in
697     the code below. */
698  /* FIXME: Is this the best place for this code? */
699  {
700    FILE *pf = fopen ("gmon.out", "wb");
701
702    if (pf == NULL)
703      sim_io_eprintf (sd, "Failed to open \"gmon.out\" profile file\n");
704    else
705      {
706	int ok;
707	/* FIXME: what if the target has a 64 bit PC? */
708	unsigned32 header[3];
709	unsigned loop;
710	if (PROFILE_PC_END (profile) != 0)
711	  {
712	    header[0] = PROFILE_PC_START (profile);
713	    header[1] = PROFILE_PC_END (profile);
714	  }
715	else
716	  {
717	    header[0] = 0;
718	    header[1] = 0;
719	  }
720	/* size of sample buffer (+ header) */
721	header[2] = PROFILE_PC_NR_BUCKETS (profile) * 2 + sizeof (header);
722
723	/* Header must be written out in target byte order.  */
724	H2T (header[0]);
725	H2T (header[1]);
726	H2T (header[2]);
727
728	ok = fwrite (&header, sizeof (header), 1, pf);
729	for (loop = 0;
730	     ok && (loop < PROFILE_PC_NR_BUCKETS (profile));
731	     loop++)
732	  {
733	    signed16 sample;
734	    if (PROFILE_PC_COUNT (profile) [loop] >= 0xffff)
735	      sample = 0xffff;
736	    else
737	      sample = PROFILE_PC_COUNT (profile) [loop];
738 	    H2T (sample);
739	    ok = fwrite (&sample, sizeof (sample), 1, pf);
740	  }
741	if (ok == 0)
742	  sim_io_eprintf (sd, "Failed to write to \"gmon.out\" profile file\n");
743	fclose(pf);
744      }
745  }
746
747  profile_printf (sd, cpu, "\n");
748}
749
750#endif
751
752/* Summary printing support.  */
753
754#if WITH_PROFILE_INSN_P
755
756static SIM_RC
757profile_insn_init (SIM_DESC sd)
758{
759  int c;
760
761  for (c = 0; c < MAX_NR_PROCESSORS; ++c)
762    {
763      sim_cpu *cpu = STATE_CPU (sd, c);
764
765      if (CPU_MAX_INSNS (cpu) > 0)
766	PROFILE_INSN_COUNT (CPU_PROFILE_DATA (cpu)) = NZALLOC (unsigned int, CPU_MAX_INSNS (cpu));
767    }
768
769  return SIM_RC_OK;
770}
771
772static void
773profile_print_insn (sim_cpu *cpu, int verbose)
774{
775  unsigned int i, n, total, max_val, max_name_len;
776  SIM_DESC sd = CPU_STATE (cpu);
777  PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
778  char comma_buf[20];
779
780  /* If MAX_INSNS not set, insn profiling isn't supported.  */
781  if (CPU_MAX_INSNS (cpu) == 0)
782    return;
783
784  profile_printf (sd, cpu, "Instruction Statistics");
785#ifdef SIM_HAVE_ADDR_RANGE
786  if (PROFILE_RANGE (data)->ranges)
787    profile_printf (sd, cpu, " (for selected address range(s))");
788#endif
789  profile_printf (sd, cpu, "\n\n");
790
791  /* First pass over data computes various things.  */
792  max_val = 0;
793  total = 0;
794  max_name_len = 0;
795  for (i = 0; i < CPU_MAX_INSNS (cpu); ++i)
796    {
797      const char *name = (*CPU_INSN_NAME (cpu)) (cpu, i);
798
799      if (name == NULL)
800	continue;
801      total += PROFILE_INSN_COUNT (data) [i];
802      if (PROFILE_INSN_COUNT (data) [i] > max_val)
803	max_val = PROFILE_INSN_COUNT (data) [i];
804      n = strlen (name);
805      if (n > max_name_len)
806	max_name_len = n;
807    }
808  /* set the total insn count, in case client is being lazy */
809  if (! PROFILE_TOTAL_INSN_COUNT (data))
810    PROFILE_TOTAL_INSN_COUNT (data) = total;
811
812  profile_printf (sd, cpu, "  Total: %s insns\n", COMMAS (total));
813
814  if (verbose && max_val != 0)
815    {
816      /* Now we can print the histogram.  */
817      profile_printf (sd, cpu, "\n");
818      for (i = 0; i < CPU_MAX_INSNS (cpu); ++i)
819	{
820	  const char *name = (*CPU_INSN_NAME (cpu)) (cpu, i);
821
822	  if (name == NULL)
823	    continue;
824	  if (PROFILE_INSN_COUNT (data) [i] != 0)
825	    {
826	      profile_printf (sd, cpu, "   %*s: %*s: ",
827			      max_name_len, name,
828			      max_val < 10000 ? 5 : 10,
829			      COMMAS (PROFILE_INSN_COUNT (data) [i]));
830	      sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH,
831				     PROFILE_INSN_COUNT (data) [i],
832				     max_val);
833	      profile_printf (sd, cpu, "\n");
834	    }
835	}
836    }
837
838  profile_printf (sd, cpu, "\n");
839}
840
841#endif
842
843#if WITH_PROFILE_MEMORY_P
844
845static void
846profile_print_memory (sim_cpu *cpu, int verbose)
847{
848  unsigned int i, n;
849  unsigned int total_read, total_write;
850  unsigned int max_val, max_name_len;
851  /* FIXME: Need to add smp support.  */
852  SIM_DESC sd = CPU_STATE (cpu);
853  PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
854  char comma_buf[20];
855
856  profile_printf (sd, cpu, "Memory Access Statistics\n\n");
857
858  /* First pass over data computes various things.  */
859  max_val = total_read = total_write = max_name_len = 0;
860  for (i = 0; i < MODE_TARGET_MAX; ++i)
861    {
862      total_read += PROFILE_READ_COUNT (data) [i];
863      total_write += PROFILE_WRITE_COUNT (data) [i];
864      if (PROFILE_READ_COUNT (data) [i] > max_val)
865	max_val = PROFILE_READ_COUNT (data) [i];
866      if (PROFILE_WRITE_COUNT (data) [i] > max_val)
867	max_val = PROFILE_WRITE_COUNT (data) [i];
868      n = strlen (MODE_NAME (i));
869      if (n > max_name_len)
870	max_name_len = n;
871    }
872
873  /* One could use PROFILE_LABEL_WIDTH here.  I chose not to.  */
874  profile_printf (sd, cpu, "  Total read:  %s accesses\n",
875		  COMMAS (total_read));
876  profile_printf (sd, cpu, "  Total write: %s accesses\n",
877		  COMMAS (total_write));
878
879  if (verbose && max_val != 0)
880    {
881      /* FIXME: Need to separate instruction fetches from data fetches
882	 as the former swamps the latter.  */
883      /* Now we can print the histogram.  */
884      profile_printf (sd, cpu, "\n");
885      for (i = 0; i < MODE_TARGET_MAX; ++i)
886	{
887	  if (PROFILE_READ_COUNT (data) [i] != 0)
888	    {
889	      profile_printf (sd, cpu, "   %*s read:  %*s: ",
890			      max_name_len, MODE_NAME (i),
891			      max_val < 10000 ? 5 : 10,
892			      COMMAS (PROFILE_READ_COUNT (data) [i]));
893	      sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH,
894				     PROFILE_READ_COUNT (data) [i],
895				     max_val);
896	      profile_printf (sd, cpu, "\n");
897	    }
898	  if (PROFILE_WRITE_COUNT (data) [i] != 0)
899	    {
900	      profile_printf (sd, cpu, "   %*s write: %*s: ",
901			      max_name_len, MODE_NAME (i),
902			      max_val < 10000 ? 5 : 10,
903			      COMMAS (PROFILE_WRITE_COUNT (data) [i]));
904	      sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH,
905				     PROFILE_WRITE_COUNT (data) [i],
906				     max_val);
907	      profile_printf (sd, cpu, "\n");
908	    }
909	}
910    }
911
912  profile_printf (sd, cpu, "\n");
913}
914
915#endif
916
917#if WITH_PROFILE_CORE_P
918
919static void
920profile_print_core (sim_cpu *cpu, int verbose)
921{
922  unsigned int total;
923  unsigned int max_val;
924  /* FIXME: Need to add smp support.  */
925  SIM_DESC sd = CPU_STATE (cpu);
926  PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
927  char comma_buf[20];
928
929  profile_printf (sd, cpu, "CORE Statistics\n\n");
930
931  /* First pass over data computes various things.  */
932  {
933    unsigned map;
934    total = 0;
935    max_val = 0;
936    for (map = 0; map < nr_maps; map++)
937      {
938	total += PROFILE_CORE_COUNT (data) [map];
939	if (PROFILE_CORE_COUNT (data) [map] > max_val)
940	  max_val = PROFILE_CORE_COUNT (data) [map];
941      }
942  }
943
944  /* One could use PROFILE_LABEL_WIDTH here.  I chose not to.  */
945  profile_printf (sd, cpu, "  Total:  %s accesses\n",
946		  COMMAS (total));
947
948  if (verbose && max_val != 0)
949    {
950      unsigned map;
951      /* Now we can print the histogram.  */
952      profile_printf (sd, cpu, "\n");
953      for (map = 0; map < nr_maps; map++)
954	{
955	  if (PROFILE_CORE_COUNT (data) [map] != 0)
956	    {
957	      profile_printf (sd, cpu, "%10s:", map_to_str (map));
958	      profile_printf (sd, cpu, "%*s: ",
959			      max_val < 10000 ? 5 : 10,
960			      COMMAS (PROFILE_CORE_COUNT (data) [map]));
961	      sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH,
962				     PROFILE_CORE_COUNT (data) [map],
963				     max_val);
964	      profile_printf (sd, cpu, "\n");
965	    }
966	}
967    }
968
969  profile_printf (sd, cpu, "\n");
970}
971
972#endif
973
974#if WITH_PROFILE_MODEL_P
975
976static void
977profile_print_model (sim_cpu *cpu, int verbose)
978{
979  SIM_DESC sd = CPU_STATE (cpu);
980  PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
981  unsigned long cti_stall_cycles = PROFILE_MODEL_CTI_STALL_CYCLES (data);
982  unsigned long load_stall_cycles = PROFILE_MODEL_LOAD_STALL_CYCLES (data);
983  unsigned long total_cycles = PROFILE_MODEL_TOTAL_CYCLES (data);
984  char comma_buf[20];
985
986  profile_printf (sd, cpu, "Model %s Timing Information",
987		  MODEL_NAME (CPU_MODEL (cpu)));
988#ifdef SIM_HAVE_ADDR_RANGE
989  if (PROFILE_RANGE (data)->ranges)
990    profile_printf (sd, cpu, " (for selected address range(s))");
991#endif
992  profile_printf (sd, cpu, "\n\n");
993  profile_printf (sd, cpu, "  %-*s %s\n",
994		  PROFILE_LABEL_WIDTH, "Taken branches:",
995		  COMMAS (PROFILE_MODEL_TAKEN_COUNT (data)));
996  profile_printf (sd, cpu, "  %-*s %s\n",
997		  PROFILE_LABEL_WIDTH, "Untaken branches:",
998		  COMMAS (PROFILE_MODEL_UNTAKEN_COUNT (data)));
999  profile_printf (sd, cpu, "  %-*s %s\n",
1000		  PROFILE_LABEL_WIDTH, "Cycles stalled due to branches:",
1001		  COMMAS (cti_stall_cycles));
1002  profile_printf (sd, cpu, "  %-*s %s\n",
1003		  PROFILE_LABEL_WIDTH, "Cycles stalled due to loads:",
1004		  COMMAS (load_stall_cycles));
1005  profile_printf (sd, cpu, "  %-*s %s\n",
1006		  PROFILE_LABEL_WIDTH, "Total cycles (*approximate*):",
1007		  COMMAS (total_cycles));
1008  profile_printf (sd, cpu, "\n");
1009}
1010
1011#endif
1012
1013void
1014sim_profile_print_bar (SIM_DESC sd, sim_cpu *cpu, unsigned int width,
1015		       unsigned int val, unsigned int max_val)
1016{
1017  unsigned int i, count;
1018
1019  count = ((double) val / (double) max_val) * (double) width;
1020
1021  for (i = 0; i < count; ++i)
1022    profile_printf (sd, cpu, "*");
1023}
1024
1025/* Print the simulator's execution speed for CPU.  */
1026
1027static void
1028profile_print_speed (sim_cpu *cpu)
1029{
1030  SIM_DESC sd = CPU_STATE (cpu);
1031  PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
1032  unsigned long milliseconds = sim_events_elapsed_time (sd);
1033  unsigned long total = PROFILE_TOTAL_INSN_COUNT (data);
1034  double clock;
1035  double secs;
1036  char comma_buf[20];
1037
1038  profile_printf (sd, cpu, "Simulator Execution Speed\n\n");
1039
1040  if (total != 0)
1041    profile_printf (sd, cpu, "  Total instructions:      %s\n", COMMAS (total));
1042
1043  if (milliseconds < 1000)
1044    profile_printf (sd, cpu, "  Total execution time:    < 1 second\n\n");
1045  else
1046    {
1047      /* The printing of the time rounded to 2 decimal places makes the speed
1048	 calculation seem incorrect [even though it is correct].  So round
1049	 MILLISECONDS first. This can marginally affect the result, but it's
1050	 better that the user not perceive there's a math error.  */
1051      secs = (double) milliseconds / 1000;
1052      secs = ((double) (unsigned long) (secs * 100 + .5)) / 100;
1053      profile_printf (sd, cpu, "  Total execution time   : %.2f seconds\n", secs);
1054      /* Don't confuse things with data that isn't useful.
1055	 If we ran for less than 2 seconds, only use the data if we
1056	 executed more than 100,000 insns.  */
1057      if (secs >= 2 || total >= 100000)
1058	profile_printf (sd, cpu, "  Simulator speed:         %s insns/second\n",
1059			COMMAS ((unsigned long) ((double) total / secs)));
1060    }
1061
1062  /* Print simulated execution time if the cpu frequency has been specified.  */
1063  clock = PROFILE_CPU_FREQ (data);
1064  if (clock != 0)
1065    {
1066      if (clock >= 1000000)
1067	profile_printf (sd, cpu, "  Simulated cpu frequency: %.2f MHz\n",
1068			clock / 1000000);
1069      else
1070	profile_printf (sd, cpu, "  Simulated cpu frequency: %.2f Hz\n", clock);
1071
1072#if WITH_PROFILE_MODEL_P
1073      if (PROFILE_FLAGS (data) [PROFILE_MODEL_IDX])
1074	{
1075	  /* The printing of the time rounded to 2 decimal places makes the
1076	     speed calculation seem incorrect [even though it is correct].
1077	     So round 	 SECS first. This can marginally affect the result,
1078	     but it's 	 better that the user not perceive there's a math
1079	     error.  */
1080	  secs = PROFILE_MODEL_TOTAL_CYCLES (data) / clock;
1081	  secs = ((double) (unsigned long) (secs * 100 + .5)) / 100;
1082	  profile_printf (sd, cpu, "  Simulated execution time: %.2f seconds\n",
1083			  secs);
1084	}
1085#endif /* WITH_PROFILE_MODEL_P */
1086    }
1087}
1088
1089#ifdef SIM_HAVE_ADDR_RANGE
1090/* Print selected address ranges.  */
1091
1092static void
1093profile_print_addr_ranges (sim_cpu *cpu)
1094{
1095  ADDR_SUBRANGE *asr = PROFILE_RANGE (CPU_PROFILE_DATA (cpu))->ranges;
1096  SIM_DESC sd = CPU_STATE (cpu);
1097
1098  if (asr)
1099    {
1100      profile_printf (sd, cpu, "Selected address ranges\n\n");
1101      while (asr != NULL)
1102	{
1103	  profile_printf (sd, cpu, "  0x%lx - 0x%lx\n",
1104			  (long) asr->start, (long) asr->end);
1105	  asr = asr->next;
1106	}
1107      profile_printf (sd, cpu, "\n");
1108    }
1109}
1110#endif
1111
1112/* Top level function to print all summary profile information.
1113   It is [currently] intended that all such data is printed by this function.
1114   I'd rather keep it all in one place for now.  To that end, MISC_CPU and
1115   MISC are callbacks used to print any miscellaneous data.
1116
1117   One might want to add a user option that allows printing by type or by cpu
1118   (i.e. print all insn data for each cpu first, or print data cpu by cpu).
1119   This may be a case of featuritis so it's currently left out.
1120
1121   Note that results are indented two spaces to distinguish them from
1122   section titles.  */
1123
1124static void
1125profile_info (SIM_DESC sd, int verbose)
1126{
1127  int i,c;
1128  int print_title_p = 0;
1129
1130  /* Only print the title if some data has been collected.  */
1131  /* ??? Why don't we just exit if no data collected?  */
1132  /* FIXME: If the number of processors can be selected on the command line,
1133     then MAX_NR_PROCESSORS will need to take an argument of `sd'.  */
1134
1135  for (c = 0; c < MAX_NR_PROCESSORS; ++c)
1136    {
1137      sim_cpu *cpu = STATE_CPU (sd, c);
1138      PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
1139
1140      for (i = 0; i < MAX_PROFILE_VALUES; ++i)
1141	if (PROFILE_FLAGS (data) [i])
1142	  {
1143	    profile_printf (sd, cpu, "Summary profiling results:\n\n");
1144	    print_title_p = 1;
1145	  }
1146    }
1147
1148  /* Loop, cpu by cpu, printing results.  */
1149
1150  for (c = 0; c < MAX_NR_PROCESSORS; ++c)
1151    {
1152      sim_cpu *cpu = STATE_CPU (sd, c);
1153      PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
1154
1155      if (MAX_NR_PROCESSORS > 1
1156	  && (0
1157#if WITH_PROFILE_INSN_P
1158	      || PROFILE_FLAGS (data) [PROFILE_INSN_IDX]
1159#endif
1160#if WITH_PROFILE_MEMORY_P
1161	      || PROFILE_FLAGS (data) [PROFILE_MEMORY_IDX]
1162#endif
1163#if WITH_PROFILE_CORE_P
1164	      || PROFILE_FLAGS (data) [PROFILE_CORE_IDX]
1165#endif
1166#if WITH_PROFILE_MODEL_P
1167	      || PROFILE_FLAGS (data) [PROFILE_MODEL_IDX]
1168#endif
1169#if WITH_PROFILE_SCACHE_P && WITH_SCACHE
1170	      || PROFILE_FLAGS (data) [PROFILE_SCACHE_IDX]
1171#endif
1172#if WITH_PROFILE_PC_P
1173	      || PROFILE_FLAGS (data) [PROFILE_PC_IDX]
1174#endif
1175	      ))
1176	{
1177	  profile_printf (sd, cpu, "CPU %d\n\n", c);
1178	}
1179
1180#ifdef SIM_HAVE_ADDR_RANGE
1181      if (print_title_p
1182	  && (PROFILE_INSN_P (cpu)
1183	      || PROFILE_MODEL_P (cpu)))
1184	profile_print_addr_ranges (cpu);
1185#endif
1186
1187#if WITH_PROFILE_INSN_P
1188      if (PROFILE_FLAGS (data) [PROFILE_INSN_IDX])
1189	profile_print_insn (cpu, verbose);
1190#endif
1191
1192#if WITH_PROFILE_MEMORY_P
1193      if (PROFILE_FLAGS (data) [PROFILE_MEMORY_IDX])
1194	profile_print_memory (cpu, verbose);
1195#endif
1196
1197#if WITH_PROFILE_CORE_P
1198      if (PROFILE_FLAGS (data) [PROFILE_CORE_IDX])
1199	profile_print_core (cpu, verbose);
1200#endif
1201
1202#if WITH_PROFILE_MODEL_P
1203      if (PROFILE_FLAGS (data) [PROFILE_MODEL_IDX])
1204	profile_print_model (cpu, verbose);
1205#endif
1206
1207#if WITH_PROFILE_SCACHE_P && WITH_SCACHE
1208      if (PROFILE_FLAGS (data) [PROFILE_SCACHE_IDX])
1209	scache_print_profile (cpu, verbose);
1210#endif
1211
1212#if WITH_PROFILE_PC_P
1213      if (PROFILE_FLAGS (data) [PROFILE_PC_IDX])
1214	profile_print_pc (cpu, verbose);
1215#endif
1216
1217      /* Print cpu-specific data before the execution speed.  */
1218      if (PROFILE_INFO_CPU_CALLBACK (data) != NULL)
1219	PROFILE_INFO_CPU_CALLBACK (data) (cpu, verbose);
1220
1221      /* Always try to print execution time and speed.  */
1222      if (verbose
1223	  || PROFILE_FLAGS (data) [PROFILE_INSN_IDX])
1224	profile_print_speed (cpu);
1225    }
1226
1227  /* Finally print non-cpu specific miscellaneous data.  */
1228  if (STATE_PROFILE_INFO_CALLBACK (sd))
1229    STATE_PROFILE_INFO_CALLBACK (sd) (sd, verbose);
1230
1231}
1232
1233/* Install profiling support in the simulator.  */
1234
1235SIM_RC
1236profile_install (SIM_DESC sd)
1237{
1238  int i;
1239
1240  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
1241  sim_add_option_table (sd, NULL, profile_options);
1242  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
1243    memset (CPU_PROFILE_DATA (STATE_CPU (sd, i)), 0,
1244	    sizeof (* CPU_PROFILE_DATA (STATE_CPU (sd, i))));
1245#if WITH_PROFILE_INSN_P
1246  sim_module_add_init_fn (sd, profile_insn_init);
1247#endif
1248#if WITH_PROFILE_PC_P
1249  sim_module_add_uninstall_fn (sd, profile_pc_uninstall);
1250  sim_module_add_init_fn (sd, profile_pc_init);
1251#endif
1252  sim_module_add_init_fn (sd, profile_init);
1253  sim_module_add_uninstall_fn (sd, profile_uninstall);
1254  sim_module_add_info_fn (sd, profile_info);
1255  return SIM_RC_OK;
1256}
1257
1258static SIM_RC
1259profile_init (SIM_DESC sd)
1260{
1261#ifdef SIM_HAVE_ADDR_RANGE
1262  /* Check if a range has been specified without specifying what to
1263     collect.  */
1264  {
1265    int i;
1266
1267    for (i = 0; i < MAX_NR_PROCESSORS; ++i)
1268      {
1269	sim_cpu *cpu = STATE_CPU (sd, i);
1270
1271	if (ADDR_RANGE_RANGES (PROFILE_RANGE (CPU_PROFILE_DATA (cpu)))
1272	    && ! (PROFILE_INSN_P (cpu)
1273		  || PROFILE_MODEL_P (cpu)))
1274	  {
1275	    sim_io_eprintf_cpu (cpu, "Profiling address range specified without --profile-insn or --profile-model.\n");
1276	    sim_io_eprintf_cpu (cpu, "Address range ignored.\n");
1277	    sim_addr_range_delete (PROFILE_RANGE (CPU_PROFILE_DATA (cpu)),
1278				   0, ~ (address_word) 0);
1279	  }
1280      }
1281  }
1282#endif
1283
1284  return SIM_RC_OK;
1285}
1286
1287static void
1288profile_uninstall (SIM_DESC sd)
1289{
1290  int i,j;
1291
1292  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
1293    {
1294      sim_cpu *cpu = STATE_CPU (sd, i);
1295      PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
1296
1297      if (PROFILE_FILE (data) != NULL)
1298	{
1299	  /* If output from different cpus is going to the same file,
1300	     avoid closing the file twice.  */
1301	  for (j = 0; j < i; ++j)
1302	    if (PROFILE_FILE (CPU_PROFILE_DATA (STATE_CPU (sd, j)))
1303		== PROFILE_FILE (data))
1304	      break;
1305	  if (i == j)
1306	    fclose (PROFILE_FILE (data));
1307	}
1308
1309      if (PROFILE_INSN_COUNT (data) != NULL)
1310	free (PROFILE_INSN_COUNT (data));
1311    }
1312}
1313