logo

utils-std

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

time.c (3683B)


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