zsh-workers
 help / color / mirror / code / Atom feed
* Bug: time doesn't work on builtins
@ 2024-08-15 13:15 Mark J. Reed
  2024-08-15 18:52 ` Bart Schaefer
  0 siblings, 1 reply; 15+ messages in thread
From: Mark J. Reed @ 2024-08-15 13:15 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 397 bytes --]

This was unexpected:

*zsh%*  time read
hello
*zsh%*


Any attempt to *time *a builtin generates no output whatsoever. No timings,
no error message.The timed command executes in the current shell and has
its usual side-effects, but the *time* itself does nothing.

Contrast to bash and ksh, in which time works on builtins the same as on
external commands.

-- 
Mark J. Reed <markjreed@gmail.com>

[-- Attachment #2: Type: text/html, Size: 925 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-08-15 13:15 Bug: time doesn't work on builtins Mark J. Reed
@ 2024-08-15 18:52 ` Bart Schaefer
  2024-08-15 22:08   ` Mark J. Reed
  0 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-08-15 18:52 UTC (permalink / raw)
  To: Mark J. Reed; +Cc: Zsh hackers list

On Thu, Aug 15, 2024 at 6:15 AM Mark J. Reed <markjreed@gmail.com> wrote:
>
> Any attempt to time a builtin generates no output whatsoever. No timings, no error message.The timed command executes in the current shell and has its usual side-effects, but the time itself does nothing.

This was last discussed in users/28740 and subsequent thread ...

>> The OS facility that reports time statistics only works for a parent
>> process to read the stats of a child.  To work on builtins, we'd have
>> to rewrite that internally

That isn't quite accurate ... what we'd have to do is call RUSAGE_SELF
both before and after the builtin and subtract the former from the
latter, there's no (other) facility to get only the internal time
delta.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-08-15 18:52 ` Bart Schaefer
@ 2024-08-15 22:08   ` Mark J. Reed
  2024-08-16 19:20     ` Bart Schaefer
  0 siblings, 1 reply; 15+ messages in thread
From: Mark J. Reed @ 2024-08-15 22:08 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 1096 bytes --]

Absent a rewrite of the functionality to actually work on builtins, maybe
it could be modified to output a message about *time* only being supported
for external commands?

On Thu, Aug 15, 2024 at 2:52 PM Bart Schaefer <schaefer@brasslantern.com>
wrote:

> On Thu, Aug 15, 2024 at 6:15 AM Mark J. Reed <markjreed@gmail.com> wrote:
> >
> > Any attempt to time a builtin generates no output whatsoever. No
> timings, no error message.The timed command executes in the current shell
> and has its usual side-effects, but the time itself does nothing.
>
> This was last discussed in users/28740 and subsequent thread ...
>
> >> The OS facility that reports time statistics only works for a parent
> >> process to read the stats of a child.  To work on builtins, we'd have
> >> to rewrite that internally
>
> That isn't quite accurate ... what we'd have to do is call RUSAGE_SELF
> both before and after the builtin and subtract the former from the
> latter, there's no (other) facility to get only the internal time
> delta.
>


-- 
Mark J. Reed <markjreed@gmail.com>

[-- Attachment #2: Type: text/html, Size: 1706 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-08-15 22:08   ` Mark J. Reed
@ 2024-08-16 19:20     ` Bart Schaefer
  2024-09-04  0:27       ` Bart Schaefer
  0 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-08-16 19:20 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 169 bytes --]

On Thu, Aug 15, 2024 at 3:08 PM Mark J. Reed <markjreed@gmail.com> wrote:
>
> Absent a rewrite of the functionality to actually work on builtins

Let's try this.

[-- Attachment #2: time-builtins.txt --]
[-- Type: text/plain, Size: 2956 bytes --]

diff --git a/Src/exec.c b/Src/exec.c
index 00278ac50..3c04c5efd 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2938,6 +2938,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
      * in order to check for prefix commands.
      */
     LinkList preargs;
+    /*
+     * for the "time" builtin
+     */
+    child_times_t shti, chti;
+    struct timeval then;
 
     doneps4 = 0;
 
@@ -3621,6 +3626,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 
     /* This is nonzero if the command is a current shell procedure? */
     is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH);
