time.c (3683B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include <errno.h> // errno
- #include <stdio.h> // perror, fprintf
- #include <stdlib.h> // abort
- #include <string.h> // strerror
- #include <sys/resource.h> // getrusage
- #include <sys/times.h> // times
- #include <sys/wait.h> // waitpid
- #include <unistd.h> // sysconf, fork, execvp, getopt
- enum cmd_time_mode
- {
- CMD_TIME_POSIX = 0,
- CMD_TIME_VERBOSE = 1,
- };
- static void
- usage(void)
- {
- fprintf(stderr, "Usage: time [-p|-v] command [argument ...]\n");
- }
- int
- main(int argc, char *argv[])
- {
- struct tms tms;
- int ret = 0;
- enum cmd_time_mode mode = CMD_TIME_POSIX;
- if(argc <= 1)
- {
- usage();
- return 0;
- }
- int c = -1;
- while((c = getopt(argc, argv, ":pv")) != -1)
- {
- switch(c)
- {
- case 'p': // POSIX format (default)
- mode = CMD_TIME_POSIX;
- break;
- case 'v':
- mode = CMD_TIME_VERBOSE;
- break;
- case ':':
- fprintf(stderr, "time: error: Missing operand for option: '-%c'\n", optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "time: error: Unrecognised option: '-%c'\n", optopt);
- usage();
- return 1;
- }
- }
- argc -= optind;
- argv += optind;
- (void)argc;
- long ticks = sysconf(_SC_CLK_TCK);
- if(ticks <= 0)
- {
- perror("time: error: sysconf(_SC_CLK_TCK)");
- return 1;
- }
- clock_t t0 = times(&tms);
- if(t0 == (clock_t)-1)
- {
- perror("time: error: times");
- return 1;
- }
- // Note: posix_spawnp seems to lack enough error information
- pid_t pid = fork();
- switch(pid)
- {
- case -1:
- perror("time: error: fork");
- return 1;
- case 0:
- execvp(argv[0], argv);
- ret = 126 + (errno == ENOENT);
- fprintf(stderr, "time: error: Failed executing '%s': %s\n", argv[0], strerror(errno));
- return ret;
- default:
- break;
- }
- int status = 0;
- waitpid(pid, &status, 0);
- int t1 = times(&tms);
- if(t1 == (clock_t)-1)
- {
- perror("time: error: times");
- return 1;
- }
- if(WIFSIGNALED(status))
- {
- fprintf(stderr, "time: error: Command terminated by signal %d\n", WTERMSIG(status));
- ret = 128 + WTERMSIG(status);
- }
- struct rusage usage;
- if(getrusage(RUSAGE_CHILDREN, &usage) != 0)
- {
- fprintf(
- stderr, "time: error: Failed getting resource usage of children: %s\n", strerror(errno));
- return 1;
- }
- double utime_s = usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec * 0.000001);
- double stime_s = usage.ru_stime.tv_sec + (usage.ru_stime.tv_usec * 0.000001);
- switch(mode)
- {
- case CMD_TIME_POSIX:
- fprintf(stderr, "real %f\nuser %f\nsys %f\n", (t1 - t0) / (double)ticks, utime_s, stime_s);
- break;
- case CMD_TIME_VERBOSE:
- fprintf(stderr, "\tExecutable: %s\n", argv[0]);
- fprintf(stderr, "\tReal time: %06f\n", (t1 - t0) / (double)ticks);
- fprintf(stderr, "\tUser time: %06f\n", utime_s);
- fprintf(stderr, "\tSystem time: %06f\n", stime_s);
- #ifdef __linux__
- fprintf(stderr, "\tMaximum resident set size (maxrss): %ld kB\n", usage.ru_maxrss);
- fprintf(stderr, "\tMinor (no I/O required) page faults (minflt): %ld\n", usage.ru_minflt);
- fprintf(stderr, "\tMajor (I/O required) page faults (majflt): %ld\n", usage.ru_majflt);
- fprintf(stderr, "\tMajor (I/O required) page faults (majflt): %ld\n", usage.ru_majflt);
- fprintf(stderr, "\tVoluntary context switch (nvcsw): %ld\n", usage.ru_nvcsw);
- fprintf(stderr, "\tInvoluntary context switch (nivcsw): %ld\n", usage.ru_nivcsw);
- #endif
- if(WIFEXITED(status)) fprintf(stderr, "\tExit status: %d\n", WEXITSTATUS(status));
- break;
- default:
- abort();
- }
- if(WIFEXITED(status))
- {
- ret = WEXITSTATUS(status);
- }
- return ret;
- }