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