commit: e8c67c47e2f5696768f3c7d044225aeb0febf070
parent 30848fea2e3e9c9020b89ef70920f251afb153ff
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Thu, 1 Dec 2022 02:01:27 +0100
cmd/time: New utility
Diffstat:
11 files changed, 219 insertions(+), 4 deletions(-)
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
@@ -6,6 +6,7 @@ packages:
- pkgconf
- kyua
- gcc
+ - devel/cram
sources:
- https://hacktivis.me/git/utils.git
tasks:
diff --git a/.builds/netbsd.yml b/.builds/netbsd.yml
@@ -6,17 +6,18 @@ packages:
- pkg-config
- kyua
- clang
+# FIXME: cram/prysk on NetBSD, where?
sources:
- https://hacktivis.me/git/utils.git
tasks:
- clang: |
cd utils
CC=clang ./configure
- DESTDIR=/tmp/clang-destdir make clean test
+ CRAM=true DESTDIR=/tmp/clang-destdir make clean test
- gcc: |
cd utils
CC=gcc ./configure
- DESTDIR=/tmp/gcc-destdir make clean test
+ CRAM=true DESTDIR=/tmp/gcc-destdir make clean test
triggers:
- action: email
condition: failure
diff --git a/.gitignore b/.gitignore
@@ -7,6 +7,7 @@
!/cmd/*.c
!/cmd/*.ha
!/cmd/*.1
+*.t.err
# Kyua
/html/
diff --git a/Makefile b/Makefile
@@ -16,6 +16,7 @@ all: $(EXE)
.PHONY: test
test: all
MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 kyua test || (kyua report --verbose --results-filter=broken,failed; false)
+ MALLOC_CHECK_=3 POSIX_ME_HARDER=1 POSIXLY_CORRECT=1 $(CRAM) test-cmd/*.t
.PHONY: lint
lint:
diff --git a/README.md b/README.md
@@ -16,6 +16,7 @@ Tested on Linux(musl), FreeBSD, NetBSD, OpenBSD:
- (optional, test) ATF: <https://github.com/jmmv/atf>
- (optional, test) Kyua: <https://github.com/jmmv/kyua>
- (optional, test) bwrap: <https://github.com/containers/bubblewrap/> For safely overlaying false files on the root filesystem
+- (optional, test) [cram](https://bitheap.org/cram) or [prysk](https://www.prysk.net/)
- (optional, lint) mandoc: <https://mdocml.bsd.lv/> For linting the manual pages
- (optional, lint) shellcheck: <https://www.shellcheck.net/> For linting `./configure` and shell scripts
diff --git a/cmd/time.1 b/cmd/time.1
@@ -0,0 +1,48 @@
+.\" Collection of Unix tools, comparable to coreutils
+.\" Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+.\" SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
+.Dd 2022-12-01
+.Dt TIME 1
+.Os
+.Sh NAME
+.Nm time
+.Nd measure time used by a command
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Ar command
+.Op Ar argument...
+.Sh DESCRIPTION
+.Nm
+measures the wall-clock time, user CPU time, system/kernel CPU time, used by
+.Ar command
+and outputs it to stderr.
+.Sh OPTIONS
+.Fl p
+is ignored for compatibility reasons, the output is always in the POSIX format.
+.Sh EXIT STATUS
+If
+.Ar command
+is invoked, the exit status should be the one given by
+.Ar command .
+Otherwise, it will exit with the following values:
+.Bl -tag -width 1-125
+.It 1-125
+An error occured in
+.Ar command
+.It 126
+.Ar command
+was found but couldn't be invoked
+.It 127
+.Ar command
+could not be found
+.El
+.Sh SEE ALSO
+.Xr times 3
+.Sh STANDARDS
+.Nm
+is compliant with the
+.St -p1003.1-2008
+specification.
+.Sh AUTHORS
+.An Haelwenn (lanodan) Monnier Aq Mt contact@hacktivis.me
diff --git a/cmd/time.c b/cmd/time.c
@@ -0,0 +1,112 @@
+// Collection of Unix tools, comparable to coreutils
+// SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
+
+#define _POSIX_C_SOURCE 200809L
+#include <errno.h> // errno
+#include <stdio.h> // perror, fprintf
+#include <stdlib.h> // abort
+#include <sys/times.h> // times
+#include <sys/wait.h> // waitpid
+#include <unistd.h> // sysconf, fork, execvp, getopt
+
+void
+usage()
+{
+ fprintf(stderr, "Usage: time command [argument ...]\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct tms tms;
+ int ret = 0;
+
+ if(argc <= 1)
+ {
+ usage();
+ return 0;
+ }
+
+ int c = -1;
+ while((c = getopt(argc, argv, ":p")) != -1)
+ {
+ switch(c)
+ {
+ case 'p': // POSIX format (default)
+ break;
+ case ':':
+ fprintf(stderr, "time: Error: Missing operand for option: '-%c'\n", optopt);
+ usage();
+ return 1;
+ case '?':
+ fprintf(stderr, "time: Error: Unrecognised option: '-%c'\n", optopt);
+ usage();
+ return 1;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ (void)argc;
+
+ long ticks = sysconf(_SC_CLK_TCK);
+ if(ticks <= 0)
+ {
+ perror("time: sysconf(_SC_CLK_TCK)");
+ return 1;
+ }
+
+ clock_t t0 = times(&tms);
+ if(t0 == (clock_t)-1)
+ {
+ perror("time: times");
+ return 1;
+ }
+
+ // Note: posix_spawnp seems to lack enough error information
+ pid_t pid = fork();
+ switch(pid)
+ {
+ case -1:
+ perror("time: fork");
+ return 1;
+ case 0:
+ /* flawfinder: ignore. No restrictions on commands is intended */
+ execvp(argv[0], argv);
+ ret = 126 + (errno == ENOENT);
+ perror("time: execvp");
+ return ret;
+ default:
+ break;
+ }
+
+ int status = 0;
+ waitpid(pid, &status, 0);
+
+ int t1 = times(&tms);
+ if(t1 == (clock_t)-1)
+ {
+ perror("time: times");
+ return 1;
+ }
+
+ if(WIFSIGNALED(status))
+ {
+ fprintf(stderr, "time: Command terminated by signal %d\n", WTERMSIG(status));
+ ret = 128 + WTERMSIG(status);
+ }
+
+ fprintf(stderr,
+ "real %f\nuser %f\nsys %f\n",
+ (t1 - t0) / (double)ticks,
+ tms.tms_cutime / (double)ticks,
+ tms.tms_cstime / (double)ticks);
+
+ if(WIFEXITED(status))
+ {
+ ret = WEXITSTATUS(status);
+ }
+
+ return ret;
+}
diff --git a/configure b/configure
@@ -23,6 +23,7 @@ Variables:
SHELLCHECK=BIN
FLAWFINDER=BIN
GCOV=BIN
+ CRAM=BIN
CFLAGS=OPTIONS
LDFLAGS=OPTIONS
@@ -92,6 +93,7 @@ CFLAGS="${CFLAGS:--g -O2}"
MANDOC="${MANDOC:-mandoc}"
SHELLCHECK="${SHELLCHECK:-shellcheck}"
FLAWFINDER="${FLAWFINDER:-flawfinder}"
+CRAM="${CRAM:-cram}"
# Also allow variables through arguments
for i; do
@@ -168,6 +170,22 @@ else
FLAWFINDER="true"
fi
+if check_cmd CRAM "$CRAM"
+then
+ :
+else
+ echo "Notice: cram not found, trying prysk"
+
+ CRAM="prysk"
+ if check_cmd CRAM "$CRAM"
+ then
+ :
+ else
+ CRAM="false"
+ echo 'Notice: Testsuite depending on cram/psyk disabled'
+ fi
+fi
+
echo
# pkg-config
@@ -217,6 +235,7 @@ FLAWFINDER = ${FLAWFINDER}
MSGFMT = ${MSGFMT}
DBG = ${DBG}
GCOV = ${GCOV}
+CRAM = ${CRAM}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS}
diff --git a/lsb_commands.txt b/lsb_commands.txt
@@ -122,7 +122,7 @@ tar: out of scope
tee: Todo
test: ?
tic: out of scope
-time: ?
+time: Done
touch: Todo (futimens(3p))
tput: out of scope
tr: Todo
diff --git a/test-cmd/Kyuafile b/test-cmd/Kyuafile
@@ -6,7 +6,7 @@ test_suite("utils")
basedir = fs.dirname(fs.dirname(current_kyuafile()))
--- 7,$|LC_ALL=C.UTF-8 sort
+-- 9,$|LC_ALL=C.UTF-8 sort
atf_test_program{name="args", required_files=basedir.."/cmd/args", timeout=1}
atf_test_program{name="basename", required_files=basedir.."/cmd/basename", timeout=1}
atf_test_program{name="cat", required_files=basedir.."/cmd/cat", timeout=1}
diff --git a/test-cmd/time.t b/test-cmd/time.t
@@ -0,0 +1,31 @@
+#!/usr/bin/env cram
+# SPDX-FileCopyrightText: 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+# SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
+
+ $ cd $TESTDIR/../cmd
+
+ $ ./time
+ Usage: time command [argument ...]
+
+ $ ./time -f
+ time: Error: Unrecognised option: '-f'
+ Usage: time command [argument ...]
+ [1]
+
+ $ ./time /var/empty/e/no/ent
+ time: execvp: No such file or directory
+ real 0.[0-9]* (re)
+ user 0.[0-9]* (re)
+ sys 0.[0-9]* (re)
+ [127]
+
+ $ ./time false
+ real 0.[0-9]* (re)
+ user 0.[0-9]* (re)
+ sys 0.[0-9]* (re)
+ [1]
+
+ $ ./time true
+ real 0.[0-9]* (re)
+ user 0.[0-9]* (re)
+ sys 0.[0-9]* (re)