1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include "libbb.h"
19
20
21typedef struct {
22 int waitstatus;
23 struct rusage ru;
24 unsigned elapsed_ms;
25} resource_t;
26
27
28
29
30#define UL unsigned long
31
32static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
33
34
35static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
36
37
38
39static const char long_format[] ALIGN1 =
40 "\tCommand being timed: \"%C\"\n"
41 "\tUser time (seconds): %U\n"
42 "\tSystem time (seconds): %S\n"
43 "\tPercent of CPU this job got: %P\n"
44 "\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
45 "\tAverage shared text size (kbytes): %X\n"
46 "\tAverage unshared data size (kbytes): %D\n"
47 "\tAverage stack size (kbytes): %p\n"
48 "\tAverage total size (kbytes): %K\n"
49 "\tMaximum resident set size (kbytes): %M\n"
50 "\tAverage resident set size (kbytes): %t\n"
51 "\tMajor (requiring I/O) page faults: %F\n"
52 "\tMinor (reclaiming a frame) page faults: %R\n"
53 "\tVoluntary context switches: %w\n"
54 "\tInvoluntary context switches: %c\n"
55 "\tSwaps: %W\n"
56 "\tFile system inputs: %I\n"
57 "\tFile system outputs: %O\n"
58 "\tSocket messages sent: %s\n"
59 "\tSocket messages received: %r\n"
60 "\tSignals delivered: %k\n"
61 "\tPage size (bytes): %Z\n"
62 "\tExit status: %x";
63
64
65
66
67static void resuse_end(pid_t pid, resource_t *resp)
68{
69 pid_t caught;
70
71
72
73 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
74 if (caught == -1 && errno != EINTR) {
75 bb_perror_msg("wait");
76 return;
77 }
78 }
79 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
80}
81
82static void printargv(char *const *argv)
83{
84 const char *fmt = " %s" + 1;
85 do {
86 printf(fmt, *argv);
87 fmt = " %s";
88 } while (*++argv);
89}
90
91
92
93
94
95
96
97
98static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
99{
100 unsigned long tmp;
101
102
103 if (pages > (LONG_MAX / pagesize)) {
104 tmp = pages / 1024;
105 return tmp * pagesize;
106 }
107
108 tmp = pages * pagesize;
109 return tmp / 1024;
110}
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156#ifndef TICKS_PER_SEC
157#define TICKS_PER_SEC 100
158#endif
159
160static void summarize(const char *fmt, char **command, resource_t *resp)
161{
162 unsigned vv_ms;
163 unsigned cpu_ticks;
164 unsigned pagesize = getpagesize();
165
166
167
168
169
170
171 if (WIFSIGNALED(resp->waitstatus))
172 printf("Command terminated by signal %u\n",
173 WTERMSIG(resp->waitstatus));
174 else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
175 printf("Command exited with non-zero status %u\n",
176 WEXITSTATUS(resp->waitstatus));
177
178 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
179 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
180
181#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
182
183 cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
184#else
185 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
186#endif
187 if (!cpu_ticks) cpu_ticks = 1;
188
189 while (*fmt) {
190
191 int n = strcspn(fmt, "%\\");
192 if (n) {
193 printf("%.*s", n, fmt);
194 fmt += n;
195 continue;
196 }
197
198 switch (*fmt) {
199#ifdef NOT_NEEDED
200
201
202
203 default:
204 bb_putchar(*fmt);
205 break;
206#endif
207
208 case '%':
209 switch (*++fmt) {
210#ifdef NOT_NEEDED_YET
211
212
213 default:
214 bb_putchar('%');
215
216 case '%':
217 if (!*fmt) goto ret;
218 bb_putchar(*fmt);
219 break;
220#endif
221 case 'C':
222 printargv(command);
223 break;
224 case 'D':
225 printf("%lu",
226 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
227 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
228 break;
229 case 'E': {
230 unsigned seconds = resp->elapsed_ms / 1000;
231 if (seconds >= 3600)
232 printf("%uh %um %02us",
233 seconds / 3600,
234 (seconds % 3600) / 60,
235 seconds % 60);
236 else
237 printf("%um %u.%02us",
238 seconds / 60,
239 seconds % 60,
240 (unsigned)(resp->elapsed_ms / 10) % 100);
241 break;
242 }
243 case 'F':
244 printf("%lu", resp->ru.ru_majflt);
245 break;
246 case 'I':
247 printf("%lu", resp->ru.ru_inblock);
248 break;
249 case 'K':
250 printf("%lu",
251 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
252 ptok(pagesize, (UL) resp->ru.ru_isrss) +
253 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
254 break;
255 case 'M':
256 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
257 break;
258 case 'O':
259 printf("%lu", resp->ru.ru_oublock);
260 break;
261 case 'P':
262
263 if (resp->elapsed_ms > 0)
264 printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
265 else
266 printf("?%%");
267 break;
268 case 'R':
269 printf("%lu", resp->ru.ru_minflt);
270 break;
271 case 'S':
272 printf("%u.%02u",
273 (unsigned)resp->ru.ru_stime.tv_sec,
274 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
275 break;
276 case 'T':
277 if (resp->ru.ru_stime.tv_sec >= 3600)
278 printf("%uh %um %02us",
279 (unsigned)(resp->ru.ru_stime.tv_sec / 3600),
280 (unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
281 (unsigned)(resp->ru.ru_stime.tv_sec % 60));
282 else
283 printf("%um %u.%02us",
284 (unsigned)(resp->ru.ru_stime.tv_sec / 60),
285 (unsigned)(resp->ru.ru_stime.tv_sec % 60),
286 (unsigned)(resp->ru.ru_stime.tv_usec / 10000));
287 break;
288 case 'U':
289 printf("%u.%02u",
290 (unsigned)resp->ru.ru_utime.tv_sec,
291 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
292 break;
293 case 'u':
294 if (resp->ru.ru_utime.tv_sec >= 3600)
295 printf("%uh %um %02us",
296 (unsigned)(resp->ru.ru_utime.tv_sec / 3600),
297 (unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
298 (unsigned)(resp->ru.ru_utime.tv_sec % 60));
299 else
300 printf("%um %u.%02us",
301 (unsigned)(resp->ru.ru_utime.tv_sec / 60),
302 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
303 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
304 break;
305 case 'W':
306 printf("%lu", resp->ru.ru_nswap);
307 break;
308 case 'X':
309 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
310 break;
311 case 'Z':
312 printf("%u", pagesize);
313 break;
314 case 'c':
315 printf("%lu", resp->ru.ru_nivcsw);
316 break;
317 case 'e':
318 printf("%u.%02u",
319 (unsigned)resp->elapsed_ms / 1000,
320 (unsigned)(resp->elapsed_ms / 10) % 100);
321 break;
322 case 'k':
323 printf("%lu", resp->ru.ru_nsignals);
324 break;
325 case 'p':
326 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
327 break;
328 case 'r':
329 printf("%lu", resp->ru.ru_msgrcv);
330 break;
331 case 's':
332 printf("%lu", resp->ru.ru_msgsnd);
333 break;
334 case 't':
335 printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
336 break;
337 case 'w':
338 printf("%lu", resp->ru.ru_nvcsw);
339 break;
340 case 'x':
341 printf("%u", WEXITSTATUS(resp->waitstatus));
342 break;
343 }
344 break;
345
346#ifdef NOT_NEEDED_YET
347 case '\\':
348 switch (*++fmt) {
349 default:
350 bb_putchar('\\');
351
352 case '\\':
353 if (!*fmt) goto ret;
354 bb_putchar(*fmt);
355 break;
356 case 't':
357 bb_putchar('\t');
358 break;
359 case 'n':
360 bb_putchar('\n');
361 break;
362 }
363 break;
364#endif
365 }
366 ++fmt;
367 }
368
369 bb_putchar('\n');
370}
371
372
373
374static void run_command(char *const *cmd, resource_t *resp)
375{
376 pid_t pid;
377 void (*interrupt_signal)(int);
378 void (*quit_signal)(int);
379
380 resp->elapsed_ms = monotonic_ms();
381 pid = xvfork();
382 if (pid == 0) {
383
384 BB_EXECVP_or_die((char**)cmd);
385 }
386
387
388
389 interrupt_signal = signal(SIGINT, SIG_IGN);
390 quit_signal = signal(SIGQUIT, SIG_IGN);
391
392 resuse_end(pid, resp);
393
394
395 signal(SIGINT, interrupt_signal);
396 signal(SIGQUIT, quit_signal);
397}
398
399int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
400int time_main(int argc UNUSED_PARAM, char **argv)
401{
402 resource_t res;
403 const char *output_format = default_format;
404 int opt;
405
406 opt_complementary = "-1";
407
408 opt = getopt32(argv, "+vp");
409 argv += optind;
410 if (opt & 1)
411 output_format = long_format;
412 if (opt & 2)
413 output_format = posix_format;
414
415 run_command(argv, &res);
416
417
418 xdup2(STDERR_FILENO, STDOUT_FILENO);
419 summarize(output_format, argv, &res);
420
421 if (WIFSTOPPED(res.waitstatus))
422 return WSTOPSIG(res.waitstatus);
423 if (WIFSIGNALED(res.waitstatus))
424 return WTERMSIG(res.waitstatus);
425 if (WIFEXITED(res.waitstatus))
426 return WEXITSTATUS(res.waitstatus);
427 fflush_stdout_and_exit(EXIT_SUCCESS);
428}
429