logo

utils-std

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

time.c (3685B)


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