+    if (is_cursh && (how & Z_TIMED))
+	shelltime(&shti, &chti, &then, 0);
 
     /**************************************************************************
      * Do we need to fork?  We need to fork if:                               *
@@ -4377,6 +4384,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    errflag |= ERRFLAG_ERROR;
 	}
     }
+    if (is_cursh && (how & Z_TIMED))
+	shelltime(&shti, is_builtin ? NULL : &chti, &then, 1);
     if (newxtrerr) {
 	int eno = errno;
 	fil = fileno(newxtrerr);
@@ -5265,7 +5274,7 @@ exectime(Estate state, UNUSED(int do_exec))
 
     jb = thisjob;
     if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) {
-	shelltime();
+	shelltime(NULL,NULL,NULL,0);
 	return 0;
     }
     execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0);
diff --git a/Src/jobs.c b/Src/jobs.c
index 07facc60b..39c664388 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -1894,7 +1894,7 @@ spawnjob(void)
 
 /**/
 void
-shelltime(void)
+shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int delta)
 {
     struct timezone dummy_tz;
     struct timeval dtimeval, now;
@@ -1913,7 +1913,28 @@ shelltime(void)
     ti.ut = buf.tms_utime;
     ti.st = buf.tms_stime;
 #endif
-    printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell");
+    if (shell) {
+	if (delta) {
+#ifdef HAVE_GETRUSAGE
+	    dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
+	    dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
+#else
+	    ti.ut -= shell->ut;
+	    ti.st -= shell->st;
+#endif
+	} else
+	    *shell = ti;
+    }
+    if (delta)
+	dtime(&dtimeval, then, &now);
+    else {
+	if (then)
+	    *then = now;
+	dtime(&dtimeval, &shtimer, &now);
+    }
+
+    if (!delta == !shell)
+	printtime(&dtimeval, &ti, "shell");
 
 #ifdef HAVE_GETRUSAGE
     getrusage(RUSAGE_CHILDREN, &ti);
@@ -1921,8 +1942,20 @@ shelltime(void)
     ti.ut = buf.tms_cutime;
     ti.st = buf.tms_cstime;
 #endif
-    printtime(&dtimeval, &ti, "children");
-
+    if (kids) {
+	if (delta) {
+#ifdef HAVE_GETRUSAGE
+	    dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
+	    dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
+#else
+	    ti.ut -= shell->ut;
+	    ti.st -= shell->st;
+#endif
+	} else
+	    *kids = ti;
+    }
+    if (!delta == !kids)
+	printtime(&dtimeval, &ti, "children");
 }
 
 /* see if jobs need printing */

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-08-16 19:20     ` Bart Schaefer
@ 2024-09-04  0:27       ` Bart Schaefer
  2024-09-04 14:32         ` Jun. T
  0 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-09-04  0:27 UTC (permalink / raw)
  To: Zsh hackers list

On Fri, Aug 16, 2024 at 12:20 PM Bart Schaefer
<schaefer@brasslantern.com> wrote:
>
> Let's try this.

Any comment on the patch?

Where would a test for this go?  The other "time" tests are in
A01grammar but only apply to whether it parses and produces TIMEFMT'ed
output correctly.  Perhaps A05execution?


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-04  0:27       ` Bart Schaefer
@ 2024-09-04 14:32         ` Jun. T
  2024-09-05 17:29           ` Jun. T
  2024-09-06  0:10           ` Bart Schaefer
  0 siblings, 2 replies; 15+ messages in thread
From: Jun. T @ 2024-09-04 14:32 UTC (permalink / raw)
  Cc: zsh-workers


> 2024/09/04 9:27, Bart Schaefer <schaefer@brasslantern.com> wrote:
> 
> On Fri, Aug 16, 2024 at 12:20 PM Bart Schaefer
> <schaefer@brasslantern.com> wrote:
>> 
>> Let's try this.
> 
> Any comment on the patch?

The followings still give no time statistics:

% time x=1
% time x=$(date)


The following works:

% x=0; time for ((i=1; i<=10000; ++i)); do let 'x+=i'; done; echo $x
shell  0.03s user 0.01s system 99% cpu 0.036 total
children  0.00s user 0.00s system 0% cpu 0.036 total
50005000

But if "let 'x+=i' is replaced by "((x+=i))"

% x=0; time for ((i=1; i<=10000; ++i)); do ((x+=i)); done; echo $x
0

The for loop is not executed, and the time statistics is not output.

This happens also without the patch time-builtins.txt.



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-04 14:32         ` Jun. T
@ 2024-09-05 17:29           ` Jun. T
  2024-09-05 23:37             ` Bart Schaefer
  2024-09-06  0:10           ` Bart Schaefer
  1 sibling, 1 reply; 15+ messages in thread
From: Jun. T @ 2024-09-05 17:29 UTC (permalink / raw)
  To: zsh-workers


> 2024/09/04 23:32, I wrote:
> 
> % x=0; time for ((i=1; i<=10000; ++i)); do ((x+=i)); done; echo $x
> 0
> 
> The for loop is not executed, and the time statistics is not output.

The reason that the for loop is skipped may be the following:

When exectime() calls execpline(,slcode=*state->pc++,) (exec.c:5280),
slcode is WC_SUBLIST with WC_SUBLIST_SIMPLE flag, and 'code' (the next
word code) obtained by (exec.c:1677)
    wordcode code = *state->pc++;
is just a line number, not WC_PIPE (see parse.c:757). Then the for
loop is skipped by (lines 1680,81)
    if (wc_code(code) != WC_PIPE)
        return lastval = (slflags & WC_SUBLIST_NOT) != 0;



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-05 17:29           ` Jun. T
@ 2024-09-05 23:37             ` Bart Schaefer
  2024-09-06  9:41               ` Jun T
  0 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-09-05 23:37 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Thu, Sep 5, 2024 at 10:29 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> When exectime() calls execpline(,slcode=*state->pc++,) (exec.c:5280),
