Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 3 Feb 2010 11:12:03 +0000 (UTC)
From:      Robert Watson <rwatson@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r203424 - projects/capabilities8/tools/tools/syscall_timing
Message-ID:  <201002031112.o13BC3k8075732@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rwatson
Date: Wed Feb  3 11:12:03 2010
New Revision: 203424
URL: http://svn.freebsd.org/changeset/base/203424

Log:
  Merge c174200, c174201, c174202, c174220, c174203, c174204, and c174219
  from the p4 TrustedBSD Capabilities branch to capabilities8:
  
    Rework syscall timing microbenchmark to make it a bit more flexible,
    add some capability-related tests.
  
    Allow multiple loops of a test to be run, not just iterations
    within a test.
  
    Add microbenchmark to compare cost of creating a shared memory
    object w/o a capability, and cost w/ a capability.
  
    cap_enter, fork, pdfork benchmarks.
  
    First cut at a sandbox create/rpc/destroy benchmark, which appears
    not to work.
  
    Microbenchmark for vfork.
  
    Can't create sandboxes from a statically linked binary, so don't
    try.
  
    Remove debugging printf, we can now micro-benchmark sandbox creation.
  
    Benchmark various fork variations with exec of /usr/bin/true.
  
  Sponsored by:	Google, Inc.

Modified:
  projects/capabilities8/tools/tools/syscall_timing/Makefile
  projects/capabilities8/tools/tools/syscall_timing/syscall_timing.c

Modified: projects/capabilities8/tools/tools/syscall_timing/Makefile
==============================================================================
--- projects/capabilities8/tools/tools/syscall_timing/Makefile	Wed Feb  3 10:12:49 2010	(r203423)
+++ projects/capabilities8/tools/tools/syscall_timing/Makefile	Wed Feb  3 11:12:03 2010	(r203424)
@@ -3,7 +3,8 @@
 #
 
 PROG=	syscall_timing
-CFLAGS+=	-static -O
+CFLAGS+=	-O -Wall -rdynamic
 NO_MAN=
+LDADD=	-lcapsicum -lsbuf
 
 .include <bsd.prog.mk>

Modified: projects/capabilities8/tools/tools/syscall_timing/syscall_timing.c
==============================================================================
--- projects/capabilities8/tools/tools/syscall_timing/syscall_timing.c	Wed Feb  3 10:12:49 2010	(r203423)
+++ projects/capabilities8/tools/tools/syscall_timing/syscall_timing.c	Wed Feb  3 11:12:03 2010	(r203424)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2003-2004 Robert N. M. Watson
+ * Copyright (c) 2003-2004, 2010 Robert N. M. Watson
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -27,26 +27,53 @@
  */
 
 #include <sys/types.h>
+#include <sys/capability.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/procdesc.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/wait.h>
 
 #include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libcapsicum.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+static struct timespec ts_start, ts_end;
+
 #define timespecsub(vvp, uvp)                                           \
-        do {                                                            \
-                (vvp)->tv_sec -= (uvp)->tv_sec;                         \
-                (vvp)->tv_nsec -= (uvp)->tv_nsec;                       \
-                if ((vvp)->tv_nsec < 0) {                               \
-                        (vvp)->tv_sec--;                                \
-                        (vvp)->tv_nsec += 1000000000;                   \
-                }                                                       \
-        } while (0)
+	do {                                                            \
+		(vvp)->tv_sec -= (uvp)->tv_sec;                         \
+		(vvp)->tv_nsec -= (uvp)->tv_nsec;                       \
+		if ((vvp)->tv_nsec < 0) {                               \
+		        (vvp)->tv_sec--;                                \
+		        (vvp)->tv_nsec += 1000000000;                   \
+		}                                                       \
+	} while (0)
+
+static void
+benchmark_start(void)
+{
+
+	assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
+}
 
-inline void
+static void
+benchmark_stop(void)
+{
+
+	assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
+}
+
+void
 test_getuid(int num)
 {
 	int i;
@@ -55,11 +82,13 @@ test_getuid(int num)
 	 * Thread-local data should require no locking if system
 	 * call is MPSAFE.
 	 */
+	benchmark_start();
 	for (i = 0; i < num; i++)
 		getuid();
+	benchmark_stop();
 }
 
