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