> slcode is WC_SUBLIST with WC_SUBLIST_SIMPLE flag, and 'code' (the next
> word code) obtained by (exec.c:1677)
>     wordcode code = *state->pc++;
> is just a line number, not WC_PIPE (see parse.c:757). Then the for
> loop is skipped by (lines 1680,81)
>     if (wc_code(code) != WC_PIPE)
>         return lastval = (slflags & WC_SUBLIST_NOT) != 0;

Is this enough?  All existing tests still pass, but I wonder if
there's another way to get there.

diff --git a/Src/exec.c b/Src/exec.c
index 00278ac50..057999844 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1677,7 +1677,7 @@ execpline(Estate state, wordcode slcode, int how, int last
1)
     wordcode code = *state->pc++;
     static int lastwj, lpforked;

-    if (wc_code(code) != WC_PIPE)
+    if (wc_code(code) != WC_PIPE && !(how & Z_TIMED))
        return lastval = (slflags & WC_SUBLIST_NOT) != 0;
     else if (slflags & WC_SUBLIST_NOT)
        last1 = 0;


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-04 14:32         ` Jun. T
  2024-09-05 17:29           ` Jun. T
@ 2024-09-06  0:10           ` Bart Schaefer
  2024-09-06  9:52             ` Jun T
  1 sibling, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-09-06  0:10 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Wed, Sep 4, 2024 at 7:33 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> The followings still give no time statistics:
>
> % time x=1
> % time x=$(date)

Hm, that's because we're taking the code path marked /* Empty command
*/ in execcmd_exec().

I think the only relevant case is around addvars() but can't be sure;
does anyone know a straightforward way to conditionally suppress the
child times in the case where the right-hand-side does not fork?

(Hand-fuzzed patch follows)

