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