/* $Id: kmk_time.c 3336 2020-04-22 12:08:35Z bird $ */ /** @file * kmk_time - Time program execution. * * This is based on kmk/kmkbuiltin/redirect.c. */ /* * Copyright (c) 2007-2010 knut st. osmundsen * * This file is part of kBuild. * * kBuild is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * kBuild is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kBuild. If not, see * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include #include #include #include #include #if defined(_MSC_VER) # include # include # include # include # include "quote_argv.h" #else # include # include # include # include #endif #ifdef __OS2__ # define INCL_BASE # include # ifndef LIBPATHSTRICT # define LIBPATHSTRICT 3 # endif #endif #ifndef _MSC_VER static const char *my_strsignal(int signo) { #define CASE_SIG_RET_STR(sig) if (signo == SIG##sig) return #sig #ifdef SIGHUP CASE_SIG_RET_STR(HUP); #endif #ifdef SIGINT CASE_SIG_RET_STR(INT); #endif #ifdef SIGQUIT CASE_SIG_RET_STR(QUIT); #endif #ifdef SIGILL CASE_SIG_RET_STR(ILL); #endif #ifdef SIGTRAP CASE_SIG_RET_STR(TRAP); #endif #ifdef SIGABRT CASE_SIG_RET_STR(ABRT); #endif #ifdef SIGIOT CASE_SIG_RET_STR(IOT); #endif #ifdef SIGBUS CASE_SIG_RET_STR(BUS); #endif #ifdef SIGFPE CASE_SIG_RET_STR(FPE); #endif #ifdef SIGKILL CASE_SIG_RET_STR(KILL); #endif #ifdef SIGUSR1 CASE_SIG_RET_STR(USR1); #endif #ifdef SIGSEGV CASE_SIG_RET_STR(SEGV); #endif #ifdef SIGUSR2 CASE_SIG_RET_STR(USR2); #endif #ifdef SIGPIPE CASE_SIG_RET_STR(PIPE); #endif #ifdef SIGALRM CASE_SIG_RET_STR(ALRM); #endif #ifdef SIGTERM CASE_SIG_RET_STR(TERM); #endif #ifdef SIGSTKFLT CASE_SIG_RET_STR(STKFLT); #endif #ifdef SIGCHLD CASE_SIG_RET_STR(CHLD); #endif #ifdef SIGCONT CASE_SIG_RET_STR(CONT); #endif #ifdef SIGSTOP CASE_SIG_RET_STR(STOP); #endif #ifdef SIGTSTP CASE_SIG_RET_STR(TSTP); #endif #ifdef SIGTTIN CASE_SIG_RET_STR(TTIN); #endif #ifdef SIGTTOU CASE_SIG_RET_STR(TTOU); #endif #ifdef SIGURG CASE_SIG_RET_STR(URG); #endif #ifdef SIGXCPU CASE_SIG_RET_STR(XCPU); #endif #ifdef SIGXFSZ CASE_SIG_RET_STR(XFSZ); #endif #ifdef SIGVTALRM CASE_SIG_RET_STR(VTALRM); #endif #ifdef SIGPROF CASE_SIG_RET_STR(PROF); #endif #ifdef SIGWINCH CASE_SIG_RET_STR(WINCH); #endif #ifdef SIGIO CASE_SIG_RET_STR(IO); #endif #ifdef SIGPWR CASE_SIG_RET_STR(PWR); #endif #ifdef SIGSYS CASE_SIG_RET_STR(SYS); #endif #ifdef SIGBREAK CASE_SIG_RET_STR(BREAK); #endif #undef CASE_SIG_RET_STR return "???"; } #endif /* unix */ static const char *name(const char *pszName) { const char *psz = strrchr(pszName, '/'); #if defined(_MSC_VER) || defined(__OS2__) const char *psz2 = strrchr(pszName, '\\'); if (!psz2) psz2 = strrchr(pszName, ':'); if (psz2 && (!psz || psz2 > psz)) psz = psz2; #endif return psz ? psz + 1 : pszName; } static int usage(FILE *pOut, const char *argv0) { fprintf(pOut, "usage: %s [options] [--] [args]\n" " or: %s --help\n" " or: %s --version\n" "\n" "Options:\n" " -i , --iteration \n" " Run the program times and display minium, maximum and average\n" " run times at the end.\n" , argv0, argv0, argv0); #ifdef _MSC_VER fprintf(pOut, " --unquoted\n" " Windows only: No argument quoting, use them as-is.\n"); #endif return 1; } int main(int argc, char **argv) { int i, j; int cTimes = 1; #if defined(_MSC_VER) int fUnquoted = 0; FILETIME ftStart, ft; unsigned _int64 usMin, usMax, usAvg, usTotal, usCur; unsigned _int64 iStart; intptr_t rc; #else struct timeval tvStart, tv; unsigned long long usMin, usMax, usAvg, usTotal, usCur; pid_t pid; int rc; #endif int rcExit = 0; /* * Parse arguments. */ if (argc <= 1) return usage(stderr, name(argv[0])); for (i = 1; i < argc; i++) { char *psz = &argv[i][0]; if (*psz++ != '-') break; if (*psz == '-') { /* '--' ? */ if (!psz[1]) { i++; break; } /* convert to short. */ if (!strcmp(psz, "-help")) psz = "h"; else if (!strcmp(psz, "-version")) psz = "V"; else if (!strcmp(psz, "-iterations")) psz = "i"; #if defined(_MSC_VER) else if (!strcmp(psz, "-unquoted")) { fUnquoted = 1; continue; } #endif } switch (*psz) { case 'h': usage(stdout, name(argv[0])); return 0; case 'V': printf("kmk_time - kBuild version %d.%d.%d (r%u)\n" "Copyright (C) 2007-2018 knut st. osmundsen\n", KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH, KBUILD_SVN_REV); return 0; case 'i': if (i + 1 >= argc) { fprintf(stderr, "%s: syntax error: missing iteration count\n", name(argv[0])); return 1; } cTimes = atoi(argv[++i]); if (cTimes <= 0) { fprintf(stderr, "%s: error: invalid interation count '%s'.\n", name(argv[0]), argv[i]); return 1; } break; default: fprintf(stderr, "%s: error: syntax error '%s'\n", name(argv[0]), argv[i]); return 1; } } /* * Make sure there's something to execute. */ if (i >= argc) { fprintf(stderr, "%s: syntax error: nothing to execute!\n", name(argv[0])); return usage(stderr, name(argv[0])); } /* * Execute the program the specified number of times. */ usMax = usMin = usTotal = 0; usMin--; /* wraps to max value */ for (j = 0; j < cTimes; j++) { /* * Execute the program (it's actually supposed to be a command I think, but wtf). */ #if defined(_MSC_VER) if (!fUnquoted) { if (quote_argv(argc - i, &argv[i], 0 /*fWatcomBrainDamage*/, 0 /*fFreeOrLeak*/) != 0) { fprintf(stderr, "%s: error: quote_argv failed\n"); return 8; } fUnquoted = 1; /* Don't quote them again in the next iteration. */ } GetSystemTimeAsFileTime(&ftStart); rc = _spawnvp(_P_WAIT, argv[i], &argv[i]); if (rc == -1) { fprintf(stderr, "%s: error: _spawnvp(_P_WAIT, \"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno)); return 8; } GetSystemTimeAsFileTime(&ft); iStart = ftStart.dwLowDateTime | ((unsigned _int64)ftStart.dwHighDateTime << 32); usCur = ft.dwLowDateTime | ((unsigned _int64)ft.dwHighDateTime << 32); usCur -= iStart; usCur /= 10; /* to usecs */ printf("%s: ", name(argv[0])); if (cTimes != 1) printf("#%02u ", j + 1); printf("%um%u.%06us - exit code: %d\n", (unsigned)(usCur / (60 * 1000000)), (unsigned)(usCur % (60 * 1000000)) / 1000000, (unsigned)(usCur % 1000000), rc); #else /* unix: */ gettimeofday(&tvStart, NULL); pid = fork(); if (!pid) { /* child */ execvp(argv[i], &argv[i]); fprintf(stderr, "%s: error: _execvp(\"%s\", ...) failed: %s\n", name(argv[0]), argv[i], strerror(errno)); return 8; } if (pid < 0) { fprintf(stderr, "%s: error: fork() failed: %s\n", name(argv[0]), strerror(errno)); return 9; } /* parent, wait for child. */ rc = 9; while (waitpid(pid, &rc, 0) == -1 && errno == EINTR) /* nothing */; gettimeofday(&tv, NULL); /* calc elapsed time */ tv.tv_sec -= tvStart.tv_sec; if (tv.tv_usec > tvStart.tv_usec) tv.tv_usec -= tvStart.tv_usec; else { tv.tv_sec--; tv.tv_usec = tv.tv_usec + 1000000 - tvStart.tv_usec; } usCur = tv.tv_sec * 1000000ULL + tv.tv_usec; printf("%s: ", name(argv[0])); if (cTimes != 1) printf("#%02u ", j + 1); printf("%um%u.%06us", (unsigned)(tv.tv_sec / 60), (unsigned)(tv.tv_sec % 60), (unsigned)tv.tv_usec); if (WIFEXITED(rc)) { printf(" - normal exit: %d\n", WEXITSTATUS(rc)); rc = WEXITSTATUS(rc); } # ifndef __HAIKU__ /**@todo figure how haiku signals that a core was dumped. */ else if (WIFSIGNALED(rc) && WCOREDUMP(rc)) { printf(" - dumped core: %s (%d)\n", my_strsignal(WTERMSIG(rc)), WTERMSIG(rc)); rc = 10; } # endif else if (WIFSIGNALED(rc)) { printf(" - killed by: %s (%d)\n", my_strsignal(WTERMSIG(rc)), WTERMSIG(rc)); rc = 11; } else if (WIFSTOPPED(rc)) { printf(" - stopped by: %s (%d)\n", my_strsignal(WSTOPSIG(rc)), WSTOPSIG(rc)); rc = 12; } else { printf(" unknown exit status %#x (%d)\n", rc, rc); rc = 13; } #endif /* unix */ if (rc && !rcExit) rcExit = (int)rc; /* calc min/max/avg */ usTotal += usCur; if (usMax < usCur) usMax = usCur; if (usMin > usCur) usMin = usCur; } /* * Summary if more than one run. */ if (cTimes != 1) { usAvg = usTotal / cTimes; printf("%s: avg %um%u.%06us\n", name(argv[0]), (unsigned)(usAvg / 60000000), (unsigned)(usAvg % 60000000) / 1000000, (unsigned)(usAvg % 1000000)); printf("%s: min %um%u.%06us\n", name(argv[0]), (unsigned)(usMin / 60000000), (unsigned)(usMin % 60000000) / 1000000, (unsigned)(usMin % 1000000)); printf("%s: max %um%u.%06us\n", name(argv[0]), (unsigned)(usMax / 60000000), (unsigned)(usMax % 60000000) / 1000000, (unsigned)(usMax % 1000000)); } return rcExit; }