/* */
This source file includes following definitions.
- init_statistics
- statistics_time_start
- statistics_time_end
- decimal_width
- get_max_width
- print_header_list
- print_time_list
- print_header_table
- print_time_table
- print_footer_common
- print_statistics
1 /*
2 * Copyright (c) 2009
3 * Tama Communications Corporation
4 *
5 * This file is part of GNU GLOBAL.
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 3 of the License, or
10 * (at your option) 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
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #if TIME_WITH_SYS_TIME
25 #include <sys/time.h>
26 #include <time.h>
27 #elif HAVE_SYS_TIME_H
28 #include <sys/time.h>
29 #else
30 #include <time.h>
31 #endif
32 #if HAVE_SYS_RESOURCE_H
33 #include <sys/resource.h>
34 #endif
35 #include <assert.h>
36 #include <math.h>
37 #include <stdarg.h>
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <string.h>
41
42 #include "checkalloc.h"
43 #include "die.h"
44 #include "queue.h"
45 #include "statistics.h"
46 #include "strbuf.h"
47
48 #if !defined(timeradd)
49 #define timeradd(a, b, r) do { \
50 (r)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
51 (r)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
52 if ((r)->tv_usec >= 1000000) { \
53 (r)->tv_sec++; \
54 (r)->tv_usec -= 1000000; \
55 } \
56 } while (0)
57 #endif
58
59 #if !defined(timersub)
60 #define timersub(a, b, r) do { \
61 (r)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
62 (r)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
63 if ((r)->tv_usec < 0) { \
64 (r)->tv_sec--; \
65 (r)->tv_usec += 1000000; \
66 } \
67 } while (0)
68 #endif
69
70 #if HAVE_GETTIMEOFDAY
71 typedef struct timeval ELAPSED_TIME_TYPE;
72 #define GET_ELAPSED_TIME(p) gettimeofday(p, NULL)
73 #define SUB_ELAPSED_TIME(a, b, r) do { \
74 struct timeval diff; \
75 timersub(a, b, &diff); \
76 *(r) = diff.tv_sec + diff.tv_usec * 1e-6; \
77 } while (0)
78 #if HAVE_GETRUSAGE
79 typedef struct timeval CPU_TIME_TYPE;
80 #define GET_CPU_TIME(pu, ps) do { \
81 struct rusage self, children; \
82 getrusage(RUSAGE_SELF, &self); \
83 getrusage(RUSAGE_CHILDREN, &children); \
84 timeradd(&self.ru_utime, &children.ru_utime, pu); \
85 timeradd(&self.ru_stime, &children.ru_stime, ps); \
86 } while (0)
87 #define SUB_CPU_TIME SUB_ELAPSED_TIME
88 #define CPU_TIME_AVAILABLE 1
89 #else
90 #define CPU_TIME_AVAILABLE 0
91 #endif
92 #else
93 typedef time_t ELAPSED_TIME_TYPE;
94 #define GET_ELAPSED_TIME(p) time(p)
95 #define SUB_ELAPSED_TIME(a, b, r) (*(r) = *(a) - *(b))
96 #define CPU_TIME_AVAILABLE 0
97 #endif
98
99 struct statistics_time {
100 STAILQ_ENTRY(statistics_time) next;
101
102 ELAPSED_TIME_TYPE elapsed_start;
103 double elapsed; /**< Elapsed time in seconds. */
104
105 #if CPU_TIME_AVAILABLE
106 CPU_TIME_TYPE user_start;
107 CPU_TIME_TYPE system_start;
108 double user; /**< User time in seconds. */
109 double system; /**< System time in seconds. */
110 double percent; /**< (user + system) * 100 / elapsed */
111 /**< percent may be NaN or infinity. */
112 #endif
113
114 int name_len;
115 char name[1];
116 };
117
118 static STRBUF *sb;
119 static STATISTICS_TIME *T_all;
120 static STAILQ_HEAD(statistics_time_list, statistics_time)
121 statistics_time_list = STAILQ_HEAD_INITIALIZER(statistics_time_list);
122
123 void
124 init_statistics(void)
125 {
126 assert(sb == NULL);
127 sb = strbuf_open(0);
128 T_all = statistics_time_start("The entire time");
129 }
130
131 STATISTICS_TIME *
132 statistics_time_start(const char *fmt, ...)
133 {
134 STATISTICS_TIME *t;
135 va_list ap;
136
137 strbuf_reset(sb);
138
139 va_start(ap, fmt);
140 strbuf_vsprintf(sb, fmt, ap);
141 va_end(ap);
142
143 t = check_malloc(offsetof(STATISTICS_TIME, name) + strbuf_getlen(sb) + 1);
144
145 t->name_len = strbuf_getlen(sb);
146 strcpy(t->name, strbuf_value(sb));
147
148 GET_ELAPSED_TIME(&t->elapsed_start);
149
150 #if CPU_TIME_AVAILABLE
151 GET_CPU_TIME(&t->user_start, &t->system_start);
152 #endif
153
154 return t;
155 }
156
157 void
158 statistics_time_end(STATISTICS_TIME *t)
159 {
160 ELAPSED_TIME_TYPE elapsed_end;
161 #if CPU_TIME_AVAILABLE
162 CPU_TIME_TYPE user_end;
163 CPU_TIME_TYPE system_end;
164 #endif
165
166 GET_ELAPSED_TIME(&elapsed_end);
167 SUB_ELAPSED_TIME(&elapsed_end, &t->elapsed_start, &t->elapsed);
168
169 #if CPU_TIME_AVAILABLE
170 GET_CPU_TIME(&user_end, &system_end);
171 SUB_CPU_TIME(&user_end, &t->user_start, &t->user);
172 SUB_CPU_TIME(&system_end, &t->system_start, &t->system);
173
174 t->percent = (t->elapsed == 0) ? (
175 #if defined(NAN)
176 (t->user + t->system == 0) ? NAN :
177 #endif
178 #if defined(INFINITY)
179 INFINITY
180 #else
181 HUGE_VAL
182 #endif
183 ) : ((t->user + t->system) / t->elapsed * 100);
184 #endif
185
186 STAILQ_INSERT_TAIL(&statistics_time_list, t, next);
187 }
188
189 struct printing_width {
190 int name;
191 int elapsed;
192 #if CPU_TIME_AVAILABLE
193 int user;
194 int system;
195 int percent;
196 #endif
197 };
198
199 static int
200 decimal_width(unsigned long num)
201 {
202 int width = 1;
203
204 while (num >= 10) {
205 num /= 10;
206 width++;
207 }
208
209 return width;
210 }
211
212 #define ELAPSED_PRECISION 3
213 #define USER_PRECISION 3
214 #define SYSTEM_PRECISION 3
215 #define PERCENT_PRECISION 1
216
217 #define STR(x) #x
218 #define XSTR(x) STR(x)
219 #define PRECISION_STRING(x) XSTR(x##_PRECISION)
220
221 static void
222 get_max_width(struct printing_width *max_width)
223 {
224 const STATISTICS_TIME *t;
225 int w;
226 #if CPU_TIME_AVAILABLE
227 char buf[64];
228 #endif
229
230 STAILQ_FOREACH(t, &statistics_time_list, next) {
231 if (t->name_len > max_width->name)
232 max_width->name = t->name_len;
233
234 w = decimal_width(t->elapsed) + ELAPSED_PRECISION + 1;
235 if (w > max_width->elapsed)
236 max_width->elapsed = w;
237
238 #if CPU_TIME_AVAILABLE
239 w = decimal_width(t->user) + USER_PRECISION + 1;
240 if (w > max_width->user )
241 max_width->user = w;
242
243 w = decimal_width(t->system) + SYSTEM_PRECISION + 1;
244 if (w > max_width->system)
245 max_width->system = w;
246
247 /*
248 * Printing style of NaN and infinity is implementation-defined.
249 * Therefore, it is impossible to know printing width without calling snprintf.
250 */
251 w = snprintf(buf, sizeof(buf), "%." PRECISION_STRING(PERCENT) "f", t->percent);
252 if (w > max_width->percent)
253 max_width->percent = w;
254 #endif
255 }
256 }
257
258 #define MIN_DOTS_LEN 3
259
260 static void
261 print_header_list(void **ppriv)
262 {
263 struct printing_width max_width;
264 char *dots;
265
266 memset(&max_width, 0, sizeof(max_width));
267 get_max_width(&max_width);
268 *ppriv = dots = check_malloc(sizeof(max_width) + max_width.name + MIN_DOTS_LEN + 1);
269 memcpy(dots, &max_width, sizeof(max_width));
270 dots += sizeof(max_width);
271 memset(dots, '.', max_width.name + MIN_DOTS_LEN);
272 dots[max_width.name + MIN_DOTS_LEN] = '\0';
273
274 setverbose();
275 }
276
277 static void
278 print_time_list(const STATISTICS_TIME *t, void *priv)
279 {
280 const struct printing_width *max_width = priv;
281 const char *dots = (const char *)&max_width[1];
282
283 #if CPU_TIME_AVAILABLE
284 message("- %s %s"
285 " user %*." PRECISION_STRING(USER) "fs"
286 " system %*." PRECISION_STRING(SYSTEM) "fs"
287 " elapsed %*." PRECISION_STRING(ELAPSED) "fs"
288 " %*." PRECISION_STRING(PERCENT) "f%%",
289 t->name, dots + t->name_len,
290 max_width->user, t->user,
291 max_width->system, t->system,
292 max_width->elapsed, t->elapsed,
293 max_width->percent, t->percent);
294 #else
295 message("- %s %s"
296 " elapsed %*." PRECISION_STRING(ELAPSED) "fs",
297 t->name, dots + t->name_len,
298 max_width->elapsed, t->elapsed);
299 #endif
300 }
301
302 static const char name_heading_string[] = "period";
303 static const char elapsed_heading_string[] = "elapsed[sec]";
304 #if CPU_TIME_AVAILABLE
305 static const char user_heading_string[] = "user[sec]";
306 static const char system_heading_string[] = "system[sec]";
307 static const char percent_heading_string[] = "%CPU";
308 #endif
309
310 static void
311 print_header_table(void **ppriv)
312 {
313 struct printing_width max_width;
314 char *bar;
315 int bar_len;
316
317 max_width.name = sizeof(name_heading_string) - 1;
318 max_width.elapsed = sizeof(elapsed_heading_string) - 1;
319 #if CPU_TIME_AVAILABLE
320 max_width.user = sizeof(user_heading_string) - 1;
321 max_width.system = sizeof(system_heading_string) - 1;
322 max_width.percent = sizeof(percent_heading_string) - 1;
323 #endif
324 get_max_width(&max_width);
325
326 bar_len = (max_width.name > max_width.elapsed)
327 ? max_width.name : max_width.elapsed;
328 #if CPU_TIME_AVAILABLE
329 if (max_width.user > bar_len)
330 bar_len = max_width.user;
331 if (max_width.system > bar_len)
332 bar_len = max_width.system;
333 if (max_width.percent > bar_len)
334 bar_len = max_width.percent;
335 #endif
336
337 *ppriv = bar = check_malloc(sizeof(max_width) + bar_len + 1);
338 memcpy(bar, &max_width, sizeof(max_width));
339 bar += sizeof(max_width);
340 memset(bar, '-', bar_len);
341 bar[bar_len] = '\0';
342
343 setverbose();
344
345 #if CPU_TIME_AVAILABLE
346 message("%-*s %*s %*s %*s %*s",
347 max_width.name, name_heading_string,
348 max_width.user, user_heading_string,
349 max_width.system, system_heading_string,
350 max_width.elapsed, elapsed_heading_string,
351 max_width.percent, percent_heading_string);
352 message("%.*s %.*s %.*s %.*s %.*s",
353 max_width.name, bar,
354 max_width.user, bar,
355 max_width.system, bar,
356 max_width.elapsed, bar,
357 max_width.percent, bar);
358 #else
359 message("%-*s %*s",
360 max_width.name, name_heading_string,
361 max_width.elapsed, elapsed_heading_string);
362 message("%.*s %.*s",
363 max_width.name, bar,
364 max_width.elapsed, bar);
365 #endif
366 }
367
368 static void
369 print_time_table(const STATISTICS_TIME *t, void *priv)
370 {
371 const struct printing_width *max_width = priv;
372 const char *bar = (const char *)&max_width[1];
373
374 if (t == T_all) {
375 #if CPU_TIME_AVAILABLE
376 message("%.*s %.*s %.*s %.*s %.*s",
377 max_width->name, bar,
378 max_width->user, bar,
379 max_width->system, bar,
380 max_width->elapsed, bar,
381 max_width->percent, bar);
382 #else
383 message("%.*s %.*s",
384 max_width->name, bar,
385 max_width->elapsed, bar);
386 #endif
387 }
388
389 #if CPU_TIME_AVAILABLE
390 message("%-*s"
391 " %*." PRECISION_STRING(USER) "f"
392 " %*." PRECISION_STRING(SYSTEM) "f"
393 " %*." PRECISION_STRING(ELAPSED) "f"
394 " %*." PRECISION_STRING(PERCENT) "f",
395 max_width->name, t->name,
396 max_width->user, t->user,
397 max_width->system, t->system,
398 max_width->elapsed, t->elapsed,
399 max_width->percent, t->percent);
400 #else
401 message("%-*s"
402 " %*." PRECISION_STRING(ELAPSED) "f",
403 max_width->name, t->name,
404 max_width->elapsed, t->elapsed);
405 #endif
406 }
407
408 static void
409 print_footer_common(void *priv)
410 {
411 free(priv);
412 }
413
414 struct printng_style {
415 void (*print_header)(void **);
416 void (*print_time)(const STATISTICS_TIME *, void *);
417 void (*print_footer)(void *);
418 };
419
420 static const struct printng_style printing_styles[] = {
421 /* STATISTICS_STYLE_NONE */
422 { NULL, NULL, NULL },
423 /* STATISTICS_STYLE_LIST */
424 { print_header_list, print_time_list, print_footer_common },
425 /* STATISTICS_STYLE_TABLE */
426 { print_header_table, print_time_table, print_footer_common },
427 };
428
429 #if !defined(ARRAY_SIZE)
430 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
431 #endif
432
433 void
434 print_statistics(int style_no)
435 {
436 const struct printng_style *style;
437 STATISTICS_TIME *t;
438 void *priv;
439
440 assert(T_all != NULL);
441 statistics_time_end(T_all);
442
443 assert(style_no >= 0 && style_no < ARRAY_SIZE(printing_styles));
444 style = &printing_styles[style_no];
445
446 if (style->print_header != NULL)
447 style->print_header(&priv);
448
449 while (!STAILQ_EMPTY(&statistics_time_list)) {
450 t = STAILQ_FIRST(&statistics_time_list);
451
452 if (style->print_time != NULL)
453 style->print_time(t, priv);
454
455 STAILQ_REMOVE_HEAD(&statistics_time_list, next);
456 free(t);
457 }
458
459 if (style->print_footer != NULL)
460 style->print_footer(priv);
461
462 strbuf_close(sb);
463 T_all = NULL;
464 sb = NULL;
465 }
466
/* */