Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/miscutils/time.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/miscutils/time.c
r821 r1770 3 3 Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc. 4 4 5 Licensed under GPL v2 or later, see file LICENSE in this tarball for details.5 Licensed under GPL version 2, see file LICENSE in this tarball for details. 6 6 */ 7 7 /* Originally written by David Keppel <pardo@cs.washington.edu>. … … 10 10 */ 11 11 12 #include "busybox.h" 13 #include <stdlib.h> 14 #include <stdio.h> 15 #include <signal.h> 16 #include <errno.h> 17 #include <getopt.h> 18 #include <string.h> 19 #include <limits.h> 20 #include <unistd.h> 21 #include <sys/types.h> /* For pid_t. */ 22 #include <sys/wait.h> 23 #include <sys/param.h> /* For getpagesize, maybe. */ 24 25 #define TV_MSEC tv_usec / 1000 26 #include <sys/resource.h> 12 #include "libbb.h" 27 13 28 14 /* Information on the resources used by a child process. */ … … 30 16 int waitstatus; 31 17 struct rusage ru; 32 struct timeval start, elapsed; /* Wallclock time of process. */18 unsigned elapsed_ms; /* Wallclock time of process. */ 33 19 } resource_t; 34 20 … … 36 22 usec = microseconds = 1/1,000,000 (1*10e-6) second. */ 37 23 38 #ifndef TICKS_PER_SEC39 #define TICKS_PER_SEC 10040 #endif41 42 /* The number of milliseconds in one `tick' used by the `rusage' structure. */43 #define MSEC_PER_TICK (1000 / TICKS_PER_SEC)44 45 /* Return the number of clock ticks that occur in M milliseconds. */46 #define MSEC_TO_TICKS(m) ((m) / MSEC_PER_TICK)47 48 24 #define UL unsigned long 49 25 50 static const char *const default_format= "real\t%E\nuser\t%u\nsys\t%T";26 static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T"; 51 27 52 28 /* The output format for the -p option .*/ 53 static const char *const posix_format= "real %e\nuser %U\nsys %S";29 static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S"; 54 30 55 31 56 32 /* Format string for printing all statistics verbosely. 57 33 Keep this output to 24 lines so users on terminals can see it all.*/ 58 static const char *const long_format=34 static const char long_format[] ALIGN1 = 59 35 "\tCommand being timed: \"%C\"\n" 60 36 "\tUser time (seconds): %U\n" … … 78 54 "\tSocket messages received: %r\n" 79 55 "\tSignals delivered: %k\n" 80 "\tPage size (bytes): %Z\n" "\tExit status: %x"; 81 82 83 /* Wait for and fill in data on child process PID. 84 Return 0 on error, 1 if ok. */ 56 "\tPage size (bytes): %Z\n" 57 "\tExit status: %x"; 58 59 60 /* Wait for and fill in data on child process PID. 61 Return 0 on error, 1 if ok. */ 85 62 86 63 /* pid_t is short on BSDI, so don't try to promote it. */ … … 88 65 { 89 66 int status; 90 91 67 pid_t caught; 92 68 … … 97 73 return 0; 98 74 } 99 100 gettimeofday(&resp->elapsed, (struct timezone *) 0); 101 resp->elapsed.tv_sec -= resp->start.tv_sec; 102 if (resp->elapsed.tv_usec < resp->start.tv_usec) { 103 /* Manually carry a one from the seconds field. */ 104 resp->elapsed.tv_usec += 1000000; 105 --resp->elapsed.tv_sec; 106 } 107 resp->elapsed.tv_usec -= resp->start.tv_usec; 108 75 resp->elapsed_ms = (monotonic_us() / 1000) - resp->elapsed_ms; 109 76 resp->waitstatus = status; 110 111 77 return 1; 112 78 } 113 79 114 /* Print ARGV to FP, with each entry in ARGV separated by FILLER. */ 115 static void fprintargv(FILE * fp, char *const *argv, const char *filler) 116 { 117 char *const *av; 118 119 av = argv; 120 fputs(*av, fp); 121 while (*++av) { 122 fputs(filler, fp); 123 fputs(*av, fp); 124 } 125 if (ferror(fp)) 126 bb_error_msg_and_die(bb_msg_write_error); 80 /* Print ARGV, with each entry in ARGV separated by FILLER. */ 81 static void printargv(char *const *argv, const char *filler) 82 { 83 fputs(*argv, stdout); 84 while (*++argv) { 85 fputs(filler, stdout); 86 fputs(*argv, stdout); 87 } 127 88 } 128 89 … … 137 98 static unsigned long ptok(unsigned long pages) 138 99 { 139 static unsigned long ps = 0;100 static unsigned long ps; 140 101 unsigned long tmp; 141 static long size = LONG_MAX;142 102 143 103 /* Initialization. */ 144 104 if (ps == 0) 145 ps = (long)getpagesize();105 ps = getpagesize(); 146 106 147 107 /* Conversion. */ 148 108 if (pages > (LONG_MAX / ps)) { /* Could overflow. */ 149 109 tmp = pages / 1024; /* Smaller first, */ 150 size = tmp * ps; /* then larger. */ 151 } else { /* Could underflow. */ 152 tmp = pages * ps; /* Larger first, */ 153 size = tmp / 1024; /* then smaller. */ 154 } 155 return size; 110 return tmp * ps; /* then larger. */ 111 } 112 /* Could underflow. */ 113 tmp = pages * ps; /* Larger first, */ 114 return tmp / 1024; /* then smaller. */ 156 115 } 157 116 158 117 /* summarize: Report on the system use of a command. 159 118 160 Copy the FMT argument to FPexcept that `%' sequences119 Print the FMT argument except that `%' sequences 161 120 have special meaning, and `\n' and `\t' are translated into 162 121 newline and tab, respectively, and `\\' is translated into `\'. … … 196 155 and dividing by elapsed real time. 197 156 198 FP is the stream to print to.199 157 FMT is the format string, interpreted as described above. 200 158 COMMAND is the command and args that are being summarized. 201 159 RESP is resource information on the command. */ 202 160 203 static void summarize(FILE * fp, const char *fmt, char **command, 204 resource_t * resp) 205 { 206 unsigned long r; /* Elapsed real milliseconds. */ 207 unsigned long v; /* Elapsed virtual (CPU) milliseconds. */ 161 #ifndef TICKS_PER_SEC 162 #define TICKS_PER_SEC 100 163 #endif 164 165 static void summarize(const char *fmt, char **command, resource_t * resp) 166 { 167 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */ 168 unsigned cpu_ticks; /* Same, in "CPU ticks" */ 208 169 209 170 if (WIFSTOPPED(resp->waitstatus)) 210 fprintf(fp, "Command stopped by signal %d\n",171 printf("Command stopped by signal %u\n", 211 172 WSTOPSIG(resp->waitstatus)); 212 173 else if (WIFSIGNALED(resp->waitstatus)) 213 fprintf(fp, "Command terminated by signal %d\n",174 printf("Command terminated by signal %u\n", 214 175 WTERMSIG(resp->waitstatus)); 215 176 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus)) 216 fprintf(fp, "Command exited with non-zero status %d\n",177 printf("Command exited with non-zero status %u\n", 217 178 WEXITSTATUS(resp->waitstatus)); 218 179 219 /* Convert all times to milliseconds. Occasionally, one of these values 220 comes out as zero. Dividing by zero causes problems, so we first 221 check the time value. If it is zero, then we take `evasive action' 222 instead of calculating a value. */ 223 224 r = resp->elapsed.tv_sec * 1000 + resp->elapsed.tv_usec / 1000; 225 226 v = resp->ru.ru_utime.tv_sec * 1000 + resp->ru.ru_utime.TV_MSEC + 227 resp->ru.ru_stime.tv_sec * 1000 + resp->ru.ru_stime.TV_MSEC; 180 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000 181 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000; 182 183 #if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000 184 /* 1000 is exactly divisible by TICKS_PER_SEC */ 185 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC); 186 #else 187 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000; 188 #endif 189 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */ 190 191 /* putchar() != putc(stdout) in glibc! */ 228 192 229 193 while (*fmt) { 194 /* Handle leading literal part */ 195 int n = strcspn(fmt, "%\\"); 196 if (n) { 197 printf("%.*s", n, fmt); 198 fmt += n; 199 continue; 200 } 201 230 202 switch (*fmt) { 203 #ifdef NOT_NEEDED 204 /* Handle literal char */ 205 /* Usually we optimize for size, but there is a limit 206 * for everything. With this we do a lot of 1-byte writes */ 207 default: 208 putc(*fmt, stdout); 209 break; 210 #endif 211 231 212 case '%': 232 213 switch (*++fmt) { 233 case '%': /* Literal '%'. */ 234 putc('%', fp); 235 break; 214 #ifdef NOT_NEEDED_YET 215 /* Our format strings do not have these */ 216 /* and we do not take format str from user */ 217 default: 218 putc('%', stdout); 219 /*FALLTHROUGH*/ 220 case '%': 221 if (!*fmt) goto ret; 222 putc(*fmt, stdout); 223 break; 224 #endif 236 225 case 'C': /* The command that got timed. */ 237 fprintargv(fp,command, " ");226 printargv(command, " "); 238 227 break; 239 228 case 'D': /* Average unshared data size. */ 240 fprintf(fp,"%lu",241 MSEC_TO_TICKS(v) == 0 ? 0 :242 ptok((UL) resp->ru.ru_i drss) / MSEC_TO_TICKS(v) +243 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v));244 break;245 case 'E': /* Elapsed real (wall clock) time. */246 if ( resp->elapsed.tv_sec>= 3600) /* One hour -> h:m:s. */247 fprintf(fp, "%ldh %ldm %02lds",248 resp->elapsed.tv_sec/ 3600,249 ( resp->elapsed.tv_sec% 3600) / 60,250 resp->elapsed.tv_sec% 60);229 printf("%lu", 230 ptok((UL) resp->ru.ru_idrss) / cpu_ticks + 231 ptok((UL) resp->ru.ru_isrss) / cpu_ticks); 232 break; 233 case 'E': { /* Elapsed real (wall clock) time. */ 234 unsigned seconds = resp->elapsed_ms / 1000; 235 if (seconds >= 3600) /* One hour -> h:m:s. */ 236 printf("%uh %um %02us", 237 seconds / 3600, 238 (seconds % 3600) / 60, 239 seconds % 60); 251 240 else 252 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */ 253 resp->elapsed.tv_sec / 60, 254 resp->elapsed.tv_sec % 60, 255 resp->elapsed.tv_usec / 10000); 256 break; 241 printf("%um %u.%02us", /* -> m:s. */ 242 seconds / 60, 243 seconds % 60, 244 (unsigned)(resp->elapsed_ms / 10) % 100); 245 break; 246 } 257 247 case 'F': /* Major page faults. */ 258 fprintf(fp, "%ld", resp->ru.ru_majflt);248 printf("%lu", resp->ru.ru_majflt); 259 249 break; 260 250 case 'I': /* Inputs. */ 261 fprintf(fp, "%ld", resp->ru.ru_inblock);251 printf("%lu", resp->ru.ru_inblock); 262 252 break; 263 253 case 'K': /* Average mem usage == data+stack+text. */ 264 fprintf(fp, "%lu", 265 MSEC_TO_TICKS(v) == 0 ? 0 : 266 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v) + 267 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v) + 268 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v)); 254 printf("%lu", 255 ptok((UL) resp->ru.ru_idrss) / cpu_ticks + 256 ptok((UL) resp->ru.ru_isrss) / cpu_ticks + 257 ptok((UL) resp->ru.ru_ixrss) / cpu_ticks); 269 258 break; 270 259 case 'M': /* Maximum resident set size. */ 271 fprintf(fp,"%lu", ptok((UL) resp->ru.ru_maxrss));260 printf("%lu", ptok((UL) resp->ru.ru_maxrss)); 272 261 break; 273 262 case 'O': /* Outputs. */ 274 fprintf(fp, "%ld", resp->ru.ru_oublock);263 printf("%lu", resp->ru.ru_oublock); 275 264 break; 276 265 case 'P': /* Percent of CPU this job got. */ 277 266 /* % cpu is (total cpu time)/(elapsed time). */ 278 if (r > 0)279 fprintf(fp, "%lu%%", (v * 100 / r));267 if (resp->elapsed_ms > 0) 268 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms)); 280 269 else 281 fprintf(fp,"?%%");270 printf("?%%"); 282 271 break; 283 272 case 'R': /* Minor page faults (reclaims). */ 284 fprintf(fp, "%ld", resp->ru.ru_minflt);273 printf("%lu", resp->ru.ru_minflt); 285 274 break; 286 275 case 'S': /* System time. */ 287 fprintf(fp, "%ld.%02ld",288 resp->ru.ru_stime.tv_sec,289 resp->ru.ru_stime.TV_MSEC / 10);276 printf("%u.%02u", 277 (unsigned)resp->ru.ru_stime.tv_sec, 278 (unsigned)(resp->ru.ru_stime.tv_usec / 10000)); 290 279 break; 291 280 case 'T': /* System time. */ 292 281 if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s. */ 293 fprintf(fp, "%ldh %ldm %02lds",294 resp->ru.ru_stime.tv_sec / 3600,295 ( resp->ru.ru_stime.tv_sec % 3600) / 60,296 resp->ru.ru_stime.tv_sec % 60);282 printf("%uh %um %02us", 283 (unsigned)(resp->ru.ru_stime.tv_sec / 3600), 284 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60, 285 (unsigned)(resp->ru.ru_stime.tv_sec % 60)); 297 286 else 298 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */299 resp->ru.ru_stime.tv_sec / 60,300 resp->ru.ru_stime.tv_sec % 60,301 resp->ru.ru_stime.tv_usec / 10000);287 printf("%um %u.%02us", /* -> m:s. */ 288 (unsigned)(resp->ru.ru_stime.tv_sec / 60), 289 (unsigned)(resp->ru.ru_stime.tv_sec % 60), 290 (unsigned)(resp->ru.ru_stime.tv_usec / 10000)); 302 291 break; 303 292 case 'U': /* User time. */ 304 fprintf(fp, "%ld.%02ld",305 resp->ru.ru_utime.tv_sec,306 resp->ru.ru_utime.TV_MSEC / 10);293 printf("%u.%02u", 294 (unsigned)resp->ru.ru_utime.tv_sec, 295 (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); 307 296 break; 308 297 case 'u': /* User time. */ 309 298 if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s. */ 310 fprintf(fp, "%ldh %ldm %02lds",311 resp->ru.ru_utime.tv_sec / 3600,312 ( resp->ru.ru_utime.tv_sec % 3600) / 60,313 resp->ru.ru_utime.tv_sec % 60);299 printf("%uh %um %02us", 300 (unsigned)(resp->ru.ru_utime.tv_sec / 3600), 301 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60, 302 (unsigned)(resp->ru.ru_utime.tv_sec % 60)); 314 303 else 315 fprintf(fp, "%ldm %ld.%02lds", /* -> m:s. */316 resp->ru.ru_utime.tv_sec / 60,317 resp->ru.ru_utime.tv_sec % 60,318 resp->ru.ru_utime.tv_usec / 10000);304 printf("%um %u.%02us", /* -> m:s. */ 305 (unsigned)(resp->ru.ru_utime.tv_sec / 60), 306 (unsigned)(resp->ru.ru_utime.tv_sec % 60), 307 (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); 319 308 break; 320 309 case 'W': /* Times swapped out. */ 321 fprintf(fp, "%ld", resp->ru.ru_nswap);310 printf("%lu", resp->ru.ru_nswap); 322 311 break; 323 312 case 'X': /* Average shared text size. */ 324 fprintf(fp, "%lu", 325 MSEC_TO_TICKS(v) == 0 ? 0 : 326 ptok((UL) resp->ru.ru_ixrss) / MSEC_TO_TICKS(v)); 313 printf("%lu", ptok((UL) resp->ru.ru_ixrss) / cpu_ticks); 327 314 break; 328 315 case 'Z': /* Page size. */ 329 fprintf(fp, "%d", getpagesize());316 printf("%u", getpagesize()); 330 317 break; 331 318 case 'c': /* Involuntary context switches. */ 332 fprintf(fp, "%ld", resp->ru.ru_nivcsw);319 printf("%lu", resp->ru.ru_nivcsw); 333 320 break; 334 321 case 'e': /* Elapsed real time in seconds. */ 335 fprintf(fp, "%ld.%02ld", 336 resp->elapsed.tv_sec, resp->elapsed.tv_usec / 10000); 322 printf("%u.%02u", 323 (unsigned)resp->elapsed_ms / 1000, 324 (unsigned)(resp->elapsed_ms / 10) % 100); 337 325 break; 338 326 case 'k': /* Signals delivered. */ 339 fprintf(fp, "%ld", resp->ru.ru_nsignals);327 printf("%lu", resp->ru.ru_nsignals); 340 328 break; 341 329 case 'p': /* Average stack segment. */ 342 fprintf(fp, "%lu", 343 MSEC_TO_TICKS(v) == 0 ? 0 : 344 ptok((UL) resp->ru.ru_isrss) / MSEC_TO_TICKS(v)); 330 printf("%lu", ptok((UL) resp->ru.ru_isrss) / cpu_ticks); 345 331 break; 346 332 case 'r': /* Incoming socket messages received. */ 347 fprintf(fp, "%ld", resp->ru.ru_msgrcv);333 printf("%lu", resp->ru.ru_msgrcv); 348 334 break; 349 335 case 's': /* Outgoing socket messages sent. */ 350 fprintf(fp, "%ld", resp->ru.ru_msgsnd);336 printf("%lu", resp->ru.ru_msgsnd); 351 337 break; 352 338 case 't': /* Average resident set size. */ 353 fprintf(fp, "%lu", 354 MSEC_TO_TICKS(v) == 0 ? 0 : 355 ptok((UL) resp->ru.ru_idrss) / MSEC_TO_TICKS(v)); 339 printf("%lu", ptok((UL) resp->ru.ru_idrss) / cpu_ticks); 356 340 break; 357 341 case 'w': /* Voluntary context switches. */ 358 fprintf(fp, "%ld", resp->ru.ru_nvcsw);342 printf("%lu", resp->ru.ru_nvcsw); 359 343 break; 360 344 case 'x': /* Exit status. */ 361 fprintf(fp, "%d", WEXITSTATUS(resp->waitstatus)); 362 break; 363 case '\0': 364 putc('?', fp); 365 return; 366 default: 367 putc('?', fp); 368 putc(*fmt, fp); 345 printf("%u", WEXITSTATUS(resp->waitstatus)); 346 break; 369 347 } 370 ++fmt;371 348 break; 372 349 350 #ifdef NOT_NEEDED_YET 373 351 case '\\': /* Format escape. */ 374 352 switch (*++fmt) { 353 default: 354 putc('\\', stdout); 355 /*FALLTHROUGH*/ 356 case '\\': 357 if (!*fmt) goto ret; 358 putc(*fmt, stdout); 359 break; 375 360 case 't': 376 putc('\t', fp);361 putc('\t', stdout); 377 362 break; 378 363 case 'n': 379 putc('\n', fp); 380 break; 381 case '\\': 382 putc('\\', fp); 383 break; 384 default: 385 putc('?', fp); 386 putc('\\', fp); 387 putc(*fmt, fp); 364 putc('\n', stdout); 365 break; 388 366 } 389 ++fmt;390 367 break; 391 392 default: 393 putc(*fmt++, fp); 368 #endif 394 369 } 395 396 if (ferror(fp)) 397 bb_error_msg_and_die(bb_msg_write_error); 398 } 399 putc('\n', fp); 400 401 if (ferror(fp)) 402 bb_error_msg_and_die(bb_msg_write_error); 370 ++fmt; 371 } 372 /* ret: */ 373 putc('\n', stdout); 403 374 } 404 375 … … 410 381 __sighandler_t interrupt_signal, quit_signal; 411 382 412 gettimeofday(&resp->start, (struct timezone *) 0);383 resp->elapsed_ms = monotonic_us() / 1000; 413 384 pid = vfork(); /* Run CMD as child process. */ 414 385 if (pid < 0) … … 417 388 /* Don't cast execvp arguments; that causes errors on some systems, 418 389 versus merely warnings if the cast is left off. */ 419 execvp(cmd[0], cmd);390 BB_EXECVP(cmd[0], cmd); 420 391 bb_error_msg("cannot run %s", cmd[0]); 421 392 _exit(errno == ENOENT ? 127 : 126); … … 434 405 } 435 406 407 int time_main(int argc, char **argv); 436 408 int time_main(int argc, char **argv) 437 409 { 438 int gotone;439 410 resource_t res; 440 411 const char *output_format = default_format; 441 442 argc--; 443 argv++;412 char c; 413 414 goto next; 444 415 /* Parse any options -- don't use getopt() here so we don't 445 416 * consume the args of our client application... */ 446 while (argc > 0 && **argv == '-') { 447 gotone = 0; 448 while (gotone == 0 && *++(*argv)) { 449 switch (**argv) { 417 while (argc > 0 && argv[0][0] == '-') { 418 while ((c = *++*argv)) { 419 switch (c) { 450 420 case 'v': 451 421 output_format = long_format; … … 457 427 bb_show_usage(); 458 428 } 459 argc--;460 argv++;461 gotone = 1;462 429 } 463 } 464 465 if (argv == NULL || *argv == NULL) 466 bb_show_usage(); 430 next: 431 argv++; 432 argc--; 433 if (!argc) 434 bb_show_usage(); 435 } 467 436 468 437 run_command(argv, &res); 469 summarize(stderr, output_format, argv, &res); 470 fflush(stderr); 438 439 /* Cheat. printf's are shorter :) */ 440 stdout = stderr; 441 dup2(2, 1); /* just in case libc does something silly :( */ 442 summarize(output_format, argv, &res); 471 443 472 444 if (WIFSTOPPED(res.waitstatus)) 473 exit(WSTOPSIG(res.waitstatus));474 elseif (WIFSIGNALED(res.waitstatus))475 exit(WTERMSIG(res.waitstatus));476 elseif (WIFEXITED(res.waitstatus))477 exit(WEXITSTATUS(res.waitstatus));478 return 0;479 } 445 return WSTOPSIG(res.waitstatus); 446 if (WIFSIGNALED(res.waitstatus)) 447 return WTERMSIG(res.waitstatus); 448 if (WIFEXITED(res.waitstatus)) 449 return WEXITSTATUS(res.waitstatus); 450 fflush_stdout_and_exit(0); 451 }
Note:
See TracChangeset
for help on using the changeset viewer.