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