9129a38a147d34ee65efc70b8f56b0fcf5459e1a
[deliverable/binutils-gdb.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 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 }
This page took 0.06297 seconds and 4 git commands to generate.