Skip to content
This repository was archived by the owner on Jun 16, 2025. It is now read-only.

Commit 038045f

Browse files
committed
Refactor new times builtin
Add support for using `getrusage()` if available. Change the default precision of the `times` and `time` output to be milliseconds. This exposes a bug in the formating of `time` output. See issue #1333. I'll fix that in a subsequent change. Related #16
1 parent 27777b3 commit 038045f

File tree

5 files changed

+75
-34
lines changed

5 files changed

+75
-34
lines changed

config_ast.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
#mesondefine _lib_ftruncate
109109
#mesondefine _lib_ftruncate64
110110
#mesondefine _lib_getexecname
111+
#mesondefine _lib_getrusage
111112
#mesondefine _lib_gettimeofday
112113
#mesondefine _lib_iswalnum
113114
#mesondefine _lib_iswprint

features/meson.build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ if feature_data.get('_lib_lchmod', 0) == 0
8383
endif
8484
endif
8585

86+
feature_data.set10('_lib_getrusage',
87+
cc.has_function('getrusage',
88+
prefix: '\n'.join(['#include <sys/resource.h>', '#include <sys/time.h>']),
89+
args: feature_test_args))
8690
feature_data.set10('_lib_sigqueue',
8791
cc.has_function('sigqueue', prefix: '#include <signal.h>', args: feature_test_args))
8892
# TODO: Enable iswprint() detection when we understand why doing so causes the

src/cmd/ksh93/bltins/times.c

Lines changed: 66 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
//
44
#include "config_ast.h" // IWYU pragma: keep
55

6-
#include <errno.h>
7-
#include <math.h>
86
#include <string.h>
9-
#include <sys/times.h>
7+
#include <sys/time.h>
108

119
#include "builtins.h"
1210
#include "defs.h"
@@ -15,8 +13,71 @@
1513
#include "sfio.h"
1614
#include "shcmd.h"
1715

