* sim-utils.c (sim_add_commas): New function.
[binutils-gdb.git] / sim / common / sim-profile.c
1 /* Default profiling support.
2 Copyright (C) 1996, 1997 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
25 #define COMMAS(n) sim_add_commas (comma_buf, sizeof (comma_buf), (n))
26
27 static MODULE_UNINSTALL_FN profile_uninstall;
28
29 static void print_bar (SIM_DESC, unsigned int, unsigned int, unsigned int);
30
31 static DECLARE_OPTION_HANDLER (profile_option_handler);
32
33 #define OPTION_PROFILE_INSN (OPTION_START + 0)
34 #define OPTION_PROFILE_MEMORY (OPTION_START + 1)
35 #define OPTION_PROFILE_MODEL (OPTION_START + 2)
36 #define OPTION_PROFILE_FILE (OPTION_START + 3)
37 #define OPTION_PROFILE_RANGE (OPTION_START + 4)
38
39 static const OPTION profile_options[] = {
40 { {"profile", no_argument, NULL, 'p'},
41 'p', NULL, "Perform profiling",
42 profile_option_handler },
43 { {"profile-insn", no_argument, NULL, OPTION_PROFILE_INSN},
44 '\0', NULL, "Perform instruction profiling",
45 profile_option_handler },
46 { {"profile-memory", no_argument, NULL, OPTION_PROFILE_MEMORY},
47 '\0', NULL, "Perform memory profiling",
48 profile_option_handler },
49 { {"profile-model", no_argument, NULL, OPTION_PROFILE_MODEL},
50 '\0', NULL, "Perform model profiling",
51 profile_option_handler },
52 { {"profile-file", required_argument, NULL, OPTION_PROFILE_FILE},
53 '\0', "FILE NAME", "Specify profile output file",
54 profile_option_handler },
55 { {"profile-pc-frequency", required_argument, NULL, 'F'},
56 'F', "PC PROFILE FREQUENCY", "Turn on PC profiling at specified frequency",
57 profile_option_handler },
58 { {"profile-pc-size", required_argument, NULL, 'S'},
59 'S', "PC PROFILE SIZE", "Specify PC profiling size",
60 profile_option_handler },
61 #if 0 /*FIXME:wip*/
62 { {"profile-range", required_argument, NULL, OPTION_PROFILE_RANGE},
63 0, NULL, "Specify range of addresses to profile",
64 profile_option_handler },
65 #endif
66 { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL }
67 };
68
69 static SIM_RC
70 profile_option_handler (SIM_DESC sd, int opt, char *arg)
71 {
72 int i,n;
73
74 switch (opt)
75 {
76 case 'p' :
77 if (! WITH_PROFILE)
78 sim_io_eprintf (sd, "Profiling not compiled in, -p option ignored\n");
79 else
80 {
81 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
82 for (i = 0; i < MAX_PROFILE_VALUES; ++i)
83 CPU_PROFILE_FLAGS (STATE_CPU (sd, n))[i] = 1;
84 }
85 break;
86
87 case OPTION_PROFILE_INSN :
88 #if WITH_PROFILE_INSN_P
89 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
90 CPU_PROFILE_FLAGS (STATE_CPU (sd, n))[PROFILE_INSN_IDX] = 1;
91 #else
92 sim_io_eprintf (sd, "Instruction profiling not compiled in, `--profile-insn' ignored\n");
93 #endif
94 break;
95
96 case OPTION_PROFILE_MEMORY :
97 #if WITH_PROFILE_MEMORY_P
98 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
99 CPU_PROFILE_FLAGS (STATE_CPU (sd, n))[PROFILE_MEMORY_IDX] = 1;
100 #else
101 sim_io_eprintf (sd, "Memory profiling not compiled in, `--profile-memory' ignored\n");
102 #endif
103 break;
104
105 case OPTION_PROFILE_MODEL :
106 #if WITH_PROFILE_MODEL_P
107 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
108 CPU_PROFILE_FLAGS (STATE_CPU (sd, n))[PROFILE_MODEL_IDX] = 1;
109 #else
110 sim_io_eprintf (sd, "Model profiling not compiled in, `--profile-model' ignored\n");
111 #endif
112 break;
113
114 case OPTION_PROFILE_FILE :
115 /* FIXME: Might want this to apply to pc profiling only,
116 or have two profile file options. */
117 if (! WITH_PROFILE)
118 sim_io_eprintf (sd, "Profiling not compiled in, `--profile-file' ignored\n");
119 else
120 {
121 FILE *f = fopen (arg, "w");
122
123 if (f == NULL)
124 {
125 sim_io_eprintf (sd, "Unable to open profile output file `%s'\n", arg);
126 return SIM_RC_FAIL;
127 }
128 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
129 PROFILE_FILE (CPU_PROFILE_DATA (STATE_CPU (sd, n))) = f;
130 }
131 break;
132
133 case 'F' :
134 #if WITH_PROFILE_PC_P
135 /* FIXME: Validate arg. */
136 i = atoi (arg);
137 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
138 PROFILE_PC_FREQ (CPU_PROFILE_DATA (STATE_CPU (sd, n))) = i;
139 #else
140 sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-frequency' ignored\n");
141 #endif
142 break;
143
144 case 'S' :
145 #if WITH_PROFILE_PC_P
146 /* FIXME: Validate arg. */
147 i = atoi (arg);
148 for (n = 0; n < MAX_NR_PROCESSORS; ++n)
149 PROFILE_PC_SIZE (CPU_PROFILE_DATA (STATE_CPU (sd, n))) = i;
150 #else
151 sim_io_eprintf (sd, "PC profiling not compiled in, `--profile-pc-size' ignored\n");
152 #endif
153 break;
154
155 #if 0 /* FIXME:wip */
156 case OPTION_PROFILE_RANGE :
157 break;
158 #endif
159 }
160
161 return SIM_RC_OK;
162 }
163 \f
164 /* Install profiling support in the simulator. */
165
166 SIM_RC
167 profile_install (SIM_DESC sd)
168 {
169 int i;
170
171 sim_add_option_table (sd, profile_options);
172 for (i = 0; i < MAX_NR_PROCESSORS; ++i)
173 memset (CPU_PROFILE_DATA (STATE_CPU (sd, i)), 0,
174 sizeof (* CPU_PROFILE_DATA (STATE_CPU (sd, i))));
175 sim_module_add_uninstall_fn (sd, profile_uninstall);
176 return SIM_RC_OK;
177 }
178
179 static void
180 profile_uninstall (SIM_DESC sd)
181 {
182 int i;
183
184 for (i = 0; i < MAX_NR_PROCESSORS; ++i)
185 {
186 PROFILE_DATA *data = CPU_PROFILE_DATA (STATE_CPU (sd, i));
187 if (PROFILE_FILE (data) != NULL)
188 fclose (PROFILE_FILE (data));
189 }
190 }
191 \f
192 /* Summary printing support. */
193
194 #if WITH_PROFILE_INSN_P
195
196 static void
197 profile_print_insn (sim_cpu *cpu, int verbose)
198 {
199 unsigned int i, n, total, max_val, max_name_len;
200 SIM_DESC sd = CPU_STATE (cpu);
201 PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
202 char comma_buf[20];
203
204 sim_io_printf (sd, "Instruction Statistics\n\n");
205
206 /* First pass over data computes various things. */
207 max_val = total = max_name_len = 0;
208 for (i = 1; i < MAX_INSNS; ++i)
209 {
210 total += PROFILE_INSN_COUNT (data) [i];
211 if (PROFILE_INSN_COUNT (data) [i] > max_val)
212 max_val = PROFILE_INSN_COUNT (data) [i];
213 n = strlen (INSN_NAME (i));
214 if (n > max_name_len)
215 max_name_len = n;
216 }
217
218 sim_io_printf (sd, " Total: %s insns\n", COMMAS (total));
219
220 if (verbose && max_val != 0)
221 {
222 /* Now we can print the histogram. */
223 sim_io_printf (sd, "\n");
224 for (i = 1; i < MAX_INSNS; ++i)
225 {
226 if (PROFILE_INSN_COUNT (data) [i] != 0)
227 {
228 sim_io_printf (sd, " %*s: %*s: ",
229 max_name_len, INSN_NAME (i),
230 max_val < 10000 ? 5 : 10,
231 COMMAS (PROFILE_INSN_COUNT (data) [i]));
232 print_bar (sd, PROFILE_HISTOGRAM_WIDTH,
233 PROFILE_INSN_COUNT (data) [i],
234 max_val);
235 sim_io_printf (sd, "\n");
236 }
237 }
238 }
239
240 sim_io_printf (sd, "\n");
241 }
242
243 #endif
244
245 #if WITH_PROFILE_MEMORY_P
246
247 static void
248 profile_print_memory (sim_cpu *cpu, int verbose)
249 {
250 unsigned int i, n;
251 unsigned int total_read, total_write;
252 unsigned int max_val, max_name_len;
253 /* FIXME: Need to add smp support. */
254 SIM_DESC sd = CPU_STATE (cpu);
255 PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
256 char comma_buf[20];
257
258 sim_io_printf (sd, "Memory Access Statistics\n\n");
259
260 /* First pass over data computes various things. */
261 max_val = total_read = total_write = max_name_len = 0;
262 for (i = 0; i < MAX_MODES; ++i)
263 {
264 total_read += PROFILE_READ_COUNT (data) [i];
265 total_write += PROFILE_WRITE_COUNT (data) [i];
266 if (PROFILE_READ_COUNT (data) [i] > max_val)
267 max_val = PROFILE_READ_COUNT (data) [i];
268 if (PROFILE_WRITE_COUNT (data) [i] > max_val)
269 max_val = PROFILE_WRITE_COUNT (data) [i];
270 n = strlen (MODE_NAME (i));
271 if (n > max_name_len)
272 max_name_len = n;
273 }
274
275 /* One could use PROFILE_LABEL_WIDTH here. I chose not to. */
276 sim_io_printf (sd, " Total read: %s accesses\n",
277 COMMAS (total_read));
278 sim_io_printf (sd, " Total write: %s accesses\n",
279 COMMAS (total_write));
280
281 if (verbose && max_val != 0)
282 {
283 /* FIXME: Need to separate instruction fetches from data fetches
284 as the former swamps the latter. */
285 /* Now we can print the histogram. */
286 sim_io_printf (sd, "\n");
287 for (i = 0; i < MAX_MODES; ++i)
288 {
289 if (PROFILE_READ_COUNT (data) [i] != 0)
290 {
291 sim_io_printf (sd, " %*s read: %*s: ",
292 max_name_len, MODE_NAME (i),
293 max_val < 10000 ? 5 : 10,
294 COMMAS (PROFILE_READ_COUNT (data) [i]));
295 print_bar (sd, PROFILE_HISTOGRAM_WIDTH,
296 PROFILE_READ_COUNT (data) [i],
297 max_val);
298 sim_io_printf (sd, "\n");
299 }
300 if (PROFILE_WRITE_COUNT (data) [i] != 0)
301 {
302 sim_io_printf (sd, " %*s write: %*s: ",
303 max_name_len, MODE_NAME (i),
304 max_val < 10000 ? 5 : 10,
305 COMMAS (PROFILE_WRITE_COUNT (data) [i]));
306 print_bar (sd, PROFILE_HISTOGRAM_WIDTH,
307 PROFILE_WRITE_COUNT (data) [i],
308 max_val);
309 sim_io_printf (sd, "\n");
310 }
311 }
312 }
313
314 sim_io_printf (sd, "\n");
315 }
316
317 #endif
318
319 #if WITH_PROFILE_MODEL_P
320
321 static void
322 profile_print_model (sim_cpu *cpu, int verbose)
323 {
324 SIM_DESC sd = CPU_STATE (cpu);
325 PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
326 unsigned long cti_stalls = PROFILE_MODEL_CTI_STALL_COUNT (data);
327 unsigned long load_stalls = PROFILE_MODEL_LOAD_STALL_COUNT (data);
328 unsigned long total = PROFILE_MODEL_CYCLE_COUNT (data)
329 + cti_stalls + load_stalls;
330 char comma_buf[20];
331
332 sim_io_printf (sd, "Model %s Timing Information\n\n",
333 MODEL_NAME (STATE_MODEL (sd)));
334 sim_io_printf (sd, " %-*s %s\n",
335 PROFILE_LABEL_WIDTH, "Taken branches:",
336 COMMAS (PROFILE_MODEL_TAKEN_COUNT (data)));
337 sim_io_printf (sd, " %-*s %s\n",
338 PROFILE_LABEL_WIDTH, "Untaken branches:",
339 COMMAS (PROFILE_MODEL_UNTAKEN_COUNT (data)));
340 sim_io_printf (sd, " %-*s %s\n",
341 PROFILE_LABEL_WIDTH, "Cycles stalled due to branches:",
342 COMMAS (cti_stalls));
343 sim_io_printf (sd, " %-*s %s\n",
344 PROFILE_LABEL_WIDTH, "Cycles stalled due to loads:",
345 COMMAS (load_stalls));
346 sim_io_printf (sd, " %-*s %s\n",
347 PROFILE_LABEL_WIDTH, "Total cycles (*approximate*):",
348 COMMAS (total));
349 sim_io_printf (sd, "\n");
350 }
351
352 #endif
353
354 static void
355 print_bar (SIM_DESC sd, unsigned int width,
356 unsigned int val, unsigned int max_val)
357 {
358 unsigned int i, count;
359
360 count = ((double) val / (double) max_val) * (double) width;
361
362 for (i = 0; i < count; ++i)
363 sim_io_printf (sd, "*");
364 }
365
366 /* Print the simulator's execution speed for CPU. */
367
368 static void
369 profile_print_speed (sim_cpu *cpu)
370 {
371 SIM_DESC sd = CPU_STATE (cpu);
372 PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
373 unsigned long milliseconds = PROFILE_EXEC_TIME (data);
374 unsigned long total = PROFILE_TOTAL_INSN_COUNT (data);
375 char comma_buf[20];
376
377 sim_io_printf (sd, "Simulator Execution Speed\n\n");
378
379 if (total != 0)
380 sim_io_printf (sd, " Total instructions: %s\n", COMMAS (total));
381
382 if (milliseconds < 1000)
383 sim_io_printf (sd, " Total Execution Time: < 1 second\n\n");
384 else
385 {
386 /* The printing of the time rounded to 2 decimal places makes the speed
387 calculation seem incorrect [even though it is correct]. So round
388 MILLISECONDS first. This can marginally affect the result, but it's
389 better that the user not perceive there's a math error. */
390 double secs = (double) milliseconds / 1000;
391 secs = ((double) (unsigned long) (secs * 100 + .5)) / 100;
392 sim_io_printf (sd, " Total Execution Time: %.2f seconds\n", secs);
393 /* Don't confuse things with data that isn't useful.
394 If we ran for less than 2 seconds, only use the data if we
395 executed more than 100,000 insns. */
396 if (secs >= 2 || total >= 100000)
397 sim_io_printf (sd, " Simulator Speed: %s insns/second\n\n",
398 COMMAS ((unsigned long) ((double) total / secs)));
399 }
400 }
401
402 /* Top level function to print all summary profile information.
403 It is [currently] intended that all such data is printed by this function.
404 I'd rather keep it all in one place for now. To that end, MISC_CPU and
405 MISC are callbacks used to print any miscellaneous data.
406
407 One might want to add a user option that allows printing by type or by cpu
408 (i.e. print all insn data for each cpu first, or print data cpu by cpu).
409 This may be a case of featuritis so it's currently left out.
410
411 Note that results are indented two spaces to distinguish them from
412 section titles. */
413
414 void
415 profile_print (SIM_DESC sd, int verbose,
416 PROFILE_CALLBACK *misc, PROFILE_CPU_CALLBACK *misc_cpu)
417 {
418 int i,c;
419 int print_title_p = 0;
420
421 /* Only print the title if some data has been collected. */
422 /* FIXME: If the number of processors can be selected on the command line,
423 then MAX_NR_PROCESSORS will need to take an argument of `sd'. */
424
425 for (c = 0; c < MAX_NR_PROCESSORS; ++c)
426 {
427 sim_cpu *cpu = STATE_CPU (sd, c);
428 PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
429
430 for (i = 0; i < MAX_PROFILE_VALUES; ++i)
431 if (PROFILE_FLAGS (data) [i])
432 print_title_p = 1;
433 /* One could break out early if print_title_p is set. */
434 }
435 if (print_title_p)
436 sim_io_printf (sd, "Summary profiling results:\n\n");
437
438 /* Loop, cpu by cpu, printing results. */
439
440 for (c = 0; c < MAX_NR_PROCESSORS; ++c)
441 {
442 sim_cpu *cpu = STATE_CPU (sd, c);
443 PROFILE_DATA *data = CPU_PROFILE_DATA (cpu);
444
445 if (MAX_NR_PROCESSORS > 1)
446 sim_io_printf (sd, "CPU %d\n\n", c);
447
448 #if WITH_PROFILE_INSN_P
449 if (PROFILE_FLAGS (data) [PROFILE_INSN_IDX])
450 profile_print_insn (cpu, verbose);
451 #endif
452
453 #if WITH_PROFILE_MEMORY_P
454 if (PROFILE_FLAGS (data) [PROFILE_MEMORY_IDX])
455 profile_print_memory (cpu, verbose);
456 #endif
457
458 #if WITH_PROFILE_MODEL_P
459 if (PROFILE_FLAGS (data) [PROFILE_MODEL_IDX])
460 profile_print_model (cpu, verbose);
461 #endif
462
463 #if WITH_PROFILE_SCACHE_P && WITH_SCACHE
464 if (PROFILE_FLAGS (data) [PROFILE_SCACHE_IDX])
465 scache_print_profile (cpu, verbose);
466 #endif
467
468 /* Print cpu-specific data before the execution speed. */
469 if (misc_cpu != NULL)
470 (*misc_cpu) (cpu, verbose);
471
472 /* Always try to print execution time and speed. */
473 if (verbose
474 || PROFILE_FLAGS (data) [PROFILE_INSN_IDX])
475 profile_print_speed (cpu);
476 }
477
478 /* Finally print non-cpu specific miscellaneous data. */
479
480 if (misc != NULL)
481 (*misc) (sd, verbose);
482 }