-inline void
+void
 test_getppid(int num)
 {
 	int i;
@@ -68,28 +97,28 @@ test_getppid(int num)
 	 * This is process-local, but can change, so will require a
 	 * lock.
 	 */
+	benchmark_start();
 	for (i = 0; i < num; i++)
 		getppid();
+	benchmark_stop();
 }
 
-inline void
+void
 test_clock_gettime(int num)
 {
 	struct timespec ts;
 	int i;
 
-	for (i = 0; i < num; i++) {
-		if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
-			perror("clock_gettime");
-			exit(-1);
-		}
-	}
+	benchmark_start();
+	for (i = 0; i < num; i++)
+		(void)clock_gettime(CLOCK_REALTIME, &ts);
+	benchmark_stop();
 }
 
-inline void
+void
 test_pipe(int num)
 {
-	int i;
+	int fd[2], i;
 
 	/*
 	 * pipe creation is expensive, as it will allocate a new file
@@ -97,153 +126,634 @@ test_pipe(int num)
 	 * Destroying is also expensive, as we now have to free up
 	 * the file descriptors and return the pipe.
 	 */
+	if (pipe(fd) < 0)
+		err(-1, "test_pipe: pipe");
+	close(fd[0]);
+	close(fd[1]);
+	benchmark_start();
 	for (i = 0; i < num; i++) {
-		int fd[2];
-		if (pipe(fd) == -1) {
-			perror("pipe");
-			exit(-1);
-		}
-
+		if (pipe(fd) == -1)
+			err(-1, "test_pipe: pipe");
 		close(fd[0]);
 		close(fd[1]);
 	}
+	benchmark_stop();
 }
 
-inline void
+void
 test_socket_stream(int num)
 {
 	int i, so;
 
+	so = socket(PF_LOCAL, SOCK_STREAM, 0);
+	if (so < 0)
+		err(-1, "test_socket_stream: socket");
+	close(so);
+	benchmark_start();
 	for (i = 0; i < num; i++) {
 		so = socket(PF_LOCAL, SOCK_STREAM, 0);
-		if (so == -1) {
-			perror("socket_stream");
-			exit(-1);
-		}
+		if (so == -1)
+			err(-1, "test_socket_stream: socket");
 		close(so);
 	}
+	benchmark_stop();
 }
 
-inline void
+void
 test_socket_dgram(int num)
 {
 	int i, so;
 
+	so = socket(PF_LOCAL, SOCK_DGRAM, 0);
+	if (so < 0)
+		err(-1, "test_socket_dgram: socket");
+	close(so);
+	benchmark_start();
 	for (i = 0; i < num; i++) {
 		so = socket(PF_LOCAL, SOCK_DGRAM, 0);
-		if (so == -1) {
-			perror("socket_dgram");
-			exit(-1);
-		}
+		if (so == -1)
+			err(-1, "test_socket_dgram: socket");
 		close(so);
 	}
+	benchmark_stop();
 }
 
-inline void
+void
 test_socketpair_stream(int num)
 {
 	int i, so[2];
 
+	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
+		err(-1, "test_socketpair_stream: socketpair");
+	close(so[0]);
+	close(so[1]);
+	benchmark_start();
 	for (i = 0; i < num; i++) {
-		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1) {
-			perror("socketpair_stream");
-			exit(-1);
-		}
+		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
+			err(-1, "test_socketpair_stream: socketpair");
 		close(so[0]);
 		close(so[1]);
 	}
+	benchmark_stop();
 }
 
-inline void
+void
 test_socketpair_dgram(int num)
 {
 	int i, so[2];
 
+	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
+		err(-1, "test_socketpair_dgram: socketpair");
+	close(so[0]);
+	close(so[1]);
+	benchmark_start();
 	for (i = 0; i < num; i++) {
-		if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1) {
-			perror("socketpair_dgram");
-			exit(-1);
-		}
+		if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
+			err(-1, "test_socketpair_dgram: socketpair");
 		close(so[0]);
 		close(so[1]);
 	}
+	benchmark_stop();
+}
+
+void
+test_dup(int num)
+{
+	int fd, i, shmfd;
+
+	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+	if (shmfd < 0)
+		err(-1, "test_dup: shm_open");
+	fd = dup(shmfd);
+	if (fd >= 0)
+		close(fd);
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		fd = dup(shmfd);
+		if (fd >= 0)
+			close(fd);
+	}
+	benchmark_stop();
+	close(shmfd);
 }
 
