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