logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

time.c (4522B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "../libutils/getopt_nolong.h"
  6. #include <errno.h>
  7. #include <signal.h>
  8. #include <spawn.h>
  9. #include <stdio.h> // perror, fprintf
  10. #include <stdlib.h> // abort
  11. #include <string.h> // strerror
  12. #include <sys/resource.h> // getrusage
  13. #include <sys/times.h>
  14. #include <sys/wait.h> // waitpid
  15. #include <unistd.h> // sysconf, getopt
  16. extern char **environ;
  17. const char *argv0 = "time";
  18. static pid_t child = -1;
  19. enum cmd_time_mode
  20. {
  21. CMD_TIME_POSIX = 0,
  22. CMD_TIME_VERBOSE = 1,
  23. };
  24. static void
  25. usage(void)
  26. {
  27. fprintf(stderr, "Usage: time [-p|-v] command [argument ...]\n");
  28. }
  29. int
  30. main(int argc, char *argv[])
  31. {
  32. struct tms tms;
  33. int ret = 0;
  34. enum cmd_time_mode mode = CMD_TIME_POSIX;
  35. if(argc <= 1)
  36. {
  37. usage();
  38. return 0;
  39. }
  40. for(int c = -1; (c = getopt_nolong(argc, argv, ":pv")) != -1;)
  41. {
  42. switch(c)
  43. {
  44. case 'p': // POSIX format (default)
  45. mode = CMD_TIME_POSIX;
  46. break;
  47. case 'v':
  48. mode = CMD_TIME_VERBOSE;
  49. break;
  50. case ':':
  51. fprintf(stderr, "time: error: Missing operand for option: '-%c'\n", optopt);
  52. usage();
  53. return 1;
  54. case '?':
  55. GETOPT_UNKNOWN_OPT
  56. usage();
  57. return 1;
  58. }
  59. }
  60. argc -= optind;
  61. argv += optind;
  62. (void)argc;
  63. long ticks = sysconf(_SC_CLK_TCK);
  64. if(ticks <= 0)
  65. {
  66. perror("time: error: sysconf(_SC_CLK_TCK)");
  67. return 1;
  68. }
  69. clock_t t0 = times(&tms);
  70. if(t0 == (clock_t)-1)
  71. {
  72. perror("time: error: times");
  73. return 1;
  74. }
  75. sigset_t sigset;
  76. if(sigfillset(&sigset) != 0) return 1;
  77. if(sigprocmask(SIG_BLOCK, &sigset, NULL) != 0)
  78. {
  79. fprintf(stderr, "time: error: Failed setting process' signal mask: %s\n", strerror(errno));
  80. return 1;
  81. }
  82. if(posix_spawnp(&child, argv[0], NULL, NULL, argv, environ) != 0)
  83. {
  84. ret = 126 + (errno == ENOENT);
  85. fprintf(stderr, "time: error: Failed executing '%s': %s\n", argv[0], strerror(errno));
  86. return ret;
  87. }
  88. int status = 0;
  89. // Loop so signals are repeateadly propagated until child terminates
  90. while(true)
  91. {
  92. int sig = -1;
  93. if(sigwait(&sigset, &sig) != 0)
  94. {
  95. fprintf(stderr, "time: error: Failed waiting for signals: %s\n", strerror(errno));
  96. return 1;
  97. }
  98. int ret = waitpid(child, &status, WNOHANG);
  99. if(ret == child)
  100. {
  101. child = -1;
  102. break;
  103. }
  104. else if(ret == -1)
  105. {
  106. fprintf(stderr, "time: error: Failed waiting for child process: %s\n", strerror(errno));
  107. return 1;
  108. }
  109. if(kill(child, sig) != 0)
  110. {
  111. // candicate for sig2str
  112. fprintf(stderr,
  113. "time: warning: Failed propagating signal number %d to child process (%d): %s\n",
  114. sig,
  115. child,
  116. strerror(errno));
  117. }
  118. }
  119. clock_t t1 = times(&tms);
  120. if(t1 == -1)
  121. {
  122. perror("time: error: times");
  123. return 1;
  124. }
  125. if(WIFSIGNALED(status))
  126. {
  127. fprintf(stderr, "time: error: Command terminated by signal %d\n", WTERMSIG(status));
  128. ret = 128 + WTERMSIG(status);
  129. }
  130. struct rusage usage;
  131. if(getrusage(RUSAGE_CHILDREN, &usage) != 0)
  132. {
  133. fprintf(
  134. stderr, "time: error: Failed getting resource usage of children: %s\n", strerror(errno));
  135. return 1;
  136. }
  137. double utime_s = usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec * 0.000001);
  138. double stime_s = usage.ru_stime.tv_sec + (usage.ru_stime.tv_usec * 0.000001);
  139. switch(mode)
  140. {
  141. case CMD_TIME_POSIX:
  142. fprintf(stderr, "real %f\nuser %f\nsys %f\n", (t1 - t0) / (double)ticks, utime_s, stime_s);
  143. break;
  144. case CMD_TIME_VERBOSE:
  145. fprintf(stderr, "\tExecutable: %s\n", argv[0]);
  146. fprintf(stderr, "\tReal time : %06f\n", (t1 - t0) / (double)ticks);
  147. fprintf(stderr, "\tUser time : %06f\n", utime_s);
  148. fprintf(stderr, "\tSystem time: %06f\n", stime_s);
  149. #ifdef __linux__
  150. fprintf(stderr, "\tMaximum resident set size (maxrss): %ld kB\n", usage.ru_maxrss);
  151. fprintf(stderr, "\tMinor (no I/O required) page faults (minflt): %ld\n", usage.ru_minflt);
  152. fprintf(stderr, "\tMajor (I/O required) page faults (majflt): %ld\n", usage.ru_majflt);
  153. fprintf(stderr, "\tMajor (I/O required) page faults (majflt): %ld\n", usage.ru_majflt);
  154. fprintf(stderr, "\tVoluntary context switch (nvcsw): %ld\n", usage.ru_nvcsw);
  155. fprintf(stderr, "\tInvoluntary context switch (nivcsw): %ld\n", usage.ru_nivcsw);
  156. #endif
  157. if(WIFEXITED(status)) fprintf(stderr, "\tExit status: %d\n", WEXITSTATUS(status));
  158. break;
  159. default:
  160. abort();
  161. }
  162. if(WIFEXITED(status))
  163. {
  164. ret = WEXITSTATUS(status);
  165. }
  166. return ret;
  167. }