Release 2.33.1
[external/binutils.git] / sim / common / sim-profile.c
1 /* Default profiling support.
2    Copyright (C) 1996-2019 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 \f
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 \f
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 \f
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 \f
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 }