+void
+test_cap_new(int num)
+{
+	int fd, i, shmfd;
+
+	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+	if (shmfd < 0)
+		err(-1, "test_cap_new: shm_open");
+	fd = cap_new(shmfd, 0);
+	if (fd >= 0)
+		close(fd);
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		fd = cap_new(shmfd, 0);
+		if (fd >= 0)
+			close(fd);
+	}
+	benchmark_stop();
+	close(shmfd);
+}
+
+void
+test_shmfd(int num)
+{
+	int i, shmfd;
+
+	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+	if (shmfd < 0)
+		err(-1, "test_shmfd: shm_open");
+	close(shmfd);
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+		if (shmfd < 0)
+			err(-1, "test_shmfd: shm_open");
+		close(shmfd);
+	}
+	benchmark_stop();
+}
+
+void
+test_cap_shmfd(int num)
+{
+	int fd, i, shmfd;
+
+	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+	if (shmfd < 0)
+		err(-1, "test_cap_shmfd: shm_open");
+	fd = cap_new(shmfd, 0);
+	if (fd < 0)
+		err(-1, "test_cap_shmfd: cap_new");
+	close(fd);
+	close(shmfd);
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+		if (shmfd < 0)
+			err(-1, "test_cap_shmfd: shm_open");
+		fd = cap_new(shmfd, 0);
+		if (fd < 0)
+			err(-1, "test_cap_shmfd: cap_new");
+		close(fd);
+		close(shmfd);
+	}
+	benchmark_stop();
+}
+
+void
+test_fstat_shmfd(int num)
+{
+	struct stat sb;
+	int i, shmfd;
+
+	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+	if (shmfd < 0)
+		err(-1, "test_fstat_shmfd: shm_open");
+	if (fstat(shmfd, &sb) < 0)
+		err(-1, "test_fstat_shmfd: fstat");
+	benchmark_start();
+	for (i = 0; i < num; i++)
+		(void)fstat(shmfd, &sb);
+	benchmark_stop();
+	close(shmfd);
+}
+
+void
+test_fstat_cap_shmfd(int num)
+{
+	struct stat sb;
+	int fd, i, shmfd;
+
+	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+	if (shmfd < 0)
+		err(-1, "test_fstat_cap_shmfd: shm_open");
+	fd = cap_new(shmfd, CAP_FSTAT);
+	if (fd < 0)
+		err(-1, "test_fstat_cap_shmfd: shm_open");
+	if (fstat(fd, &sb) < 0)
+		err(-1, "test_fstat_cap_shmfd: fstat");
+	benchmark_start();
+	for (i = 0; i < num; i++)
+		(void)fstat(fd, &sb);
+	benchmark_stop();
+	close(fd);
+	close(shmfd);
+}
+
+void
+test_cap_enter(int num)
+{
+	int i;
+
+	/* XXXRW: Note that some tests will fail after this test. */
+
+	if (cap_enter() < 0)
+		err(-1, "test_cap_enter: cap_enter");
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		if (cap_enter() < 0)
+			err(-1, "test_cap_enter: cap_enter");
+	}
+	benchmark_stop();
+}
+
+void
+test_fork(int num)
+{
+	pid_t pid;
+	int i;
+
+	pid = fork();
+	if (pid < 0)
+		err(-1, "test_fork: fork");
+	if (pid == 0)
+		exit(0);
+	if (waitpid(pid, NULL, 0) < 0)
+		err(-1, "test_fork: waitpid");
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		pid = fork();
+		if (pid < 0)
+			err(-1, "test_fork: fork");
+		if (pid == 0)
+			exit(0);
+		if (waitpid(pid, NULL, 0) < 0)
+			err(-1, "test_fork: waitpid");
+	}
+	benchmark_stop();
+}
+
+void
+test_vfork(int num)
+{
+	pid_t pid;
+	int i;
+
+	pid = vfork();
+	if (pid < 0)
+		err(-1, "test_vfork: vfork");
+	if (pid == 0)
+		exit(0);
+	if (waitpid(pid, NULL, 0) < 0)
+		err(-1, "test_vfork: waitpid");
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		pid = vfork();
+		if (pid < 0)
+			err(-1, "test_vfork: vfork");
+		if (pid == 0)
+			exit(0);
+		if (waitpid(pid, NULL, 0) < 0)
+			err(-1, "test_vfork: waitpid");
+	}
+	benchmark_stop();
+}
+
+void
+test_pdfork(int num)
+{
+	struct pollfd pollfd;
+	pid_t pid;
+	int fd, i, n;
+
+	pid = pdfork(&fd);
+	if (pid < 0)
+		err(-1, "test_pdfork: pdfork");
+	if (pid == 0)
+		exit(0);
+	pollfd.fd = fd;
+	pollfd.events = POLLHUP;
+	pollfd.revents = 0;
+	n = poll(&pollfd, 1, INFTIM);
+	if (n < 0)
+		err(-1, "poll");
+	if (n != 1)
+		errx(-1, "poll returned %d", n);
+	close(fd);
+
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		pid = pdfork(&fd);
+		if (pid < 0)
+			err(-1, "test_pdfork: pdfork");
+		if (pid == 0)
+			exit(0);
+		pollfd.fd = fd;
+		pollfd.events = POLLHUP;
+		pollfd.revents = 0;
+		n = poll(&pollfd, 1, INFTIM);
+		if (n < 0)
+			err(-1, "poll");
+		if (n != 1)
+			errx(-1, "poll returned %d", n);
+		close(fd);
+	}
+	benchmark_stop();
+}
+
+#define	USR_BIN_TRUE	"/usr/bin/true"
+static char *execve_args[] = { USR_BIN_TRUE, NULL};
+extern char **environ;
+
+void
+test_fork_exec(int num)
+{
+	pid_t pid;
+	int i;
+
+	pid = fork();
+	if (pid < 0)
+		err(-1, "test_fork: fork");
+	if (pid == 0) {
+		(void)execve(USR_BIN_TRUE, execve_args, environ);
+		err(-1, "execve");
+	}
+	if (waitpid(pid, NULL, 0) < 0)
+		err(-1, "test_fork: waitpid");
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		pid = fork();
+		if (pid < 0)
+			err(-1, "test_fork: fork");
+		if (pid == 0) {
+			(void)execve(USR_BIN_TRUE, execve_args, environ);
+			err(-1, "execve");
+		}
+		if (waitpid(pid, NULL, 0) < 0)
+			err(-1, "test_fork: waitpid");
+	}
+	benchmark_stop();
+}
+
+void
+test_vfork_exec(int num)
+{
+	pid_t pid;
+	int i;
+
+	pid = vfork();
+	if (pid < 0)
+		err(-1, "test_vfork: vfork");
+	if (pid == 0) {
+		(void)execve(USR_BIN_TRUE, execve_args, environ);
+		err(-1, "execve");
+	}
+	if (waitpid(pid, NULL, 0) < 0)
+		err(-1, "test_vfork: waitpid");
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		pid = vfork();
+		if (pid < 0)
+			err(-1, "test_vfork: vfork");
+		if (pid == 0) {
+			(void)execve(USR_BIN_TRUE, execve_args, environ);
+			err(-1, "execve");
+		}
+		if (waitpid(pid, NULL, 0) < 0)
+			err(-1, "test_vfork: waitpid");
+	}
+	benchmark_stop();
+}
+
+void
+test_pdfork_exec(int num)
+{
+	struct pollfd pollfd;
+	pid_t pid;
+	int fd, i, n;
+
+	pid = pdfork(&fd);
+	if (pid < 0)
+		err(-1, "test_pdfork: pdfork");
+	if (pid == 0) {
+		(void)execve(USR_BIN_TRUE, execve_args, environ);
+		err(-1, "execve");
+	}
+	pollfd.fd = fd;
+	pollfd.events = POLLHUP;
+	pollfd.revents = 0;
+	n = poll(&pollfd, 1, INFTIM);
+	if (n < 0)
+		err(-1, "poll");
+	if (n != 1)
+		errx(-1, "poll returned %d", n);
+	close(fd);
+
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		pid = pdfork(&fd);
+		if (pid < 0)
+			err(-1, "test_pdfork: pdfork");
+		if (pid == 0) {
+			(void)execve(USR_BIN_TRUE, execve_args, environ);
+			err(-1, "execve");
+		}
+		pollfd.fd = fd;
+		pollfd.events = POLLHUP;
+		pollfd.revents = 0;
+		n = poll(&pollfd, 1, INFTIM);
+		if (n < 0)
+			err(-1, "poll");
+		if (n != 1)
+			errx(-1, "poll returned %d", n);
+		close(fd);
+	}
+	benchmark_stop();
+}
+
+#define	MYNAME	"./syscall_timing"		/* Binary to run in sandbox. */
+
+/*
+ * Unsandboxed host process with full user rights.
+ */
+void
+test_sandbox(int num)
+{
+	struct lc_sandbox *lcsp;
+	char *sandbox_argv[2] = { MYNAME, NULL };
+	struct iovec iov;
+	size_t len;
+	char ch;
+	int i;
+
+	if (lch_start(MYNAME, sandbox_argv, LCH_PERMIT_STDERR |
+	    LCH_PERMIT_STDOUT, NULL, &lcsp) < 0)
+		err(-1, "lch_start %s", MYNAME);
+	ch = 'X';
+	iov.iov_base = &ch;
+	iov.iov_len = sizeof(ch);
+	if (lch_rpc(lcsp, 0, &iov, 1, &iov, 1, &len) < 0)
+		err(-1, "lch_rpc");
+	if (len != sizeof(ch))
+		errx(-1, "lch_rpc returned size %zd not %zd", len, sizeof(ch));
+	if (ch != 'X')
+		errx(-1, "lch_recv: expected %d and got %d", 'X', ch);
+	lch_stop(lcsp);
+
+	benchmark_start();
+	for (i = 0; i < num; i++) {
+		if (lch_start(MYNAME, sandbox_argv, LCH_PERMIT_STDERR |
+		    LCH_PERMIT_STDOUT, NULL, &lcsp) < 0)
+			err(-1, "lch_start %s", MYNAME);
+		ch = 'X';
+		iov.iov_base = &ch;
+		iov.iov_len = sizeof(ch);
+		if (lch_rpc(lcsp, 0, &iov, 1, &iov, 1, &len) < 0)
+			err(-1, "lch_rpc");
+		if (len != sizeof(ch))
+			errx(-1, "lch_rpc returned size %zd not %zd", len,
+			    sizeof(ch));
+		if (ch != 'X')
+			errx(-1, "lch_recv: expected %d and got %d", 'X', ch);
+		lch_stop(lcsp);
+	}
+	benchmark_stop();
+}
+
+int
+cap_main(int argc, char *argv[])
+{
+	struct lc_host *lchp;
+	u_int32_t opno, seqno;
+	struct iovec iov;
+	u_char *buffer;
+	size_t len;
+
+	if (lcs_get(&lchp) < 0)
+		err(-1, "lcs_get");
+
+	/*
+	 * Serve RPCs from the host until the sandbox is killed.
+	 */
+	while (1) {
+		/*
+		 * Receive a one-byte RPC from the host.
+		 */
+		if (lcs_recvrpc(lchp, &opno, &seqno, &buffer, &len) < 0) {
+			if (errno != EPIPE)
+				err(-6, "lcs_recvrpc");
+			else
+				exit(-6);
+		}
+		if (len != 1)
+			errx(-7, "lcs_recvrpc len");
+
+		/*
+		 * Reply with the same message.  Remember to free the message
+		 * when done.
+		 */
+		iov.iov_base = buffer;
+		iov.iov_len = 1;
+		if (lcs_sendrpc(lchp, opno, seqno, &iov, 1) < 0) {
+			if (errno != EPIPE)
+				err(-8, "lcs_sendrpc");
+			else
+				exit(-8);
+		}
+		free(buffer);
+	}
+}
+
+struct test {
+	const char	*t_name;
+	void		(*t_func)(int);
+};
+
+static const struct test tests[] = {
+	{ "getuid", test_getuid },
+	{ "getppid", test_getppid },
+	{ "clock_gettime", test_clock_gettime },
+	{ "pipe", test_pipe },
+	{ "socket_stream", test_socket_stream },
+	{ "socket_dgram", test_socket_dgram },
+	{ "socketpair_stream", test_socketpair_stream },
+	{ "socketpair_dgram", test_socketpair_dgram },
+	{ "dup", test_dup },
+	{ "cap_new", test_cap_new },
+	{ "test_shmfd", test_shmfd },
+	{ "test_cap_shmfd", test_cap_shmfd },
+	{ "fstat_shmfd", test_fstat_shmfd },
+	{ "fstat_cap_shmfd", test_fstat_cap_shmfd },
+	{ "cap_enter", test_cap_enter },
+	{ "fork", test_fork },
+	{ "vfork", test_vfork },
+	{ "pdfork", test_pdfork },
+	{ "fork_exec", test_fork_exec },
+	{ "vfork_exec", test_vfork_exec },
+	{ "pdfork_exec", test_pdfork_exec },
+	{ "sandbox", test_sandbox },
+};
+static const int tests_count = sizeof(tests) / sizeof(tests[0]);
+
 static void
 usage(void)
 {
+	int i;
 
-	fprintf(stderr, "syscall_timing [iterations] [test]\n");
-	fprintf(stderr,
-	    "supported tests: getuid getppid clock_gettime pipe\n"
-	    "socket_stream socket_dgram socketpair_stream\n"
-	    "socketpair_dgram\n");
+	fprintf(stderr, "syscall_timing [iterations] [loops] [test]\n");
+	for (i = 0; i < tests_count; i++)
+		fprintf(stderr, "  %s\n", tests[i].t_name);
 	exit(-1);
 }
 
 int
 main(int argc, char *argv[])
 {
-	struct timespec ts_start, ts_end, ts_res;
-	int count;
+	struct timespec ts_res;
+	const struct test *the_test;
+	long long ll;
+	char *endp;
+	int i, j, k;
+	int iterations, loops;
 
-	if (argc != 3)
+	if (argc < 3)
 		usage();
-	count = atoi(argv[1]);
-
-	assert(clock_getres(CLOCK_REALTIME, &ts_res) == 0);
-	printf("Clock resolution: %d.%09lu\n", ts_res.tv_sec, ts_res.tv_nsec);
 
-	if (strcmp(argv[2], "getuid") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_getuid(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "getppid") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_getppid(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "clock_gettime") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_clock_gettime(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "pipe") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_pipe(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "socket_stream") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_socket_stream(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "socket_dgram") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_socket_dgram(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "socketpair_stream") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_socketpair_stream(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	} else if (strcmp(argv[2], "socketpair_dgram") == 0) {
-		assert(clock_gettime(CLOCK_REALTIME, &ts_start) == 0);
-		test_socketpair_dgram(count);
-		assert(clock_gettime(CLOCK_REALTIME, &ts_end) == 0);
-	 } else
+	ll = strtoll(argv[1], &endp, 10);
+	if (*endp != 0 || ll < 0 || ll > 100000)
 		usage();
+	iterations = ll;
 
-	timespecsub(&ts_end, &ts_start);
-
-	printf("test: %s\n", argv[2]);
+	ll = strtoll(argv[2], &endp, 10);
+	if (*endp != 0 || ll < 0 || ll > 100000)
+		usage();
+	loops = ll;
 
-	printf("%d.%09lu for %d iterations\n", ts_end.tv_sec,
-	    ts_end.tv_nsec, count);
+	assert(clock_getres(CLOCK_REALTIME, &ts_res) == 0);
+	printf("Clock resolution: %ju.%ju\n", (uintmax_t)ts_res.tv_sec,
+	    (uintmax_t)ts_res.tv_nsec);
+	printf("test\tloop\ttotal\titerations\tperiteration\n");
+
+	for (j = 3; j < argc; j++) {
+		the_test = NULL;
+		for (i = 0; i < tests_count; i++) {
+			if (strcmp(argv[j], tests[i].t_name) == 0)
+				the_test = &tests[i];
+		}
+		if (the_test == NULL)
+			usage();
 
-	/*
-	 * Note.  This assumes that each iteration takes less than
-	 * a second, and that our total nanoseconds doesn't exceed
-	 * the room in our arithmetic unit.  Fine for system calls,
-	 * but not for long things.
-	 */
-	ts_end.tv_sec *= 1000000000 / count;
-	printf("0.%09lu per/iteration\n", 
-	    ts_end.tv_sec + ts_end.tv_nsec / count);
+		for (k = 0; k < loops; k++) {
+			the_test->t_func(iterations);
+			timespecsub(&ts_end, &ts_start);
+			printf("%s\t%d\t", the_test->t_name, k);
+			printf("%ju.%09ju\t%d\t", (uintmax_t)ts_end.tv_sec,
+			    (uintmax_t)ts_end.tv_nsec, iterations);
+
+		/*
+		 * Note.  This assumes that each iteration takes less than
+		 * a second, and that our total nanoseconds doesn't exceed
+		 * the room in our arithmetic unit.  Fine for system calls,
+		 * but not for long things.
+		 */
+			ts_end.tv_sec *= 1000000000 / iterations;
+			printf("0.%09ju\n", (uintmax_t)(ts_end.tv_sec +
+			    ts_end.tv_nsec / iterations));
+		}
+	}
 	return (0);
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201002031112.o13BC3k8075732>