16+
// Print user and system mode CPU times.
17+
static_fn void print_times(struct timeval utime, struct timeval stime) {
18+
int ut_min = utime.tv_sec / 60;
19+
int ut_sec = utime.tv_sec % 60;
20+
int ut_ms = utime.tv_usec / 1000;
21+
int st_min = stime.tv_sec / 60;
22+
int st_sec = stime.tv_sec % 60;
23+
int st_ms = stime.tv_usec / 1000;
24+
sfprintf(sfstdout, "%dm%d.%03ds %dm%d.%03ds\n", ut_min, ut_sec, ut_ms, st_min, st_sec, st_ms);
25+
}
26+
27+
#if _lib_getrusage
28+
29+
// Use getrusage() rather than times() since the former typically has higher resolution.
30+
#include <sys/resource.h>
31+
32+
// Print user and system mode CPU times for both the shell and its child processes.
33+
static_fn void print_cpu_times(Shell_t *shp) {
34+
UNUSED(shp);
35+
struct rusage usage;
36+
37+
// Print the time (user & system) consumed by the shell.
38+
getrusage(RUSAGE_SELF, &usage);
39+
print_times(usage.ru_utime, usage.ru_stime);
40+
// Print the time (user & system) consumed by the child processes of the shell.
41+
getrusage(RUSAGE_CHILDREN, &usage);
42+
print_times(usage.ru_utime, usage.ru_stime);
43+
}
44+
45+
#else // _lib_getrusage
46+
47+
// Use times() since getrusage() isn't available. Note that it typically has a lower resolution
48+
// which is why we prefer getrusage().
49+
#include <sys/times.h>
50+
51+
// Print user and system mode CPU times for both the shell and its child processes.
52+
static_fn void print_cpu_times(Shell_t *shp) {
53+
struct tms cpu_times;
54+
struct timeval utime, stime;
55+
double dtime;
56+
57+
times(&cpu_times);
58+
59+
// Print the time (user & system) consumed by the shell.
60+
dtime = (double)cpu_times.tms_utime / shp->gd->lim.clk_tck;
61+
utime.tv_sec = dtime / 60;
62+
utime.tv_usec = 1000000 * (dtime - utime.tv_sec);
63+
dtime = (double)cpu_times.tms_stime / shp->gd->lim.clk_tck;
64+
stime.tv_sec = dtime / 60;
65+
stime.tv_usec = 1000000 * (dtime - utime.tv_sec);
66+
print_times(utime, stime);
67+
68+
// Print the time (user & system) consumed by the child processes of the shell.
69+
dtime = (double)cpu_times.tms_cutime / shp->gd->lim.clk_tck;
70+
utime.tv_sec = dtime / 60;
71+
utime.tv_usec = 1000000 * (dtime - utime.tv_sec);
72+
dtime = (double)cpu_times.tms_cstime / shp->gd->lim.clk_tck;
73+
stime.tv_sec = dtime / 60;
74+
stime.tv_usec = 1000000 * (dtime - utime.tv_sec);
75+
print_times(utime, stime);
76+
}
77+
78+
#endif // _lib_getrusage
79+
1880
int b_times(int argc, char *argv[], Shbltin_t *context) {
19-
Shell_t *shp = context->shp;
2081
const char *cmd = argv[0];
2182

2283
while ((argc = optget(argv, sh_opttimes))) {
@@ -44,31 +105,6 @@ int b_times(int argc, char *argv[], Shbltin_t *context) {
44105
__builtin_unreachable();
45106
}
46107

47-
struct tms cpu_times;
48-
clock_t rv = times(&cpu_times);
49-
if (rv == (clock_t)-1) {
50-
errormsg(SH_DICT, ERROR_usage(2), "times() function unexpectedly failed: errno %d %s",
51-
errno, strerror(errno));
52-
__builtin_unreachable();
53-
}
54-
55-
double utime, stime, utime_min, utime_sec, stime_min, stime_sec;
56-
57-
utime = (double)cpu_times.tms_utime / shp->gd->lim.clk_tck;
58-
utime_min = floor(utime / 60);
59-
utime_sec = utime - utime_min;
60-
stime = (double)cpu_times.tms_stime / shp->gd->lim.clk_tck;
61-
stime_min = floor(stime / 60);
62-
stime_sec = stime - stime_min;
63-
sfprintf(sfstdout, "%dm%.2fs %dm%.2fs\n", (int)utime_min, utime_sec, (int)stime_min, stime_sec);
64-
65-
utime = (double)cpu_times.tms_cutime / shp->gd->lim.clk_tck;
66-
utime_min = floor(utime / 60);
67-
utime_sec = utime - utime_min;
68-
stime = (double)cpu_times.tms_cstime / shp->gd->lim.clk_tck;
69-
stime_min = floor(stime / 60);
70-
stime_sec = stime - stime_min;
71-
sfprintf(sfstdout, "%dm%.2fs %dm%.2fs\n", (int)utime_min, utime_sec, (int)stime_min, stime_sec);
72-
108+
print_cpu_times(context->shp);
73109
return 0;
74110
}

src/cmd/ksh93/data/msg.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,6 @@ const char e_prohibited[] = "login setuid/setgid shells prohibited";
187187
const char hist_fname[] = "/.sh_history";
188188
const char e_dot[] = ".";
189189
const char e_envmarker[] = "A__z";
190-
const char e_timeformat[] = "\nreal\t%2lR\nuser\t%2lU\nsys\t%2lS";
190+
const char e_timeformat[] = "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS";
191191
const char e_dict[] = "libshell";
192192
const char e_funload[] = "function, built-in or type definition for %s not found in %s";

src/cmd/ksh93/tests/time.exp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set pid [spawn $ksh]
33
expect_prompt
44

5-
set zero_time_re "\[\t \]*0m0\.\[0-9\]\[0-9\]s"
5+
set zero_time_re "\[\t \]*0m0\.\[0-9\]\[0-9\]\[0-9\]s"
66

77
# ==========
88
log_test_entry
@@ -16,7 +16,7 @@ expect_prompt
1616
# Timing a simple sleep command works.
1717
log_test_entry
1818
send "time sleep 1\r"
19-
expect -re "\r\nreal\[\t \]*0m1\.\[0-9\]\[0-9\]s\r\nuser$zero_time_re\r\nsys$zero_time_re\r\n" {
19+
expect -re "\r\nreal\[\t \]*0m1\.00\[0-9\]s\r\nuser$zero_time_re\r\nsys$zero_time_re\r\n" {
2020
puts "time sleep 1 produces expected output"
2121
}
2222
expect_prompt
@@ -25,7 +25,7 @@ expect_prompt
2525
# Timing a more complex pipeline works.
2626
log_test_entry
2727
send "time { sleep 0.05 | sleep 0.05; }\r"
28-
expect -re "\r\nreal\[\t \]*0m0\.0\[5-9\]s\r\nuser$zero_time_re\r\nsys$zero_time_re\r\n" {
28+
expect -re "\r\nreal\[\t \]*0m0\.0\[0-9\]\[0-9\]s\r\nuser$zero_time_re\r\nsys$zero_time_re\r\n" {
2929
puts "time sleep 0.05 pipeline produces expected output"
3030
}
3131
expect_prompt

0 commit comments

Comments
 (0)