xref: /netbsd-src/external/gpl3/gdb/dist/sim/common/sim-profile.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
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 
6 This file is part of GDB, the GNU debugger.
7 
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along 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
40 static 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 
51 static MODULE_INIT_FN profile_init;
52 static MODULE_UNINSTALL_FN profile_uninstall;
53 
54 static DECLARE_OPTION_HANDLER (profile_option_handler);
55 
56 enum {
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 
70 static 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 
127 SIM_RC
128 set_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 
201 SIM_RC
202 sim_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 
207 static SIM_RC
208 parse_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 
254 static SIM_RC
255 profile_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 
467 static void
468 profile_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)))
480 static void
481 profile_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 
494 static void
495 profile_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 
512 static void
513 profile_pc_uninstall (SIM_DESC sd)
514 {
515   profile_pc_cleanup (sd);
516 }
517 
518 static void
519 profile_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 
542 static SIM_RC
543 profile_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 
625 static void
626 profile_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 
756 static SIM_RC
757 profile_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 
772 static void
773 profile_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 
845 static void
846 profile_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 
919 static void
920 profile_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 
976 static void
977 profile_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 
1013 void
1014 sim_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 
1027 static void
1028 profile_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 
1092 static void
1093 profile_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 
1124 static void
1125 profile_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 
1235 SIM_RC
1236 profile_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 
1258 static SIM_RC
1259 profile_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 
1287 static void
1288 profile_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