]>
Commit | Line | Data |
---|---|---|
2a9a326b AS |
1 | /* Timing variables for measuring compiler performance. |
2 | Copyright (C) 2000 Free Software Foundation, Inc. | |
3 | Contributed by Alex Samuel <samuel@codesourcery.com> | |
4 | ||
5 | This file is part of GNU CC. | |
6 | ||
7 | GNU CC 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 | GNU CC 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 | |
18 | along with GNU CC; see the file COPYING. If not, write to | |
19 | the Free Software Foundation, 59 Temple Place - Suite 330, | |
20 | Boston, MA 02111-1307, USA. */ | |
21 | ||
22 | #include "config.h" | |
23 | #include "system.h" | |
24 | ||
25 | #ifdef HAVE_SYS_TIMES_H | |
26 | # include <sys/times.h> | |
27 | #endif | |
28 | ||
26026d38 | 29 | #include "flags.h" |
2a9a326b AS |
30 | #include "timevar.h" |
31 | ||
32 | /* See timevar.h for an explanation of timing variables. */ | |
33 | ||
26026d38 AS |
34 | /* This macro evaluates to non-zero if timing variables are enabled. */ |
35 | #define TIMEVAR_ENABLE (!quiet_flag) | |
36 | ||
2a9a326b AS |
37 | /* A timing variable. */ |
38 | ||
39 | struct timevar_def | |
40 | { | |
41 | /* Elapsed time for this variable. */ | |
42 | struct timevar_time_def elapsed; | |
43 | ||
44 | /* If this variable is timed independently of the timing stack, | |
45 | using timevar_start, this contains the start time. */ | |
46 | struct timevar_time_def start_time; | |
47 | ||
26026d38 AS |
48 | /* The name of this timing variable. */ |
49 | const char *name; | |
50 | ||
2a9a326b AS |
51 | /* Non-zero if this timing variable is running as a standalone |
52 | timer. */ | |
26026d38 | 53 | unsigned standalone : 1; |
2a9a326b | 54 | |
26026d38 AS |
55 | /* Non-zero if this timing variable was ever started or pushed onto |
56 | the timing stack. */ | |
57 | unsigned used : 1; | |
2a9a326b AS |
58 | }; |
59 | ||
60 | /* An element on the timing stack. Elapsed time is attributed to the | |
61 | topmost timing variable on the stack. */ | |
62 | ||
63 | struct timevar_stack_def | |
64 | { | |
65 | /* The timing variable at this stack level. */ | |
66 | struct timevar_def *timevar; | |
67 | ||
68 | /* The next lower timing variable context in the stack. */ | |
69 | struct timevar_stack_def *next; | |
70 | }; | |
71 | ||
72 | /* Declared timing variables. Constructed from the contents of | |
73 | timevar.def. */ | |
74 | static struct timevar_def timevars[TIMEVAR_LAST]; | |
75 | ||
76 | /* The top of the timing stack. */ | |
77 | static struct timevar_stack_def *stack; | |
78 | ||
26026d38 AS |
79 | /* A list of unused (i.e. allocated and subsequently popped) |
80 | timevar_stack_def instances. */ | |
81 | static struct timevar_stack_def *unused_stack_instances; | |
82 | ||
2a9a326b AS |
83 | /* The time at which the topmost element on the timing stack was |
84 | pushed. Time elapsed since then is attributed to the topmost | |
85 | element. */ | |
86 | static struct timevar_time_def start_time; | |
87 | ||
88 | static void get_time | |
89 | PARAMS ((struct timevar_time_def *)); | |
90 | static void timevar_add | |
91 | PARAMS ((struct timevar_time_def *, struct timevar_time_def *)); | |
92 | static void timevar_accumulate | |
93 | PARAMS ((struct timevar_time_def *, struct timevar_time_def *, | |
94 | struct timevar_time_def *)); | |
95 | ||
96 | /* Fill the current times into TIME. The definition of this function | |
97 | also defines any or all of the HAVE_USER_TIME, HAVE_SYS_TIME, and | |
98 | HAVA_WALL_TIME macros. */ | |
99 | ||
100 | static void | |
26026d38 AS |
101 | get_time (now) |
102 | struct timevar_time_def *now; | |
2a9a326b | 103 | { |
26026d38 AS |
104 | now->user = 0; |
105 | now->sys = 0; | |
106 | now->wall = 0; | |
107 | ||
108 | if (!TIMEVAR_ENABLE) | |
109 | return; | |
2a9a326b AS |
110 | |
111 | #ifdef __BEOS__ | |
112 | /* Nothing. */ | |
113 | #else /* not BeOS */ | |
114 | #if defined (_WIN32) && !defined (__CYGWIN__) | |
115 | if (clock () >= 0) | |
26026d38 | 116 | now->user = clock () * 1000; |
2a9a326b AS |
117 | #define HAVE_USER_TIME |
118 | ||
119 | #else /* not _WIN32 */ | |
120 | #ifdef _SC_CLK_TCK | |
121 | { | |
122 | static int tick; | |
123 | struct tms tms; | |
124 | if (tick == 0) | |
125 | tick = 1000000 / sysconf (_SC_CLK_TCK); | |
26026d38 AS |
126 | now->wall = times (&tms) * tick; |
127 | now->user = tms.tms_utime * tick; | |
128 | now->sys = tms.tms_stime * tick; | |
2a9a326b AS |
129 | } |
130 | #define HAVE_USER_TIME | |
131 | #define HAVE_SYS_TIME | |
132 | #define HAVE_WALL_TIME | |
133 | ||
134 | #else | |
135 | #ifdef USG | |
136 | { | |
137 | struct tms tms; | |
138 | # if HAVE_SYSCONF && defined _SC_CLK_TCK | |
139 | # define TICKS_PER_SECOND sysconf (_SC_CLK_TCK) /* POSIX 1003.1-1996 */ | |
140 | # else | |
141 | # ifdef CLK_TCK | |
142 | # define TICKS_PER_SECOND CLK_TCK /* POSIX 1003.1-1988; obsolescent */ | |
143 | # else | |
144 | # define TICKS_PER_SECOND HZ /* traditional UNIX */ | |
145 | # endif | |
146 | # endif | |
26026d38 AS |
147 | now->wall = times (&tms) * (1000000 / TICKS_PER_SECOND); |
148 | now->user = tms.tms_utime * (1000000 / TICKS_PER_SECOND); | |
149 | now->sys = tms.tms_stime * (1000000 / TICKS_PER_SECOND); | |
2a9a326b AS |
150 | } |
151 | #define HAVE_USER_TIME | |
152 | #define HAVE_SYS_TIME | |
153 | #define HAVE_WALL_TIME | |
154 | ||
155 | #else | |
156 | #ifndef VMS | |
157 | { | |
158 | struct rusage rusage; | |
159 | getrusage (0, &rusage); | |
26026d38 | 160 | now->user |
2a9a326b | 161 | = rusage.ru_utime.tv_sec * 1000000 + rusage.ru_utime.tv_usec; |
26026d38 | 162 | now->sys |
2a9a326b AS |
163 | = rusage.ru_stime.tv_sec * 1000000 + rusage.ru_stime.tv_usec; |
164 | } | |
165 | #define HAVE_USER_TIME | |
166 | #define HAVE_SYS_TIME | |
167 | ||
168 | #else /* VMS */ | |
169 | { | |
170 | struct | |
171 | { | |
172 | int proc_user_time; | |
173 | int proc_system_time; | |
174 | int child_user_time; | |
175 | int child_system_time; | |
176 | } vms_times; | |
26026d38 AS |
177 | now->wall = times ((void *) &vms_times) * 10000; |
178 | now->user = vms_times.proc_user_time * 10000; | |
179 | now->sys = vms_times.proc_system_time * 10000; | |
2a9a326b AS |
180 | } |
181 | #define HAVE_USER_TIME | |
182 | #define HAVE_SYS_TIME | |
183 | #define HAVE_WALL_TIME | |
184 | ||
185 | #endif /* VMS */ | |
186 | #endif /* USG */ | |
187 | #endif /* _SC_CLK_TCK */ | |
188 | #endif /* _WIN32 */ | |
189 | #endif /* __BEOS__ */ | |
190 | } | |
191 | ||
192 | /* Add ELAPSED to TIMER. */ | |
193 | ||
194 | static void | |
195 | timevar_add (timer, elapsed) | |
196 | struct timevar_time_def *timer; | |
197 | struct timevar_time_def *elapsed; | |
198 | { | |
199 | timer->user += elapsed->user; | |
200 | timer->sys += elapsed->sys; | |
201 | timer->wall += elapsed->wall; | |
202 | } | |
203 | ||
204 | /* Add the difference between STOP_TIME and START_TIME to TIMER. */ | |
205 | ||
206 | static void | |
207 | timevar_accumulate (timer, start_time, stop_time) | |
208 | struct timevar_time_def *timer; | |
209 | struct timevar_time_def *start_time; | |
210 | struct timevar_time_def *stop_time; | |
211 | { | |
212 | timer->user += stop_time->user - start_time->user; | |
213 | timer->sys += stop_time->sys - start_time->sys; | |
214 | timer->wall += stop_time->wall - start_time->wall; | |
215 | } | |
216 | ||
217 | /* Initialize timing variables. */ | |
218 | ||
219 | void | |
4fbe8d07 | 220 | init_timevar () |
2a9a326b | 221 | { |
26026d38 AS |
222 | if (!TIMEVAR_ENABLE) |
223 | return; | |
224 | ||
2a9a326b AS |
225 | /* Zero all elapsed times. */ |
226 | memset ((void *) timevars, 0, sizeof (timevars)); | |
227 | ||
228 | /* Initialize the names of timing variables. */ | |
229 | #define DEFTIMEVAR(identifer__, name__) \ | |
230 | timevars[identifer__].name = name__; | |
231 | #include "timevar.def" | |
232 | #undef DEFTIMEVAR | |
233 | } | |
234 | ||
235 | /* Push TIMEVAR onto the timing stack. No further elapsed time is | |
236 | attributed to the previous topmost timing variable on the stack; | |
237 | subsequent elapsed time is attributed to TIMEVAR, until it is | |
238 | popped or another element is pushed on top. | |
239 | ||
240 | TIMEVAR cannot be running as a standalone timer. */ | |
241 | ||
242 | void | |
243 | timevar_push (timevar) | |
244 | timevar_id_t timevar; | |
245 | { | |
246 | struct timevar_def *tv = &timevars[timevar]; | |
247 | struct timevar_stack_def *context; | |
248 | struct timevar_time_def now; | |
249 | ||
26026d38 AS |
250 | if (!TIMEVAR_ENABLE) |
251 | return; | |
252 | ||
253 | /* Mark this timing variable as used. */ | |
254 | tv->used = 1; | |
255 | ||
2a9a326b AS |
256 | /* Can't push a standalone timer. */ |
257 | if (tv->standalone) | |
258 | abort (); | |
259 | ||
260 | /* What time is it? */ | |
261 | get_time (&now); | |
262 | ||
263 | /* If the stack isn't empty, attribute the current elapsed time to | |
264 | the old topmost element. */ | |
265 | if (stack) | |
266 | timevar_accumulate (&stack->timevar->elapsed, &start_time, &now); | |
267 | ||
268 | /* Reset the start time; from now on, time is attributed to | |
269 | TIMEVAR. */ | |
270 | start_time = now; | |
271 | ||
26026d38 AS |
272 | /* See if we have a previously-allocated stack instance. If so, |
273 | take it off the list. If not, malloc a new one. */ | |
274 | if (unused_stack_instances != NULL) | |
275 | { | |
276 | context = unused_stack_instances; | |
277 | unused_stack_instances = unused_stack_instances->next; | |
278 | } | |
279 | else | |
280 | context = (struct timevar_stack_def *) | |
281 | xmalloc (sizeof (struct timevar_stack_def)); | |
282 | ||
283 | /* Fill it in and put it on the stack. */ | |
2a9a326b AS |
284 | context->timevar = tv; |
285 | context->next = stack; | |
286 | stack = context; | |
287 | } | |
288 | ||
289 | /* Pop the topmost timing variable element off the timing stack. The | |
290 | popped variable must be TIMEVAR. Elapsed time since the that | |
291 | element was pushed on, or since it was last exposed on top of the | |
292 | stack when the element above it was popped off, is credited to that | |
293 | timing variable. */ | |
294 | ||
295 | void | |
296 | timevar_pop (timevar) | |
297 | timevar_id_t timevar; | |
298 | { | |
299 | struct timevar_time_def now; | |
26026d38 AS |
300 | struct timevar_stack_def *popped = stack; |
301 | ||
302 | if (!TIMEVAR_ENABLE) | |
303 | return; | |
2a9a326b AS |
304 | |
305 | if (&timevars[timevar] != stack->timevar) | |
306 | abort (); | |
307 | ||
308 | /* What time is it? */ | |
309 | get_time (&now); | |
310 | ||
311 | /* Attribute the elapsed time to the element we're popping. */ | |
26026d38 | 312 | timevar_accumulate (&popped->timevar->elapsed, &start_time, &now); |
2a9a326b AS |
313 | |
314 | /* Reset the start time; from now on, time is attributed to the | |
315 | element just exposed on the stack. */ | |
316 | start_time = now; | |
317 | ||
26026d38 AS |
318 | /* Take the item off the stack. */ |
319 | stack = stack->next; | |
320 | ||
321 | /* Don't delete the stack element; instead, add it to the list of | |
322 | unused elements for later use. */ | |
323 | popped->next = unused_stack_instances; | |
324 | unused_stack_instances = popped; | |
2a9a326b AS |
325 | } |
326 | ||
327 | /* Start timing TIMEVAR independently of the timing stack. Elapsed | |
328 | time until timevar_stop is called for the same timing variable is | |
329 | attributed to TIMEVAR. */ | |
330 | ||
331 | void | |
332 | timevar_start (timevar) | |
333 | timevar_id_t timevar; | |
334 | { | |
335 | struct timevar_def *tv = &timevars[timevar]; | |
336 | ||
26026d38 AS |
337 | if (!TIMEVAR_ENABLE) |
338 | return; | |
339 | ||
340 | /* Mark this timing variable as used. */ | |
341 | tv->used = 1; | |
342 | ||
2a9a326b AS |
343 | /* Don't allow the same timing variable to be started more than |
344 | once. */ | |
345 | if (tv->standalone) | |
346 | abort (); | |
347 | tv->standalone = 1; | |
348 | ||
349 | get_time (&tv->start_time); | |
350 | } | |
351 | ||
352 | /* Stop timing TIMEVAR. Time elapsed since timevar_start was called | |
353 | is attributed to it. */ | |
354 | ||
355 | void | |
356 | timevar_stop (timevar) | |
357 | timevar_id_t timevar; | |
358 | { | |
359 | struct timevar_def *tv = &timevars[timevar]; | |
360 | struct timevar_time_def now; | |
361 | ||
26026d38 AS |
362 | if (!TIMEVAR_ENABLE) |
363 | return; | |
364 | ||
2a9a326b AS |
365 | /* TIMEVAR must have been started via timevar_start. */ |
366 | if (!tv->standalone) | |
367 | abort (); | |
368 | ||
369 | get_time (&now); | |
370 | timevar_accumulate (&tv->elapsed, &tv->start_time, &now); | |
371 | } | |
372 | ||
373 | /* Fill the elapsed time for TIMEVAR into ELAPSED. Returns | |
374 | update-to-date information even if TIMEVAR is currently running. */ | |
375 | ||
376 | void | |
377 | timevar_get (timevar, elapsed) | |
378 | timevar_id_t timevar; | |
379 | struct timevar_time_def *elapsed; | |
380 | { | |
381 | struct timevar_def *tv = &timevars[timevar]; | |
382 | ||
383 | *elapsed = tv->elapsed; | |
384 | ||
385 | /* Is TIMEVAR currently running as a standalone timer? */ | |
386 | if (tv->standalone) | |
387 | /* Add the time elapsed since the it was started. */ | |
388 | timevar_add (elapsed, &tv->start_time); | |
389 | ||
390 | /* Is TIMEVAR at the top of the timer stack? */ | |
391 | if (stack->timevar == tv) | |
392 | /* Add the elapsed time since it was pushed. */ | |
393 | timevar_add (elapsed, &start_time); | |
394 | } | |
395 | ||
396 | /* Summarize timing variables to FP. The timing variable TV_TOTAL has | |
397 | a special meaning -- it's considered to be the total elapsed time, | |
398 | for normalizing the others, and is displayed last. */ | |
399 | ||
400 | void | |
401 | timevar_print (fp) | |
402 | FILE *fp; | |
403 | { | |
404 | /* Only print stuff if we have some sort of time information. */ | |
405 | #if defined (HAVE_USER_TIME) || defined (HAVE_SYS_TIME) || defined (HAVE_WALL_TIME) | |
406 | timevar_id_t id; | |
407 | struct timevar_time_def *total = &timevars[TV_TOTAL].elapsed; | |
ea11ca7e | 408 | struct timevar_time_def now; |
2a9a326b | 409 | |
26026d38 AS |
410 | if (!TIMEVAR_ENABLE) |
411 | return; | |
412 | ||
ea11ca7e JM |
413 | /* Update timing information in case we're calling this from GDB. */ |
414 | ||
415 | if (fp == 0) | |
416 | fp = stderr; | |
417 | ||
418 | /* What time is it? */ | |
419 | get_time (&now); | |
420 | ||
421 | /* If the stack isn't empty, attribute the current elapsed time to | |
422 | the old topmost element. */ | |
423 | if (stack) | |
424 | timevar_accumulate (&stack->timevar->elapsed, &start_time, &now); | |
425 | ||
426 | /* Reset the start time; from now on, time is attributed to | |
427 | TIMEVAR. */ | |
428 | start_time = now; | |
429 | ||
5e4adfba | 430 | fprintf (fp, _("\nExecution times (seconds)\n")); |
2a9a326b AS |
431 | for (id = 0; id < TIMEVAR_LAST; ++id) |
432 | { | |
433 | struct timevar_def *tv = &timevars[id]; | |
434 | ||
435 | /* Don't print the total execution time here; that goes at the | |
436 | end. */ | |
437 | if (id == TV_TOTAL) | |
438 | continue; | |
439 | ||
26026d38 AS |
440 | /* Don't print timing variables that were never used. */ |
441 | if (!tv->used) | |
442 | continue; | |
443 | ||
2a9a326b AS |
444 | /* The timing variable name. */ |
445 | fprintf (fp, " %-22s:", tv->name); | |
446 | ||
447 | #ifdef HAVE_USER_TIME | |
448 | /* Print user-mode time for this process. */ | |
449 | fprintf (fp, "%4ld.%02ld (%2.0f%%) usr", | |
450 | tv->elapsed.user / 1000000, | |
451 | (tv->elapsed.user % 1000000) / 10000, | |
452 | (total->user == 0) ? 0.0 | |
453 | : (100.0 * tv->elapsed.user / (double) total->user)); | |
454 | #endif /* HAVE_USER_TIME */ | |
455 | ||
456 | #ifdef HAVE_SYS_TIME | |
457 | /* Print system-mode time for this process. */ | |
458 | fprintf (fp, "%4ld.%02ld (%2.0f%%) sys", | |
459 | tv->elapsed.sys / 1000000, | |
460 | (tv->elapsed.sys % 1000000) / 10000, | |
461 | (total->sys == 0) ? 0.0 | |
462 | : (100.0 * tv->elapsed.sys / (double) total->sys)); | |
463 | #endif /* HAVE_SYS_TIME */ | |
464 | ||
465 | #ifdef HAVE_WALL_TIME | |
466 | /* Print wall clock time elapsed. */ | |
467 | fprintf (fp, "%4ld.%02ld (%2.0f%%) wall", | |
468 | tv->elapsed.wall / 1000000, | |
469 | (tv->elapsed.wall % 1000000) / 10000, | |
470 | (total->wall == 0) ? 0.0 | |
471 | : (100.0 * tv->elapsed.wall / (double) total->wall)); | |
472 | #endif /* HAVE_WALL_TIME */ | |
473 | ||
474 | fprintf (fp, "\n"); | |
475 | } | |
476 | ||
477 | /* Print total time. */ | |
5e4adfba | 478 | fprintf (fp, _(" TOTAL :")); |
2a9a326b AS |
479 | #ifdef HAVE_USER_TIME |
480 | fprintf (fp, "%4ld.%02ld ", | |
481 | total->user / 1000000, (total->user % 1000000) / 10000); | |
482 | #endif | |
483 | #ifdef HAVE_SYS_TIME | |
484 | fprintf (fp, "%4ld.%02ld ", | |
485 | total->sys / 1000000, (total->sys % 1000000) / 10000); | |
486 | #endif | |
487 | #ifdef HAVE_WALL_TIME | |
488 | fprintf (fp, "%4ld.%02ld\n", | |
489 | total->wall / 1000000, (total->wall % 1000000) / 10000); | |
490 | #endif | |
491 | ||
492 | #endif /* defined (HAVE_USER_TIME) || defined (HAVE_SYS_TIME) | |
493 | || defined (HAVE_WALL_TIME) */ | |
494 | } | |
495 | ||
496 | /* Returns time (user + system) used so far by the compiler process, | |
497 | in microseconds. */ | |
498 | ||
499 | long | |
500 | get_run_time () | |
501 | { | |
502 | struct timevar_time_def total_elapsed; | |
503 | timevar_get (TV_TOTAL, &total_elapsed); | |
504 | return total_elapsed.user + total_elapsed.sys; | |
505 | } | |
506 | ||
507 | /* Prints a message to stderr stating that time elapsed in STR is | |
508 | TOTAL (given in microseconds). */ | |
509 | ||
510 | void | |
511 | print_time (str, total) | |
512 | const char *str; | |
513 | long total; | |
514 | { | |
515 | long all_time = get_run_time (); | |
516 | fprintf (stderr, | |
5e4adfba | 517 | _("time in %s: %ld.%06ld (%ld%%)\n"), |
2a9a326b AS |
518 | str, total / 1000000, total % 1000000, |
519 | all_time == 0 ? 0 | |
520 | : (long) (((100.0 * (double) total) / (double) all_time) + .5)); | |
521 | } |