OILS / benchmarks / time-helper.c View on Github | oilshell.org

218 lines, 153 significant
1#define _GNU_SOURCE // for timersub()
2#include <assert.h>
3#include <errno.h>
4#include <getopt.h>
5#include <stdbool.h>
6#include <stdio.h>
7#include <stdlib.h> // exit()
8#include <sys/resource.h> // getrusage()
9#include <sys/time.h>
10#include <sys/wait.h>
11#include <unistd.h>
12
13void die_errno(const char *message) {
14 perror(message);
15 exit(1);
16}
17
18void die(const char *message) {
19 fprintf(stderr, "time-helper: %s\n", message);
20 exit(1);
21}
22
23typedef struct Spec_t {
24 char *out_path;
25 bool append;
26
27 char delimiter; // delimiter should be tab or ,
28 bool verbose; // whether to show verbose logging
29
30 bool x; // %x status
31 bool e; // %e elapsed
32 bool y; // start time
33 bool z; // end time
34 bool U; // %U user time
35 bool S; // %S system time
36 bool M; // %M maxrss
37 bool m; // page faults, context switches, etc.
38 int argc;
39 char **argv;
40} Spec;
41
42// Write CSV/TSV cells of different types
43void int_cell(FILE *f, char delimiter, int val) {
44 if (delimiter != 0) { // NUL is invalid delimiter
45 fprintf(f, "%c%d", delimiter, val);
46 } else {
47 fprintf(f, "%d", val);
48 }
49}
50
51void time_cell(FILE *f, char delimiter, struct timeval *val) {
52 fprintf(f, "%c%ld.%06ld", delimiter, val->tv_sec, val->tv_usec);
53}
54
55int time_helper(Spec *spec, FILE *f) {
56 char *prog = spec->argv[0];
57
58 struct timeval start;
59 struct timeval end;
60
61 int status = 0;
62 switch (fork()) {
63 case -1:
64 die_errno("fork");
65 break;
66
67 case 0: // child exec
68 if (execvp(prog, spec->argv) < 0) {
69 fprintf(stderr, "time-helper: error executing '%s'\n", prog);
70 die_errno("execvp");
71 }
72 assert(0); // execvp() never returns
73
74 default: // parent measures elapsed time of child
75 if (gettimeofday(&start, NULL) < 0) {
76 die_errno("gettimeofday");
77 }
78 wait(&status);
79 if (gettimeofday(&end, NULL) < 0) {
80 die_errno("gettimeofday");
81 }
82 break;
83 }
84 // fprintf(stderr, "done waiting\n");
85
86 struct timeval elapsed;
87 timersub(&end, &start, &elapsed);
88
89 struct rusage usage;
90 getrusage(RUSAGE_CHILDREN, &usage);
91
92 // struct timeval *user = &usage.ru_utime;
93 // struct timeval *sys = &usage.ru_stime;
94
95 // this is like the definition of $? that shell use
96 int exit_status = -1;
97 if (WIFEXITED(status)) {
98 exit_status = WEXITSTATUS(status);
99 } else if (WIFSIGNALED(status)) {
100 exit_status = 128 + WTERMSIG(status);
101 } else {
102 // We didn't pass WUNTRACED, so normally we won't get this. But ptrace()
103 // will get here.
104 ;
105 }
106
107 char d = spec->delimiter;
108 // NO delimiter at first!
109 if (spec->x) {
110 int_cell(f, 0, exit_status);
111 }
112 if (spec->e) {
113 time_cell(f, d, &elapsed);
114 }
115 if (spec->y) {
116 time_cell(f, d, &start);
117 }
118 if (spec->z) {
119 time_cell(f, d, &end);
120 }
121 if (spec->U) {
122 time_cell(f, d, &usage.ru_utime);
123 }
124 if (spec->S) {
125 time_cell(f, d, &usage.ru_stime);
126 }
127 if (spec->M) {
128 int_cell(f, d, usage.ru_maxrss);
129 }
130 if (spec->m) {
131 int_cell(f, d, usage.ru_minflt);
132 int_cell(f, d, usage.ru_majflt);
133 int_cell(f, d, usage.ru_nswap);
134 int_cell(f, d, usage.ru_inblock);
135 int_cell(f, d, usage.ru_oublock);
136 int_cell(f, d, usage.ru_nsignals);
137 int_cell(f, d, usage.ru_nvcsw);
138 int_cell(f, d, usage.ru_nivcsw);
139 }
140
141 return exit_status;
142}
143
144int main(int argc, char **argv) {
145 Spec spec = {0};
146
147 spec.out_path = "/dev/null"; // default value
148
149 // http://www.gnu.org/software/libc/manual/html_node/Example-of-Getopt.html
150 // + means to be strict about flag parsing.
151 int c;
152 while ((c = getopt(argc, argv, "+o:ad:vxeyzUSMm")) != -1) {
153 switch (c) {
154 case 'o':
155 spec.out_path = optarg;
156 break;
157 case 'a':
158 spec.append = true;
159 break;
160
161 case 'd':
162 spec.delimiter = optarg[0];
163 break;
164 case 'v':
165 spec.verbose = true;
166 break;
167
168 case 'x':
169 spec.x = true;
170 break;
171 case 'e':
172 spec.e = true;
173 break;
174 case 'y':
175 spec.y = true;
176 break;
177 case 'z':
178 spec.z = true;
179 break;
180
181 // --rusage
182 case 'U':
183 spec.U = true;
184 break;
185 case 'S':
186 spec.S = true;
187 break;
188 case 'M':
189 spec.M = true;
190 break;
191
192 case 'm': // --rusage-2
193 spec.m = true;
194 break;
195
196 case '?': // getopt library will print error
197 return 2;
198
199 default:
200 abort(); // should never happen
201 }
202 }
203
204 int a = optind; // index into argv
205 if (a == argc) {
206 die("expected a command to run");
207 }
208
209 spec.argv = argv + a;
210 spec.argc = argc - a;
211
212 char *mode = spec.append ? "a" : "w";
213 FILE *f = fopen(spec.out_path, mode);
214 int exit_status = time_helper(&spec, f);
215 fclose(f);
216
217 return exit_status;
218}