logo

utils-std

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

time.c (3589B)


  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 <spawn.h>
  8. #include <stdio.h> // perror, fprintf
  9. #include <stdlib.h> // abort
  10. #include <string.h> // strerror
  11. #include <sys/resource.h> // getrusage
  12. #include <sys/times.h>
  13. #include <sys/wait.h> // waitpid
  14. #include <unistd.h> // sysconf, getopt
  15. extern char **environ;
  16. const char *argv0 = "time";
  17. enum cmd_time_mode
  18. {
  19. CMD_TIME_POSIX = 0,
  20. CMD_TIME_VERBOSE = 1,
  21. };
  22. static void
  23. usage(void)
  24. {
  25. fprintf(stderr, "Usage: time [-p|-v] command [argument ...]\n");
  26. }
  27. int
  28. main(int argc, char *argv[])
  29. {
  30. struct tms tms;
  31. int ret = 0;
  32. enum cmd_time_mode mode = CMD_TIME_POSIX;
  33. if(argc <= 1)
  34. {
  35. usage();
  36. return 0;
  37. }
  38. for(int c = -1; (c = getopt_nolong(argc, argv, ":pv")) != -1;)
  39. {
  40. switch(c)
  41. {
  42. case 'p': // POSIX format (default)
  43. mode = CMD_TIME_POSIX;
  44. break;
  45. case 'v':
  46. mode = CMD_TIME_VERBOSE;
  47. break;
  48. case ':':
  49. fprintf(stderr, "time: error: Missing operand for option: '-%c'\n", optopt);
  50. usage();
  51. return 1;
  52. case '?':
  53. GETOPT_UNKNOWN_OPT
  54. usage();
  55. return 1;
  56. }
  57. }
  58. argc -= optind;
  59. argv += optind;
  60. (void)argc;
  61. long ticks = sysconf(_SC_CLK_TCK);
  62. if(ticks <= 0)
  63. {
  64. perror("time: error: sysconf(_SC_CLK_TCK)");
  65. return 1;
  66. }
  67. clock_t t0 = times(&tms);
  68. if(t0 == (clock_t)-1)
  69. {
  70. perror("time: error: times");
  71. return 1;
  72. }
  73. pid_t child = -1;
  74. if(posix_spawnp(&child, argv[0], NULL, NULL, argv, environ) != 0)
  75. {
  76. ret = 126 + (errno == ENOENT);
  77. fprintf(stderr, "time: error: Failed executing '%s': %s\n", argv[0], strerror(errno));
  78. return ret;
  79. }
  80. int status = 0;
  81. waitpid(child, &status, 0);
  82. int t1 = times(&tms);
  83. if(t1 == (clock_t)-1)
  84. {
  85. perror("time: error: times");
  86. return 1;
  87. }
  88. if(WIFSIGNALED(status))
  89. {
  90. fprintf(stderr, "time: error: Command terminated by signal %d\n", WTERMSIG(status));
  91. ret = 128 + WTERMSIG(status);
  92. }
  93. struct rusage usage;
  94. if(getrusage(RUSAGE_CHILDREN, &usage) != 0)
  95. {
  96. fprintf(
  97. stderr, "time: error: Failed getting resource usage of children: %s\n", strerror(errno));
  98. return 1;
  99. }
  100. double utime_s = usage.ru_utime.tv_sec + (usage.ru_utime.tv_usec * 0.000001);
  101. double stime_s = usage.ru_stime.tv_sec + (usage.ru_stime.tv_usec * 0.000001);
  102. switch(mode)
  103. {
  104. case CMD_TIME_POSIX:
  105. fprintf(stderr, "real %f\nuser %f\nsys %f\n", (t1 - t0) / (double)ticks, utime_s, stime_s);
  106. break;
  107. case CMD_TIME_VERBOSE:
  108. fprintf(stderr, "\tExecutable: %s\n", argv[0]);
  109. fprintf(stderr, "\tReal time : %06f\n", (t1 - t0) / (double)ticks);
  110. fprintf(stderr, "\tUser time : %06f\n", utime_s);
  111. fprintf(stderr, "\tSystem time: %06f\n", stime_s);
  112. #ifdef __linux__
  113. fprintf(stderr, "\tMaximum resident set size (maxrss): %ld kB\n", usage.ru_maxrss);
  114. fprintf(stderr, "\tMinor (no I/O required) page faults (minflt): %ld\n", usage.ru_minflt);
  115. fprintf(stderr, "\tMajor (I/O required) page faults (majflt): %ld\n", usage.ru_majflt);
  116. fprintf(stderr, "\tMajor (I/O required) page faults (majflt): %ld\n", usage.ru_majflt);
  117. fprintf(stderr, "\tVoluntary context switch (nvcsw): %ld\n", usage.ru_nvcsw);
  118. fprintf(stderr, "\tInvoluntary context switch (nivcsw): %ld\n", usage.ru_nivcsw);
  119. #endif
  120. if(WIFEXITED(status)) fprintf(stderr, "\tExit status: %d\n", WEXITSTATUS(status));
  121. break;
  122. default:
  123. abort();
  124. }
  125. if(WIFEXITED(status))
  126. {
  127. ret = WEXITSTATUS(status);
  128. }
  129. return ret;
  130. }