Skip to content
7 changes: 7 additions & 0 deletions es.h
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,13 @@ extern void blocksignals(void);
extern void unblocksignals(void);


/* prim-sys.c */

#if BUILTIN_TIME
extern void setbasetime(void);
#endif


/* open.c */

typedef enum { oOpen, oCreate, oAppend, oReadWrite, oReadCreate, oReadAppend } OpenKind;
Expand Down
13 changes: 12 additions & 1 deletion initial.es
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,18 @@ fn-unwind-protect = $&noreturn @ body cleanup {
# and get time from /bin or wherever.

if {~ <=$&primitives limit} {fn-limit = $&limit}
if {~ <=$&primitives time} {fn-time = $&time}
if {~ <=$&primitives time} {
fn time cmd {
$&collect
let ((str times) = <=$&time)
unwind-protect {
$cmd
} {
(str times) = <={$&time $times}
echo >[1=2] $str^\t^$^cmd
}
}
}

# These builtins are mainly useful for internal functions, but
# they're there to be called if you want to use them.
Expand Down
3 changes: 3 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ int main(int argc, char **argv0) {

initconv();
initgc();
#if BUILTIN_TIME
setbasetime();
#endif

if (argc == 0) {
argc = 1;
Expand Down
105 changes: 57 additions & 48 deletions prim-sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,30 +299,32 @@ struct times {
intmax_t sys_usec;
};

static void tmerrchk(int result, char *str) {
if (result == -1)
static void tmerrchk(int result, char *str, Boolean throw) {
if (result != -1)
return;
if (throw)
fail("$&time", "%s: %s", str, esstrerror(errno));
eprint("%s: %s\n", str, esstrerror(errno));
eprint("Calls to `$&time` or `time` in this shell may produce bad values.\n");
}

static void getrealtime(struct times *ret) {
#if HAVE_GETTIMEOFDAY && MILLISECOND_TIME
#define HAVE_PRECISE_REALTIME 1
static void getrealtime(struct times *ret, Boolean throw) {
#if HAVE_GETTIMEOFDAY
struct timeval tv;
tmerrchk(gettimeofday(&tv, NULL), "getrealtime()");
tmerrchk(gettimeofday(&tv, NULL), "getrealtime()", throw);
ret->real_usec = (tv.tv_sec * INTMAX_C(1000000)) + tv.tv_usec;
#else /* use time(3p) */
#define HAVE_PRECISE_REALTIME 0
time_t t = time(NULL);
tmerrchk(t, "getrealtime()");
tmerrchk(t, "getrealtime()", throw);
ret->real_usec = t * 1000000;
#endif
}

static void getusagetimes(struct times *ret) {
static void getusagetimes(struct times *ret, Boolean throw) {
#if HAVE_GETRUSAGE
struct rusage ru_self, ru_child;
tmerrchk(getrusage(RUSAGE_SELF, &ru_self), "getrusage(RUSAGE_SELF)");
tmerrchk(getrusage(RUSAGE_CHILDREN, &ru_child), "getrusage(RUSAGE_CHILDREN)");
tmerrchk(getrusage(RUSAGE_SELF, &ru_self), "getrusage(RUSAGE_SELF)", throw);
tmerrchk(getrusage(RUSAGE_CHILDREN, &ru_child), "getrusage(RUSAGE_CHILDREN)", throw);
ret->user_usec = (ru_self.ru_utime.tv_sec * 1000000)
+ ru_self.ru_utime.tv_usec
+ (ru_child.ru_utime.tv_sec * 1000000)
Expand All @@ -336,15 +338,31 @@ static void getusagetimes(struct times *ret) {
static long mul = -1;
if (mul == -1)
mul = 1000000 / sysconf(_SC_CLK_TCK);
tmerrchk(times(&tms), "getusagetimes()");
tmerrchk(times(&tms), "getusagetimes()", throw);
ret->user_usec = ((intmax_t)tms.tms_utime + tms.tms_cutime) * mul;
ret->sys_usec = ((intmax_t)tms.tms_stime + tms.tms_cstime) * mul;
#endif
}

static void gettimes(struct times *ret) {
getrealtime(ret);
getusagetimes(ret);
static void gettimes(struct times *ret, Boolean throw) {
getrealtime(ret, throw);
getusagetimes(ret, throw);
}

static void parsetimes(List *list, struct times *ret) {
char *suffix;
if (length(list) != 3)
fail("$&time", "usage: $&time [r u s]");

ret->real_usec = strtoimax(getstr(list->term), &suffix, 10);
if (*suffix != '\0')
fail("$&time", "real-time argument not an int", list->term);
ret->user_usec = strtoimax(getstr(list->next->term), &suffix, 10);
if (*suffix != '\0')
fail("$&time", "user-time argument not an int", list->next->term);
ret->sys_usec = strtoimax(getstr(list->next->next->term), &suffix, 10);
if (*suffix != '\0')
fail("$&time", "sys-time argument not an int", list->next->next->term);
}

static void subtimes(struct times a, struct times b, struct times *ret) {
Expand All @@ -353,56 +371,47 @@ static void subtimes(struct times a, struct times b, struct times *ret) {
ret->sys_usec = a.sys_usec - b.sys_usec;
}

static void strtimes(struct times time, List *list) {
#if MILLISECOND_TIME
eprint(
#if HAVE_PRECISE_REALTIME
static char *strtimes(struct times time) {
return str(
#if HAVE_GETTIMEOFDAY
"%6.3jd"
#else
"%6jd"
#endif
"r %7.3jdu %7.3jds\t%L\n",
#if HAVE_PRECISE_REALTIME
"r %7.3jdu %7.3jds",
#if HAVE_GETTIMEOFDAY
time.real_usec / 1000,
#else
time.real_usec / 1000000,
#endif
time.user_usec / 1000,
time.sys_usec / 1000,
list, " "
);
#else
eprint(
"%6jdr %7.1jdu %7.1jds\t%L\n",
time.real_usec / 1000000,
time.user_usec / 100000,
time.sys_usec / 100000,
list, " "
time.sys_usec / 1000
);
#endif
}

static struct times base;
extern void setbasetime(void) {
gettimes(&base, FALSE);
}

PRIM(time) {
int pid, status;
struct times prev, time;

Ref(List *, lp, list);

gc(); /* do a garbage collection first to ensure reproducible results */
gettimes(&prev);
pid = efork(TRUE, FALSE);
if (pid == 0)
esexit(exitstatus(eval(lp, NULL, evalflags | eval_inchild)));
status = ewait(pid, FALSE);
gettimes(&time);
SIGCHK();
printstatus(0, status);
gettimes(&time, TRUE);
subtimes(time, base, &time);

subtimes(time, prev, &time);
strtimes(time, lp);
if (list != NULL) {
parsetimes(list, &prev);
subtimes(time, prev, &time);
}

RefEnd(lp);
return mklist(mkstr(mkstatus(status)), NULL);
gcdisable();
list = mklist(mkstr(strtimes(time)),
mklist(mkstr(str("%jd", time.real_usec)),
mklist(mkstr(str("%jd", time.user_usec)),
mklist(mkstr(str("%jd", time.sys_usec)), NULL))));
gcenable();
return list;
}
#endif /* BUILTIN_TIME */

Expand Down