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 
467