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