diff --git a/Src/exec.c b/Src/exec.c
index 00278ac50..c2f813078 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -3381,7 +3386,11 @@ execcmd_exec(Estate state, Execcmd_params eparams,
                    if (varspc) {
                        /* Make sure $? is still correct for assignment */
                        lastval = old_lastval;
+                       if (how & Z_TIMED)
+                           shelltime(&shti, &chti, &then, 0);
                        addvars(state, varspc, 0);
+                       if (how & Z_TIMED)
+                           shelltime(&shti, &chti, &then, 1);
                    }
                    if (errflag)
                        lastval = 1;


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-05 23:37             ` Bart Schaefer
@ 2024-09-06  9:41               ` Jun T
  0 siblings, 0 replies; 15+ messages in thread
From: Jun T @ 2024-09-06  9:41 UTC (permalink / raw)
  To: zsh-workers


> 2024/09/06 8:37, Bart Schaefer <schaefer@brasslantern.com> wrote:
> 
> On Thu, Sep 5, 2024 at 10:29 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>> 
>> loop is skipped by (lines 1680,81)
>>    if (wc_code(code) != WC_PIPE)
>>        return lastval = (slflags & WC_SUBLIST_NOT) != 0;
> 
> Is this enough?  All existing tests still pass, but I wonder if
> there's another way to get there.

Other calls of execpline() are all from execlist(), and it seems
the case with WC_SUBST_SIMPLE is already taken care of (for
example at around line 1492).



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-06  0:10           ` Bart Schaefer
@ 2024-09-06  9:52             ` Jun T
  2024-09-06 19:13               ` Bart Schaefer
  0 siblings, 1 reply; 15+ messages in thread
From: Jun T @ 2024-09-06  9:52 UTC (permalink / raw)
  To: zsh-workers


> 2024/09/06 9:10, Bart Schaefer <schaefer@brasslantern.com> wrote:
> 
> On Wed, Sep 4, 2024 at 7:33 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>> 
>> The followings still give no time statistics:
>> 
>> % time x=1
>> % time x=$(date)
> 
> (Hand-fuzzed patch follows)

The patch works for the above two cases, but I've fond there
are still more problems...

[1] In the patch file "time-builtin.txt":

@@ -4377,6 +4384,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    errflag |= ERRFLAG_ERROR;
 	}
     }
+    if (is_cursh && (how & Z_TIMED))
+	shelltime(&shti, is_builtin ? NULL : &chti, &then, 1);

Due to "is_builtin ? NULL : &chti", child time is not reported for
the cases such as

% time eval 'external command'
% time . ./script	# script runs external command

I think it's better to pass &chti always.


[2] Time spent in prefork() is not included in the statistics.

% time echo $(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x)  5000050000
shell  0.00s user 0.00s system 100% cpu 0.000 total

% time cat <(time-consuming-command)	# cat is not builtin
(the problem existed before applying "time-builtin.txt")

% time echo $(external command)
Even if the problem [1] is fixed (by always passing &chti)
the time reported would not include the time used for
the 'external command'.

Not sure this can be fixed easily.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: Bug: time doesn't work on builtins
  2024-09-06  9:52             ` Jun T
@ 2024-09-06 19:13               ` Bart Schaefer
  2024-09-14  2:10                 ` PATCH: " Bart Schaefer
  0 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-09-06 19:13 UTC (permalink / raw)
  To: Jun T; +Cc: zsh-workers

On Fri, Sep 6, 2024 at 2:52 AM Jun T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> % time eval 'external command'
> % time . ./script       # script runs external command
>
> I think it's better to pass &chti always.

OK.

> [2] Time spent in prefork() is not included in the statistics.

I believe that's fixable by always calling shelltime(...,0) when
Z_TIMED but then only calling shelltime(...,1) when is_cursh is
determined later.

Remaining question is whether stats should be printed on error cases,
e.g., "no such builtin":

time builtin nosuch $(long running command)

That means sticking in a few more calls to shelltime() in front of
early return statements.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* PATCH: Re: Bug: time doesn't work on builtins
  2024-09-06 19:13               ` Bart Schaefer
@ 2024-09-14  2:10                 ` Bart Schaefer
  2024-09-20  7:46                   ` Oliver Kiddle
  0 siblings, 1 reply; 15+ messages in thread
From: Bart Schaefer @ 2024-09-14  2:10 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 550 bytes --]

On Fri, Sep 6, 2024 at 12:13 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> Remaining question is whether stats should be printed on error cases,
> e.g., "no such builtin":

I've gone ahead with this, it's a bit ugly because of all the early
"return;" but it works.

Tests included, though they only check whether `time' returns
something, not whether that something makes sense in terms of cpu
usage etc.  If anyone has any ideas how to make that predictable
enough to pattern-match the output, feel free to update the tests.

[-- Attachment #2: time-builtins2.txt --]
[-- Type: text/plain, Size: 8351 bytes --]

diff --git a/Src/exec.c b/Src/exec.c
index 00278ac50..8aa7466f5 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -881,6 +881,10 @@ execute(LinkList args, int flags, int defpath)
 	_realexit();
     else
 	zerr("command not found: %s", arg0);
+    /* This is bash behavior, but fails to restore interactive settings etc.
+    lastval = ((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
+    return;
+    */
     _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
 }
 
@@ -1677,7 +1681,7 @@ execpline(Estate state, wordcode slcode, int how, int last1)
     wordcode code = *state->pc++;
     static int lastwj, lpforked;
 
-    if (wc_code(code) != WC_PIPE)
+    if (wc_code(code) != WC_PIPE && !(how & Z_TIMED))
 	return lastval = (slflags & WC_SUBLIST_NOT) != 0;
     else if (slflags & WC_SUBLIST_NOT)
 	last1 = 0;
@@ -2939,6 +2943,14 @@ execcmd_exec(Estate state, Execcmd_params eparams,
      */
     LinkList preargs;
 
+    /*
+     * for the "time" keyword
+     */
+    child_times_t shti, chti;
+    struct timeval then;
+    if (how & Z_TIMED)
+	shelltime(&shti, &chti, &then, 0);
+
     doneps4 = 0;
 
     /*
@@ -3071,6 +3083,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		if (!(hn = resolvebuiltin(cmdarg, hn))) {
 		    if (forked)
 			_realexit();
+		    if (how & Z_TIMED)
+			shelltime(&shti, &chti, &then, 1);
 		    return;
 		}
 		if (type != WC_TYPESET)
@@ -3252,6 +3266,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 			    errflag |= ERRFLAG_ERROR;
 			    if (forked)
 				_realexit();
+			    if (how & Z_TIMED)
+				shelltime(&shti, &chti, &then, 1);
 			    return;
 			}
 		    }
@@ -3343,6 +3359,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 			errflag |= ERRFLAG_ERROR;
 			if (forked)
 			    _realexit();
+			if (how & Z_TIMED)
+			    shelltime(&shti, &chti, &then, 1);
 			return;
 		    } else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) {
 			if (!args)
@@ -3363,6 +3381,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		    lastval = 0;
 		    if (forked)
 			_realexit();
+		    if (how & Z_TIMED)
+			shelltime(&shti, &chti, &then, 1);
 		    return;
 		} else {
 		    /*
@@ -3375,6 +3395,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 			lastval = 1;
 			if (forked)
 			    _realexit();
+			if (how & Z_TIMED)
+			    shelltime(&shti, &chti, &then, 1);
 			return;
 		    }
 		    cmdoutval = use_cmdoutval ? lastval : 0;
@@ -3393,6 +3415,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		    }
 		    if (forked)
 			_realexit();
+		    if (how & Z_TIMED)
+			shelltime(&shti, &chti, &then, 1);
 		    return;
 		}
 	    } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) {
@@ -3401,6 +3425,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		lastval = 1;
 		if (forked)
 		    _realexit();
+		if (how & Z_TIMED)
+		    shelltime(&shti, &chti, &then, 1);
 		return;
 	    }
 
@@ -3437,6 +3463,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 			opts[AUTOCONTINUE] = oautocont;
 		    if (forked)
 			_realexit();
+		    if (how & Z_TIMED)
+			shelltime(&shti, &chti, &then, 1);
 		    return;
 		}
 		break;
@@ -3448,6 +3476,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		if (!(hn = resolvebuiltin(cmdarg, hn))) {
 		    if (forked)
 			_realexit();
+		    if (how & Z_TIMED)
+			shelltime(&shti, &chti, &then, 1);
 		    return;
 		}
 		break;
@@ -3466,6 +3496,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    opts[AUTOCONTINUE] = oautocont;
 	if (forked)
 	    _realexit();
+	if (how & Z_TIMED)
+	    shelltime(&shti, &chti, &then, 1);
 	return;
     }
 
@@ -3545,6 +3577,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		    opts[AUTOCONTINUE] = oautocont;
 		if (forked)
 		    _realexit();
+		if (how & Z_TIMED)
+		    shelltime(&shti, &chti, &then, 1);
 		return;
 	    }
 	}
@@ -3575,6 +3609,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    opts[AUTOCONTINUE] = oautocont;
 	if (forked)
 	    _realexit();
+	if (how & Z_TIMED)
+	    shelltime(&shti, &chti, &then, 1);
 	return;
     }
 
@@ -4377,6 +4413,8 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    errflag |= ERRFLAG_ERROR;
 	}
     }
+    if ((is_cursh || do_exec) && (how & Z_TIMED))
+	shelltime(&shti, &chti, &then, 1);
     if (newxtrerr) {
 	int eno = errno;
 	fil = fileno(newxtrerr);
@@ -5265,7 +5303,7 @@ exectime(Estate state, UNUSED(int do_exec))
 
     jb = thisjob;
     if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) {
-	shelltime();
+	shelltime(NULL,NULL,NULL,0);
 	return 0;
     }
     execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0);
diff --git a/Src/jobs.c b/Src/jobs.c
index 07facc60b..39c664388 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -1894,7 +1894,7 @@ spawnjob(void)
 
 /**/
 void
-shelltime(void)
+shelltime(child_times_t *shell, child_times_t *kids, struct timeval *then, int delta)
 {
     struct timezone dummy_tz;
     struct timeval dtimeval, now;
@@ -1913,7 +1913,28 @@ shelltime(void)
     ti.ut = buf.tms_utime;
     ti.st = buf.tms_stime;
 #endif
-    printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell");
+    if (shell) {
+	if (delta) {
+#ifdef HAVE_GETRUSAGE
+	    dtime(&ti.ru_utime, &shell->ru_utime, &ti.ru_utime);
+	    dtime(&ti.ru_stime, &shell->ru_stime, &ti.ru_stime);
+#else
+	    ti.ut -= shell->ut;
+	    ti.st -= shell->st;
+#endif
+	} else
+	    *shell = ti;
+    }
+    if (delta)
+	dtime(&dtimeval, then, &now);
+    else {
+	if (then)
+	    *then = now;
+	dtime(&dtimeval, &shtimer, &now);
+    }
+
+    if (!delta == !shell)
+	printtime(&dtimeval, &ti, "shell");
 
 #ifdef HAVE_GETRUSAGE
     getrusage(RUSAGE_CHILDREN, &ti);
@@ -1921,8 +1942,20 @@ shelltime(void)
     ti.ut = buf.tms_cutime;
     ti.st = buf.tms_cstime;
 #endif
-    printtime(&dtimeval, &ti, "children");
-
+    if (kids) {
+	if (delta) {
+#ifdef HAVE_GETRUSAGE
+	    dtime(&ti.ru_utime, &kids->ru_utime, &ti.ru_utime);
+	    dtime(&ti.ru_stime, &kids->ru_stime, &ti.ru_stime);
+#else
+	    ti.ut -= shell->ut;
+	    ti.st -= shell->st;
+#endif
+	} else
+	    *kids = ti;
+    }
+    if (!delta == !kids)
+	printtime(&dtimeval, &ti, "children");
 }
 
 /* see if jobs need printing */
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
index d57085798..660602caf 100644
--- a/Test/A01grammar.ztst
+++ b/Test/A01grammar.ztst
@@ -399,13 +399,6 @@
 >This is name2
 >This is still name2
 
-  (time cat) >&/dev/null
-0:`time' keyword (status only)
-
-  TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:)
-0:`time' keyword with custom TIMEFMT
-*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu
-
   if [[ -f foo && -d . && -n $ZTST_testdir ]]; then
     true
   else
diff --git a/Test/A08time.ztst b/Test/A08time.ztst
new file mode 100644
index 000000000..9fb1f3ebf
--- /dev/null
+++ b/Test/A08time.ztst
@@ -0,0 +1,64 @@
+#
+# This file contains tests for the "time" reserved word
+#
+
+%prep
+
+  unset TIMEFMT
+
+%test
+
+  (time cat) >&/dev/null
+0:`time' keyword (status only)
+
+  ( TIMEFMT='%E %mE %uE %* %m%mm %u%uu'; time (:) )
+0:`time' keyword with custom TIMEFMT
+*?[0-9]##.[0-9](#c2)s [0-9]##ms [0-9]##us %\* %m%mm %u%uu
+
+  time x=1
+0:`time' simple assignment
+*?shell*
+*?children*
+
+  time x=$(date)
+0:`time' assignment with external command
+*?shell*
+*?children*
+
+  x=0; time for ((i=1; i<=10000; ++i)); do ((x+=i)); done; echo $x
+0:`time' for-loop with arithmetic condition
+>50005000
+*?shell*
+*?children*
+
+  time echo $(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x)
+0:`time' of a builtin with argument command substitution
+>5000050000
+*?shell*
+*?children*
+
+  time cat <(x=0;for ((i=0; i<=100000; ++i)); do ((x+=i)); done; echo $x)
+0:`time' of external command with process substitution
+>5000050000
+*?*user*system*cpu*total
+
+  print -u $ZTST_fd 'This test takes 2 seconds'
+  time builtin nonesuch $(sleep 2)
+1:`time' of nonexistent builtin with command substitution
+*?*: no such builtin: nonesuch
+*?shell*
+*?children*
+
+  time /no/such/commmand
+127:`time' of nonexistent external
+*?*no such file or directory: /no/such/commmand
+*?*user*system*cpu*total
+
+  ( setopt errexit; time false; print notreached )
+1:`time' of failed builtin with errexit
+*?shell*
+*?children*
+
+  ( setopt errexit; time =false; print notreached )
+1:`time' of failed external with errexit
+*?*user*system*cpu*total

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PATCH: Re: Bug: time doesn't work on builtins
  2024-09-14  2:10                 ` PATCH: " Bart Schaefer
@ 2024-09-20  7:46                   ` Oliver Kiddle
  2024-09-22  0:59                     ` Bart Schaefer
  0 siblings, 1 reply; 15+ messages in thread
From: Oliver Kiddle @ 2024-09-20  7:46 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On 13 Sep, Bart Schaefer wrote:
> +  ( setopt errexit; time =false; print notreached )
> +1:`time' of failed external with errexit
> +*?*user*system*cpu*total

This test fails on Solaris because the external false returns -1 instead
of 1.

Two alternatives that come to mind would be test which returns false if
given no arguments and expr 0 which has the advantage of not existing as
a builtin so no need for the =. Both of those return 1 on Solaris for
failure.

Oliver


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: PATCH: Re: Bug: time doesn't work on builtins
  2024-09-20  7:46                   ` Oliver Kiddle
@ 2024-09-22  0:59                     ` Bart Schaefer
  0 siblings, 0 replies; 15+ messages in thread
From: Bart Schaefer @ 2024-09-22  0:59 UTC (permalink / raw)
  To: Zsh hackers list

On Fri, Sep 20, 2024 at 12:46 AM Oliver Kiddle <opk@zsh.org> wrote:
>
> ... expr 0 which has the advantage of not existing as a builtin

I'll go with that.

diff --git a/Test/A08time.ztst b/Test/A08time.ztst
index 9fb1f3ebf..22a460f5e 100644
--- a/Test/A08time.ztst
+++ b/Test/A08time.ztst
@@ -59,6 +59,7 @@
 *?shell*
 *?children*

-  ( setopt errexit; time =false; print notreached )
+  ( setopt errexit; time expr 0; print notreached )
 1:`time' of failed external with errexit
+>0
 *?*user*system*cpu*total


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2024-09-22  1:00 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-08-15 13:15 Bug: time doesn't work on builtins Mark J. Reed
2024-08-15 18:52 ` Bart Schaefer
2024-08-15 22:08   ` Mark J. Reed
2024-08-16 19:20     ` Bart Schaefer
2024-09-04  0:27       ` Bart Schaefer
2024-09-04 14:32         ` Jun. T
2024-09-05 17:29           ` Jun. T
2024-09-05 23:37             ` Bart Schaefer
2024-09-06  9:41               ` Jun T
2024-09-06  0:10           ` Bart Schaefer
2024-09-06  9:52             ` Jun T
2024-09-06 19:13               ` Bart Schaefer
2024-09-14  2:10                 ` PATCH: " Bart Schaefer
2024-09-20  7:46                   ` Oliver Kiddle
2024-09-22  0:59                     ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).