1 | /* Timing variables for measuring compiler performance. |
2 | |
3 | Copyright (C) 2000, 2002, 2004, 2006, 2009-2015, 2018-2019 Free Software |
4 | Foundation, Inc. |
5 | |
6 | Contributed by Alex Samuel <samuel@codesourcery.com> |
7 | |
8 | This program is free software: you can redistribute it and/or modify |
9 | it under the terms of the GNU General Public License as published by |
10 | the Free Software Foundation, either version 3 of the License, or |
11 | (at your option) any later version. |
12 | |
13 | This program is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | GNU General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU General Public License |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
20 | |
21 | #include <config.h> |
22 | |
23 | /* Specification. */ |
24 | #include "timevar.h" |
25 | |
26 | #include <stdio.h> |
27 | #include <stdlib.h> |
28 | #include <string.h> |
29 | #include <sys/resource.h> |
30 | #include <sys/time.h> |
31 | #include <sys/times.h> |
32 | |
33 | #include "gethrxtime.h" |
34 | #include "gettext.h" |
35 | #define _(msgid) gettext (msgid) |
36 | #include "xalloc.h" |
37 | |
38 | /* See timevar.h for an explanation of timing variables. */ |
39 | |
40 | int timevar_enabled = 0; |
41 | |
42 | /* A timing variable. */ |
43 | |
44 | struct timevar_def |
45 | { |
46 | /* Elapsed time for this variable. */ |
47 | struct timevar_time_def elapsed; |
48 | |
49 | /* If this variable is timed independently of the timing stack, |
50 | using timevar_start, this contains the start time. */ |
51 | struct timevar_time_def start_time; |
52 | |
53 | /* The name of this timing variable. */ |
54 | const char *name; |
55 | |
56 | /* Non-zero if this timing variable is running as a standalone |
57 | timer. */ |
58 | unsigned standalone : 1; |
59 | |
60 | /* Non-zero if this timing variable was ever started or pushed onto |
61 | the timing stack. */ |
62 | unsigned used : 1; |
63 | }; |
64 | |
65 | /* An element on the timing stack. Elapsed time is attributed to the |
66 | topmost timing variable on the stack. */ |
67 | |
68 | struct timevar_stack_def |
69 | { |
70 | /* The timing variable at this stack level. */ |
71 | struct timevar_def *timevar; |
72 | |
73 | /* The next lower timing variable context in the stack. */ |
74 | struct timevar_stack_def *next; |
75 | }; |
76 | |
77 | /* Declared timing variables. Constructed from the contents of |
78 | timevar.def. */ |
79 | static struct timevar_def timevars[TIMEVAR_LAST]; |
80 | |
81 | /* The top of the timing stack. */ |
82 | static struct timevar_stack_def *stack; |
83 | |
84 | /* A list of unused (i.e. allocated and subsequently popped) |
85 | timevar_stack_def instances. */ |
86 | static struct timevar_stack_def *unused_stack_instances; |
87 | |
88 | /* The time at which the topmost element on the timing stack was |
89 | pushed. Time elapsed since then is attributed to the topmost |
90 | element. */ |
91 | static struct timevar_time_def start_time; |
92 | |
93 | /* Fill the current times into TIME. */ |
94 | |
95 | static void |
96 | set_to_current_time (struct timevar_time_def *now) |
97 | { |
98 | now->user = 0; |
99 | now->sys = 0; |
100 | now->wall = 0; |
101 | |
102 | if (!timevar_enabled) |
103 | return; |
104 | |
105 | struct rusage self; |
106 | getrusage (RUSAGE_SELF, &self); |
107 | struct rusage chld; |
108 | getrusage (RUSAGE_CHILDREN, &chld); |
109 | |
110 | now->user = |
111 | xtime_make (self.ru_utime.tv_sec + chld.ru_utime.tv_sec, |
112 | (self.ru_utime.tv_usec + chld.ru_utime.tv_usec) * 1000); |
113 | |
114 | now->sys = |
115 | xtime_make (self.ru_stime.tv_sec + chld.ru_stime.tv_sec, |
116 | (self.ru_stime.tv_usec + chld.ru_stime.tv_usec) * 1000); |
117 | |
118 | now->wall = gethrxtime(); |
119 | } |
120 | |
121 | /* Return the current time. */ |
122 | |
123 | static struct timevar_time_def |
124 | get_current_time (void) |
125 | { |
126 | struct timevar_time_def now; |
127 | set_to_current_time (&now); |
128 | return now; |
129 | } |
130 | |
131 | /* Add the difference between STOP and START to TIMER. */ |
132 | |
133 | static void |
134 | timevar_accumulate (struct timevar_time_def *timer, |
135 | const struct timevar_time_def *start, |
136 | const struct timevar_time_def *stop) |
137 | { |
138 | timer->user += stop->user - start->user; |
139 | timer->sys += stop->sys - start->sys; |
140 | timer->wall += stop->wall - start->wall; |
141 | } |
142 | |
143 | void |
144 | timevar_init () |
145 | { |
146 | if (!timevar_enabled) |
147 | return; |
148 | |
149 | /* Zero all elapsed times. */ |
150 | memset ((void *) timevars, 0, sizeof (timevars)); |
151 | |
152 | /* Initialize the names of timing variables. */ |
153 | #define DEFTIMEVAR(identifier__, name__) \ |
154 | timevars[identifier__].name = name__; |
155 | #include "timevar.def" |
156 | #undef DEFTIMEVAR |
157 | } |
158 | |
159 | void |
160 | timevar_push (timevar_id_t timevar) |
161 | { |
162 | if (!timevar_enabled) |
163 | return; |
164 | |
165 | struct timevar_def *tv = &timevars[timevar]; |
166 | |
167 | /* Mark this timing variable as used. */ |
168 | tv->used = 1; |
169 | |
170 | /* Can't push a standalone timer. */ |
171 | if (tv->standalone) |
172 | abort (); |
173 | |
174 | /* What time is it? */ |
175 | struct timevar_time_def const now = get_current_time (); |
176 | |
177 | /* If the stack isn't empty, attribute the current elapsed time to |
178 | the old topmost element. */ |
179 | if (stack) |
180 | timevar_accumulate (&stack->timevar->elapsed, &start_time, &now); |
181 | |
182 | /* Reset the start time; from now on, time is attributed to |
183 | TIMEVAR. */ |
184 | start_time = now; |
185 | |
186 | /* See if we have a previously-allocated stack instance. If so, |
187 | take it off the list. If not, malloc a new one. */ |
188 | struct timevar_stack_def *context = NULL; |
189 | if (unused_stack_instances != NULL) |
190 | { |
191 | context = unused_stack_instances; |
192 | unused_stack_instances = unused_stack_instances->next; |
193 | } |
194 | else |
195 | context = (struct timevar_stack_def *) |
196 | xmalloc (sizeof (struct timevar_stack_def)); |
197 | |
198 | /* Fill it in and put it on the stack. */ |
199 | context->timevar = tv; |
200 | context->next = stack; |
201 | stack = context; |
202 | } |
203 | |
204 | void |
205 | timevar_pop (timevar_id_t timevar) |
206 | { |
207 | if (!timevar_enabled) |
208 | return; |
209 | |
210 | if (&timevars[timevar] != stack->timevar) |
211 | abort (); |
212 | |
213 | /* What time is it? */ |
214 | struct timevar_time_def const now = get_current_time (); |
215 | |
216 | /* Attribute the elapsed time to the element we're popping. */ |
217 | struct timevar_stack_def *popped = stack; |
218 | timevar_accumulate (&popped->timevar->elapsed, &start_time, &now); |
219 | |
220 | /* Reset the start time; from now on, time is attributed to the |
221 | element just exposed on the stack. */ |
222 | start_time = now; |
223 | |
224 | /* Take the item off the stack. */ |
225 | stack = stack->next; |
226 | |
227 | /* Don't delete the stack element; instead, add it to the list of |
228 | unused elements for later use. */ |
229 | popped->next = unused_stack_instances; |
230 | unused_stack_instances = popped; |
231 | } |
232 | |
233 | void |
234 | timevar_start (timevar_id_t timevar) |
235 | { |
236 | if (!timevar_enabled) |
237 | return; |
238 | |
239 | struct timevar_def *tv = &timevars[timevar]; |
240 | |
241 | /* Mark this timing variable as used. */ |
242 | tv->used = 1; |
243 | |
244 | /* Don't allow the same timing variable to be started more than |
245 | once. */ |
246 | if (tv->standalone) |
247 | abort (); |
248 | tv->standalone = 1; |
249 | |
250 | set_to_current_time (&tv->start_time); |
251 | } |
252 | |
253 | void |
254 | timevar_stop (timevar_id_t timevar) |
255 | { |
256 | if (!timevar_enabled) |
257 | return; |
258 | |
259 | struct timevar_def *tv = &timevars[timevar]; |
260 | |
261 | /* TIMEVAR must have been started via timevar_start. */ |
262 | if (!tv->standalone) |
263 | abort (); |
264 | |
265 | struct timevar_time_def const now = get_current_time (); |
266 | timevar_accumulate (&tv->elapsed, &tv->start_time, &now); |
267 | } |
268 | |
269 | void |
270 | timevar_get (timevar_id_t timevar, |
271 | struct timevar_time_def *elapsed) |
272 | { |
273 | struct timevar_def *tv = &timevars[timevar]; |
274 | *elapsed = tv->elapsed; |
275 | |
276 | /* Is TIMEVAR currently running as a standalone timer? */ |
277 | if (tv->standalone) |
278 | { |
279 | struct timevar_time_def const now = get_current_time (); |
280 | timevar_accumulate (elapsed, &tv->start_time, &now); |
281 | } |
282 | /* Or is TIMEVAR at the top of the timer stack? */ |
283 | else if (stack->timevar == tv) |
284 | { |
285 | struct timevar_time_def const now = get_current_time (); |
286 | timevar_accumulate (elapsed, &start_time, &now); |
287 | } |
288 | } |
289 | |
290 | void |
291 | timevar_print (FILE *fp) |
292 | { |
293 | if (!timevar_enabled) |
294 | return; |
295 | |
296 | /* Update timing information in case we're calling this from GDB. */ |
297 | |
298 | if (fp == 0) |
299 | fp = stderr; |
300 | |
301 | /* What time is it? */ |
302 | struct timevar_time_def const now = get_current_time (); |
303 | |
304 | /* If the stack isn't empty, attribute the current elapsed time to |
305 | the old topmost element. */ |
306 | if (stack) |
307 | timevar_accumulate (&stack->timevar->elapsed, &start_time, &now); |
308 | |
309 | /* Reset the start time; from now on, time is attributed to |
310 | TIMEVAR. */ |
311 | start_time = now; |
312 | |
313 | struct timevar_time_def const* total = &timevars[tv_total].elapsed; |
314 | |
315 | fprintf (fp, "%-22s\n" , |
316 | _("Execution times (seconds)" )); |
317 | fprintf (fp, " %-22s %-13s %-13s %-16s\n" , |
318 | "" , _("CPU user" ), _("CPU system" ), _("wall clock" )); |
319 | for (unsigned /* timevar_id_t */ id = 0; id < (unsigned) TIMEVAR_LAST; ++id) |
320 | { |
321 | /* Don't print the total execution time here; that goes at the |
322 | end. */ |
323 | if ((timevar_id_t) id == tv_total) |
324 | continue; |
325 | |
326 | /* Don't print timing variables that were never used. */ |
327 | struct timevar_def *tv = &timevars[(timevar_id_t) id]; |
328 | if (!tv->used) |
329 | continue; |
330 | |
331 | /* Percentages. */ |
332 | const int usr = total->user ? tv->elapsed.user * 100 / total->user : 0; |
333 | const int sys = total->sys ? tv->elapsed.sys * 100 / total->sys : 0; |
334 | const int wall = total->wall ? tv->elapsed.wall * 100 / total->wall : 0; |
335 | |
336 | /* Ignore insignificant lines. */ |
337 | if (!usr && !sys && !wall) |
338 | continue; |
339 | |
340 | fprintf (fp, " %-22s" , tv->name); |
341 | fprintf (fp, "%8.3f (%2d%%)" , tv->elapsed.user * 1e-9, usr); |
342 | fprintf (fp, "%8.3f (%2d%%)" , tv->elapsed.sys * 1e-9, sys); |
343 | fprintf (fp, "%11.6f (%2d%%)\n" , tv->elapsed.wall * 1e-9, wall); |
344 | } |
345 | |
346 | /* Print total time. */ |
347 | fprintf (fp, " %-22s" , timevars[tv_total].name); |
348 | fprintf (fp, "%8.3f " , total->user * 1e-9); |
349 | fprintf (fp, "%8.3f " , total->sys * 1e-9); |
350 | fprintf (fp, "%11.6f\n" , total->wall * 1e-9); |
351 | } |
352 | |