root/libutil/statistics.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_statistics
  2. statistics_time_start
  3. statistics_time_end
  4. decimal_width
  5. get_max_width
  6. print_header_list
  7. print_time_list
  8. print_header_table
  9. print_time_table
  10. print_footer_common
  11. 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 

/* [previous][next][first][last][top][bottom][index][help] */