1145519Sdarrenr/*-
2145510Sdarrenr * Copyright (c) 2003-2004, 2010 Robert N. M. Watson
3170268Sdarrenr * All rights reserved.
4255332Scy *
5170268Sdarrenr * Portions of this software were developed at the University of Cambridge
6170268Sdarrenr * Computer Laboratory with support from a grant from Google, Inc.
7170268Sdarrenr *
8145510Sdarrenr * Redistribution and use in source and binary forms, with or without
9145510Sdarrenr * modification, are permitted provided that the following conditions
10145510Sdarrenr * are met:
11145510Sdarrenr * 1. Redistributions of source code must retain the above copyright
12145510Sdarrenr *    notice, this list of conditions and the following disclaimer.
13145510Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
14145510Sdarrenr *    notice, this list of conditions and the following disclaimer in the
15145510Sdarrenr *    documentation and/or other materials provided with the distribution.
16145510Sdarrenr *
17145510Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18145510Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19145510Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20145510Sdarrenr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21145510Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22145510Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23145510Sdarrenr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24145510Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25145510Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26145510Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27145510Sdarrenr * SUCH DAMAGE.
28145510Sdarrenr *
29145510Sdarrenr * $FreeBSD$
30145510Sdarrenr */
31145510Sdarrenr
32145510Sdarrenr#include <sys/types.h>
33145510Sdarrenr#include <sys/mman.h>
34145510Sdarrenr#include <sys/socket.h>
35145510Sdarrenr#include <sys/stat.h>
36145510Sdarrenr#include <sys/time.h>
37145510Sdarrenr#include <sys/wait.h>
38255332Scy
39145510Sdarrenr#include <assert.h>
40145510Sdarrenr#include <err.h>
41145510Sdarrenr#include <fcntl.h>
42145510Sdarrenr#include <inttypes.h>
43170268Sdarrenr#include <limits.h>
44145510Sdarrenr#include <signal.h>
45145510Sdarrenr#include <stdio.h>
46145510Sdarrenr#include <stdlib.h>
47145510Sdarrenr#include <string.h>
48145510Sdarrenr#include <unistd.h>
49145510Sdarrenr
50145510Sdarrenrstatic struct timespec ts_start, ts_end;
51145510Sdarrenrstatic int alarm_timeout;
52255332Scystatic volatile int alarm_fired;
53145510Sdarrenr
54145510Sdarrenr#define timespecsub(vvp, uvp)						\
55145510Sdarrenr	do {								\
56170268Sdarrenr		(vvp)->tv_sec -= (uvp)->tv_sec;				\
57170268Sdarrenr		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
58255332Scy		if ((vvp)->tv_nsec < 0) {				\
59255332Scy			(vvp)->tv_sec--;				\
60170268Sdarrenr			(vvp)->tv_nsec += 1000000000;			\
61145510Sdarrenr		}							\
62145510Sdarrenr	} while (0)
63145510Sdarrenr
64145510Sdarrenrstatic void
65145510Sdarrenralarm_handler(int signum)
66255332Scy{
67145510Sdarrenr
68255332Scy	alarm_fired = 1;
69145510Sdarrenr}
70145510Sdarrenr
71255332Scystatic void
72255332Scybenchmark_start(void)
73255332Scy{
74145510Sdarrenr	int error;
75145510Sdarrenr
76255332Scy	alarm_fired = 0;
77255332Scy	if (alarm_timeout) {
78255332Scy		signal(SIGALRM, alarm_handler);
79255332Scy		alarm(alarm_timeout);
80255332Scy	}
81255332Scy	error = clock_gettime(CLOCK_REALTIME, &ts_start);
82255332Scy	assert(error == 0);
83255332Scy}
84145510Sdarrenr
85255332Scystatic void
86255332Scybenchmark_stop(void)
87255332Scy{
88255332Scy	int error;
89255332Scy
90145510Sdarrenr	error = clock_gettime(CLOCK_REALTIME, &ts_end);
91145510Sdarrenr	assert(error == 0);
92145510Sdarrenr}
93145510Sdarrenr
94255332Scyuintmax_t
95255332Scytest_getuid(uintmax_t num, uintmax_t int_arg, const char *path)
96255332Scy{
97255332Scy	uintmax_t i;
98145510Sdarrenr
99145510Sdarrenr	/*
100145510Sdarrenr	 * Thread-local data should require no locking if system
101145510Sdarrenr	 * call is MPSAFE.
102145510Sdarrenr	 */
103145510Sdarrenr	benchmark_start();
104145510Sdarrenr	for (i = 0; i < num; i++) {
105145510Sdarrenr		if (alarm_fired)
106255332Scy			break;
107255332Scy		getuid();
108145510Sdarrenr	}
109145510Sdarrenr	benchmark_stop();
110255332Scy	return (i);
111255332Scy}
112255332Scy
113255332Scyuintmax_t
114145510Sdarrenrtest_getppid(uintmax_t num, uintmax_t int_arg, const char *path)
115255332Scy{
116145510Sdarrenr	uintmax_t i;
117255332Scy
118255332Scy	/*
119145510Sdarrenr	 * This is process-local, but can change, so will require a
120145510Sdarrenr	 * lock.
121255332Scy	 */
122255332Scy	benchmark_start();
123255332Scy	for (i = 0; i < num; i++) {
124255332Scy		if (alarm_fired)
125145510Sdarrenr			break;
126255332Scy		getppid();
127145510Sdarrenr	}
128145510Sdarrenr	benchmark_stop();
129255332Scy	return (i);
130255332Scy}
131145510Sdarrenr
132145510Sdarrenruintmax_t
133145510Sdarrenrtest_clock_gettime(uintmax_t num, uintmax_t int_arg, const char *path)
134145510Sdarrenr{
135255332Scy	struct timespec ts;
136255332Scy	uintmax_t i;
137255332Scy
138255332Scy	benchmark_start();
139145510Sdarrenr	for (i = 0; i < num; i++) {
140255332Scy		if (alarm_fired)
141145510Sdarrenr			break;
142145510Sdarrenr		(void)clock_gettime(CLOCK_REALTIME, &ts);
143255332Scy	}
144145510Sdarrenr	benchmark_stop();
145145510Sdarrenr	return (i);
146145510Sdarrenr}
147145510Sdarrenr
148145510Sdarrenruintmax_t
149145510Sdarrenrtest_gettimeofday(uintmax_t num, uintmax_t int_arg, const char *path)
150145510Sdarrenr{
151145510Sdarrenr	struct timeval tv;
152145510Sdarrenr	uintmax_t i;
153170268Sdarrenr
154145510Sdarrenr	benchmark_start();
155145510Sdarrenr	for (i = 0; i < num; i++) {
156145510Sdarrenr		if (alarm_fired)
157145510Sdarrenr			break;
158145510Sdarrenr		(void)gettimeofday(&tv, NULL);
159145510Sdarrenr	}
160145510Sdarrenr	benchmark_stop();
161145510Sdarrenr	return (i);
162145510Sdarrenr}
163145510Sdarrenr
164255332Scyuintmax_t
165145510Sdarrenrtest_pipe(uintmax_t num, uintmax_t int_arg, const char *path)
166145510Sdarrenr{
167145510Sdarrenr	int fd[2], i;
168145510Sdarrenr
169145510Sdarrenr	/*
170145510Sdarrenr	 * pipe creation is expensive, as it will allocate a new file
171145510Sdarrenr	 * descriptor, allocate a new pipe, hook it all up, and return.
172145510Sdarrenr	 * Destroying is also expensive, as we now have to free up
173145510Sdarrenr	 * the file descriptors and return the pipe.
174145510Sdarrenr	 */
175145510Sdarrenr	if (pipe(fd) < 0)
176145510Sdarrenr		err(-1, "test_pipe: pipe");
177145510Sdarrenr	close(fd[0]);
178145510Sdarrenr	close(fd[1]);
179145510Sdarrenr	benchmark_start();
180145510Sdarrenr	for (i = 0; i < num; i++) {
181145510Sdarrenr		if (alarm_fired)
182145510Sdarrenr			break;
183255332Scy		if (pipe(fd) == -1)
184255332Scy			err(-1, "test_pipe: pipe");
185145510Sdarrenr		close(fd[0]);
186145510Sdarrenr		close(fd[1]);
187255332Scy	}
188255332Scy	benchmark_stop();
189255332Scy	return (i);
190255332Scy}
191255332Scy
192255332Scyuintmax_t
193255332Scytest_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path)
194145510Sdarrenr{
195145510Sdarrenr	uintmax_t i;
196145510Sdarrenr	int so;
197145510Sdarrenr
198145510Sdarrenr	so = socket(int_arg, SOCK_STREAM, 0);
199145510Sdarrenr	if (so < 0)
200145510Sdarrenr		err(-1, "test_socket_stream: socket");
201145510Sdarrenr	close(so);
202145510Sdarrenr	benchmark_start();
203145510Sdarrenr	for (i = 0; i < num; i++) {
204145510Sdarrenr		if (alarm_fired)
205145510Sdarrenr			break;
206145510Sdarrenr		so = socket(int_arg, SOCK_STREAM, 0);
207145510Sdarrenr		if (so == -1)
208145510Sdarrenr			err(-1, "test_socket_stream: socket");
209145510Sdarrenr		close(so);
210145510Sdarrenr	}
211145510Sdarrenr	benchmark_stop();
212145510Sdarrenr	return (i);
213145510Sdarrenr}
214145510Sdarrenr
215145510Sdarrenruintmax_t
216145510Sdarrenrtest_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
217145510Sdarrenr{
218145510Sdarrenr	uintmax_t i;
219145510Sdarrenr	int so;
220255332Scy
221145510Sdarrenr	so = socket(int_arg, SOCK_DGRAM, 0);
222255332Scy	if (so < 0)
223255332Scy		err(-1, "test_socket_dgram: socket");
224145510Sdarrenr	close(so);
225145510Sdarrenr	benchmark_start();
226145510Sdarrenr	for (i = 0; i < num; i++) {
227145510Sdarrenr		if (alarm_fired)
228145510Sdarrenr			break;
229255332Scy		so = socket(int_arg, SOCK_DGRAM, 0);
230255332Scy		if (so == -1)
231255332Scy			err(-1, "test_socket_dgram: socket");
232255332Scy		close(so);
233255332Scy	}
234255332Scy	benchmark_stop();
235145510Sdarrenr	return (i);
236145510Sdarrenr}
237145510Sdarrenr
238145510Sdarrenruintmax_t
239145510Sdarrenrtest_socketpair_stream(uintmax_t num, uintmax_t int_arg, const char *path)
240145510Sdarrenr{
241145510Sdarrenr	uintmax_t i;
242255332Scy	int so[2];
243145510Sdarrenr
244145510Sdarrenr	if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
245145510Sdarrenr		err(-1, "test_socketpair_stream: socketpair");
246145510Sdarrenr	close(so[0]);
247145510Sdarrenr	close(so[1]);
248145510Sdarrenr	benchmark_start();
249145510Sdarrenr	for (i = 0; i < num; i++) {
250145510Sdarrenr		if (alarm_fired)
251145510Sdarrenr			break;
252145510Sdarrenr		if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
253145510Sdarrenr			err(-1, "test_socketpair_stream: socketpair");
254145510Sdarrenr		close(so[0]);
255145510Sdarrenr		close(so[1]);
256145510Sdarrenr	}
257255332Scy	benchmark_stop();
258255332Scy	return (i);
259255332Scy}
260255332Scy
261255332Scyuintmax_t
262255332Scytest_socketpair_dgram(uintmax_t num, uintmax_t int_arg, const char *path)
263145510Sdarrenr{
264145510Sdarrenr	uintmax_t i;
265145510Sdarrenr	int so[2];
266145510Sdarrenr
267255332Scy	if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
268145510Sdarrenr		err(-1, "test_socketpair_dgram: socketpair");
269145510Sdarrenr	close(so[0]);
270255332Scy	close(so[1]);
271255332Scy	benchmark_start();
272255332Scy	for (i = 0; i < num; i++) {
273145510Sdarrenr		if (alarm_fired)
274145510Sdarrenr			break;
275145510Sdarrenr		if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
276145510Sdarrenr			err(-1, "test_socketpair_dgram: socketpair");
277255332Scy		close(so[0]);
278255332Scy		close(so[1]);
279255332Scy	}
280255332Scy	benchmark_stop();
281255332Scy	return (i);
282255332Scy}
283255332Scy
284255332Scyuintmax_t
285145510Sdarrenrtest_create_unlink(uintmax_t num, uintmax_t int_arg, const char *path)
286255332Scy{
287255332Scy	uintmax_t i;
288255332Scy	int fd;
289145510Sdarrenr
290145510Sdarrenr	(void)unlink(path);
291145510Sdarrenr	fd = open(path, O_RDWR | O_CREAT, 0600);
292255332Scy	if (fd < 0)
293145510Sdarrenr		err(-1, "test_create_unlink: create: %s", path);
294145510Sdarrenr	close(fd);
295145510Sdarrenr	if (unlink(path) < 0)
296145510Sdarrenr		err(-1, "test_create_unlink: unlink: %s", path);
297145510Sdarrenr	benchmark_start();
298145510Sdarrenr	for (i = 0; i < num; i++) {
299255332Scy		if (alarm_fired)
300255332Scy			break;
301145510Sdarrenr		fd = open(path, O_RDWR | O_CREAT, 0600);
302145510Sdarrenr		if (fd < 0)
303255332Scy			err(-1, "test_create_unlink: create: %s", path);
304255332Scy		close(fd);
305255332Scy		if (unlink(path) < 0)
306255332Scy			err(-1, "test_create_unlink: unlink: %s", path);
307255332Scy	}
308255332Scy	benchmark_stop();
309145510Sdarrenr	return (i);
310145510Sdarrenr}
311145510Sdarrenr
312145510Sdarrenruintmax_t
313255332Scytest_open_close(uintmax_t num, uintmax_t int_arg, const char *path)
314255332Scy{
315255332Scy	uintmax_t i;
316255332Scy	int fd;
317255332Scy
318255332Scy	fd = open(path, O_RDONLY);
319255332Scy	if (fd < 0)
320255332Scy		err(-1, "test_open_close: %s", path);
321255332Scy	close(fd);
322255332Scy
323255332Scy	benchmark_start();
324255332Scy	for (i = 0; i < num; i++) {
325255332Scy		if (alarm_fired)
326255332Scy			break;
327255332Scy		fd = open(path, O_RDONLY);
328255332Scy		if (fd < 0)
329255332Scy			err(-1, "test_open_close: %s", path);
330145510Sdarrenr		close(fd);
331145510Sdarrenr	}
332255332Scy	benchmark_stop();
333145510Sdarrenr	return (i);
334145510Sdarrenr}
335145510Sdarrenr
336145510Sdarrenruintmax_t
337145510Sdarrenrtest_read(uintmax_t num, uintmax_t int_arg, const char *path)
338255332Scy{
339255332Scy	char buf[int_arg];
340255332Scy	uintmax_t i;
341255332Scy	int fd;
342255332Scy
343255332Scy	fd = open(path, O_RDONLY);
344255332Scy	if (fd < 0)
345255332Scy		err(-1, "test_open_read: %s", path);
346255332Scy	(void)pread(fd, buf, int_arg, 0);
347255332Scy
348255332Scy	benchmark_start();
349255332Scy	for (i = 0; i < num; i++) {
350255332Scy		if (alarm_fired)
351145510Sdarrenr			break;
352145510Sdarrenr		(void)pread(fd, buf, int_arg, 0);
353145510Sdarrenr	}
354255332Scy	benchmark_stop();
355255332Scy	close(fd);
356255332Scy	return (i);
357255332Scy}
358145510Sdarrenr
359255332Scyuintmax_t
360255332Scytest_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
361255332Scy{
362255332Scy	char buf[int_arg];
363255332Scy	uintmax_t i;
364255332Scy	int fd;
365255332Scy
366255332Scy	fd = open(path, O_RDONLY);
367255332Scy	if (fd < 0)
368255332Scy		err(-1, "test_open_read_close: %s", path);
369255332Scy	(void)read(fd, buf, int_arg);
370145510Sdarrenr	close(fd);
371145510Sdarrenr
372145510Sdarrenr	benchmark_start();
373255332Scy	for (i = 0; i < num; i++) {
374255332Scy		if (alarm_fired)
375255332Scy			break;
376255332Scy		fd = open(path, O_RDONLY);
377255332Scy		if (fd < 0)
378255332Scy			err(-1, "test_open_read_close: %s", path);
379255332Scy		(void)read(fd, buf, int_arg);
380255332Scy		close(fd);
381255332Scy	}
382255332Scy	benchmark_stop();
383255332Scy	return (i);
384255332Scy}
385255332Scy
386255332Scyuintmax_t
387255332Scytest_dup(uintmax_t num, uintmax_t int_arg, const char *path)
388145510Sdarrenr{
389145510Sdarrenr	int fd, i, shmfd;
390255332Scy
391255332Scy	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
392255332Scy	if (shmfd < 0)
393255332Scy		err(-1, "test_dup: shm_open");
394255332Scy	fd = dup(shmfd);
395255332Scy	if (fd >= 0)
396255332Scy		close(fd);
397255332Scy	benchmark_start();
398255332Scy	for (i = 0; i < num; i++) {
399255332Scy		if (alarm_fired)
400255332Scy			break;
401255332Scy		fd = dup(shmfd);
402255332Scy		if (fd >= 0)
403255332Scy			close(fd);
404255332Scy	}
405255332Scy	benchmark_stop();
406255332Scy	close(shmfd);
407145510Sdarrenr	return (i);
408145510Sdarrenr}
409255332Scy
410145510Sdarrenruintmax_t
411145510Sdarrenrtest_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
412255332Scy{
413145510Sdarrenr	uintmax_t i;
414145510Sdarrenr	int shmfd;
415145510Sdarrenr
416145510Sdarrenr	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
417145510Sdarrenr	if (shmfd < 0)
418145510Sdarrenr		err(-1, "test_shmfd: shm_open");
419145510Sdarrenr	close(shmfd);
420145510Sdarrenr	benchmark_start();
421145510Sdarrenr	for (i = 0; i < num; i++) {
422145510Sdarrenr		if (alarm_fired)
423145510Sdarrenr			break;
424255332Scy		shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
425255332Scy		if (shmfd < 0)
426255332Scy			err(-1, "test_shmfd: shm_open");
427255332Scy		close(shmfd);
428255332Scy	}
429255332Scy	benchmark_stop();
430255332Scy	return (i);
431255332Scy}
432255332Scy
433255332Scyuintmax_t
434255332Scytest_fstat_shmfd(uintmax_t num, uintmax_t int_arg, const char *path)
435255332Scy{
436255332Scy	struct stat sb;
437255332Scy	uintmax_t i;
438255332Scy	int shmfd;
439255332Scy
440255332Scy	shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
441255332Scy	if (shmfd < 0)
442255332Scy		err(-1, "test_fstat_shmfd: shm_open");
443255332Scy	if (fstat(shmfd, &sb) < 0)
444255332Scy		err(-1, "test_fstat_shmfd: fstat");
445255332Scy	benchmark_start();
446255332Scy	for (i = 0; i < num; i++) {
447255332Scy		if (alarm_fired)
448255332Scy			break;
449255332Scy		(void)fstat(shmfd, &sb);
450255332Scy	}
451255332Scy	benchmark_stop();
452255332Scy	close(shmfd);
453255332Scy	return (i);
454255332Scy}
455255332Scy
456255332Scyuintmax_t
457255332Scytest_fork(uintmax_t num, uintmax_t int_arg, const char *path)
458255332Scy{
459255332Scy	pid_t pid;
460255332Scy	uintmax_t i;
461255332Scy
462255332Scy	pid = fork();
463255332Scy	if (pid < 0)
464255332Scy		err(-1, "test_fork: fork");
465255332Scy	if (pid == 0)
466255332Scy		_exit(0);
467255332Scy	if (waitpid(pid, NULL, 0) < 0)
468255332Scy		err(-1, "test_fork: waitpid");
469255332Scy	benchmark_start();
470255332Scy	for (i = 0; i < num; i++) {
471255332Scy		if (alarm_fired)
472255332Scy			break;
473255332Scy		pid = fork();
474255332Scy		if (pid < 0)
475255332Scy			err(-1, "test_fork: fork");
476255332Scy		if (pid == 0)
477255332Scy			_exit(0);
478255332Scy		if (waitpid(pid, NULL, 0) < 0)
479255332Scy			err(-1, "test_fork: waitpid");
480255332Scy	}
481255332Scy	benchmark_stop();
482255332Scy	return (i);
483255332Scy}
484255332Scy
485255332Scyuintmax_t
486255332Scytest_vfork(uintmax_t num, uintmax_t int_arg, const char *path)
487255332Scy{
488255332Scy	pid_t pid;
489255332Scy	uintmax_t i;
490255332Scy
491255332Scy	pid = vfork();
492255332Scy	if (pid < 0)
493255332Scy		err(-1, "test_vfork: vfork");
494255332Scy	if (pid == 0)
495255332Scy		_exit(0);
496255332Scy	if (waitpid(pid, NULL, 0) < 0)
497255332Scy		err(-1, "test_vfork: waitpid");
498255332Scy	benchmark_start();
499255332Scy	for (i = 0; i < num; i++) {
500255332Scy		if (alarm_fired)
501255332Scy			break;
502255332Scy		pid = vfork();
503255332Scy		if (pid < 0)
504255332Scy			err(-1, "test_vfork: vfork");
505255332Scy		if (pid == 0)
506255332Scy			_exit(0);
507255332Scy		if (waitpid(pid, NULL, 0) < 0)
508255332Scy			err(-1, "test_vfork: waitpid");
509255332Scy	}
510255332Scy	benchmark_stop();
511255332Scy	return (i);
512255332Scy}
513255332Scy
514255332Scy#define	USR_BIN_TRUE	"/usr/bin/true"
515255332Scystatic char *execve_args[] = { USR_BIN_TRUE, NULL};
516255332Scyextern char **environ;
517255332Scy
518255332Scyuintmax_t
519255332Scytest_fork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
520255332Scy{
521255332Scy	pid_t pid;
522255332Scy	uintmax_t i;
523255332Scy
524255332Scy	pid = fork();
525255332Scy	if (pid < 0)
526255332Scy		err(-1, "test_fork_exec: fork");
527255332Scy	if (pid == 0) {
528255332Scy		(void)execve(USR_BIN_TRUE, execve_args, environ);
529255332Scy		err(-1, "execve");
530255332Scy	}
531255332Scy	if (waitpid(pid, NULL, 0) < 0)
532255332Scy		err(-1, "test_fork: waitpid");
533255332Scy	benchmark_start();
534255332Scy	for (i = 0; i < num; i++) {
535255332Scy		if (alarm_fired)
536255332Scy			break;
537255332Scy		pid = fork();
538255332Scy		if (pid < 0)
539255332Scy			err(-1, "test_fork_exec: fork");
540255332Scy		if (pid == 0) {
541255332Scy			(void)execve(USR_BIN_TRUE, execve_args, environ);
542255332Scy			err(-1, "test_fork_exec: execve");
543255332Scy		}
544255332Scy		if (waitpid(pid, NULL, 0) < 0)
545255332Scy			err(-1, "test_fork_exec: waitpid");
546255332Scy	}
547255332Scy	benchmark_stop();
548255332Scy	return (i);
549255332Scy}
550255332Scy
551255332Scyuintmax_t
552255332Scytest_vfork_exec(uintmax_t num, uintmax_t int_arg, const char *path)
553255332Scy{
554255332Scy	pid_t pid;
555255332Scy	uintmax_t i;
556255332Scy
557255332Scy	pid = vfork();
558255332Scy	if (pid < 0)
559255332Scy		err(-1, "test_vfork_exec: vfork");
560255332Scy	if (pid == 0) {
561255332Scy		(void)execve(USR_BIN_TRUE, execve_args, environ);
562255332Scy		err(-1, "test_vfork_exec: execve");
563255332Scy	}
564255332Scy	if (waitpid(pid, NULL, 0) < 0)
565255332Scy		err(-1, "test_vfork_exec: waitpid");
566145510Sdarrenr	benchmark_start();
567145510Sdarrenr	for (i = 0; i < num; i++) {
568255332Scy		if (alarm_fired)
569255332Scy			break;
570255332Scy		pid = vfork();
571255332Scy		if (pid < 0)
572255332Scy			err(-1, "test_vfork_exec: vfork");
573255332Scy		if (pid == 0) {
574255332Scy			(void)execve(USR_BIN_TRUE, execve_args, environ);
575255332Scy			err(-1, "execve");
576255332Scy		}
577255332Scy		if (waitpid(pid, NULL, 0) < 0)
578255332Scy			err(-1, "test_vfork_exec: waitpid");
579255332Scy	}
580255332Scy	benchmark_stop();
581255332Scy	return (i);
582255332Scy}
583255332Scy
584255332Scyuintmax_t
585255332Scytest_chroot(uintmax_t num, uintmax_t int_arg, const char *path)
586255332Scy{
587255332Scy	uintmax_t i;
588255332Scy
589255332Scy	if (chroot("/") < 0)
590255332Scy		err(-1, "test_chroot: chroot");
591255332Scy	benchmark_start();
592255332Scy	for (i = 0; i < num; i++) {
593255332Scy		if (alarm_fired)
594255332Scy			break;
595255332Scy		if (chroot("/") < 0)
596255332Scy			err(-1, "test_chroot: chroot");
597255332Scy	}
598145510Sdarrenr	benchmark_stop();
599145510Sdarrenr	return (i);
600145510Sdarrenr}
601145510Sdarrenr
602145510Sdarrenruintmax_t
603145510Sdarrenrtest_setuid(uintmax_t num, uintmax_t int_arg, const char *path)
604145510Sdarrenr{
605145510Sdarrenr	uid_t uid;
606145510Sdarrenr	uintmax_t i;
607145510Sdarrenr
608145510Sdarrenr	uid = getuid();
609145510Sdarrenr	if (setuid(uid) < 0)
610145510Sdarrenr		err(-1, "test_setuid: setuid");
611145510Sdarrenr	benchmark_start();
612145510Sdarrenr	for (i = 0; i < num; i++) {
613145510Sdarrenr		if (alarm_fired)
614145510Sdarrenr			break;
615145510Sdarrenr		if (setuid(uid) < 0)
616145510Sdarrenr			err(-1, "test_setuid: setuid");
617145510Sdarrenr	}
618145510Sdarrenr	benchmark_stop();
619145510Sdarrenr	return (i);
620145510Sdarrenr}
621145510Sdarrenr
622145510Sdarrenrstruct test {
623145510Sdarrenr	const char	*t_name;
624145510Sdarrenr	uintmax_t	(*t_func)(uintmax_t, uintmax_t, const char *);
625145510Sdarrenr	int		 t_flags;
626145510Sdarrenr	uintmax_t	 t_int;
627145510Sdarrenr};
628145510Sdarrenr
629145510Sdarrenr#define	FLAG_PATH	0x00000001
630145510Sdarrenr
631145510Sdarrenrstatic const struct test tests[] = {
632145510Sdarrenr	{ "getuid", test_getuid },
633145510Sdarrenr	{ "getppid", test_getppid },
634145510Sdarrenr	{ "clock_gettime", test_clock_gettime },
635145510Sdarrenr	{ "gettimeofday", test_gettimeofday },
636145510Sdarrenr	{ "pipe", test_pipe },
637145510Sdarrenr	{ "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
638145510Sdarrenr	{ "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
639145510Sdarrenr	{ "socketpair_stream", test_socketpair_stream },
640145510Sdarrenr	{ "socketpair_dgram", test_socketpair_dgram },
641145510Sdarrenr	{ "socket_tcp", test_socket_stream, .t_int = PF_INET },
642145510Sdarrenr	{ "socket_udp", test_socket_dgram, .t_int = PF_INET },
643145510Sdarrenr	{ "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
644145510Sdarrenr	{ "open_close", test_open_close, .t_flags = FLAG_PATH },
645145510Sdarrenr	{ "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
646145510Sdarrenr	    .t_int = 1 },
647145510Sdarrenr	{ "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
648145510Sdarrenr	    .t_int = 10 },
649145510Sdarrenr	{ "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
650145510Sdarrenr	    .t_int = 100 },
651145510Sdarrenr	{ "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
652145510Sdarrenr	    .t_int = 1000 },
653145510Sdarrenr	{ "open_read_close_10000", test_open_read_close,
654145510Sdarrenr	    .t_flags = FLAG_PATH, .t_int = 10000 },
655145510Sdarrenr	{ "open_read_close_100000", test_open_read_close,
656145510Sdarrenr	    .t_flags = FLAG_PATH, .t_int = 100000 },
657145510Sdarrenr	{ "open_read_close_1000000", test_open_read_close,
658145510Sdarrenr	    .t_flags = FLAG_PATH, .t_int = 1000000 },
659145510Sdarrenr	{ "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
660145510Sdarrenr	{ "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
661145510Sdarrenr	{ "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
662145510Sdarrenr	{ "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
663145510Sdarrenr	{ "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
664145510Sdarrenr	{ "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
665170268Sdarrenr	{ "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
666170268Sdarrenr	{ "dup", test_dup },
667170268Sdarrenr	{ "shmfd", test_shmfd },
668170268Sdarrenr	{ "fstat_shmfd", test_fstat_shmfd },
669170268Sdarrenr	{ "fork", test_fork },
670170268Sdarrenr	{ "vfork", test_vfork },
671170268Sdarrenr	{ "fork_exec", test_fork_exec },
672170268Sdarrenr	{ "vfork_exec", test_vfork_exec },
673170268Sdarrenr	{ "chroot", test_chroot },
674170268Sdarrenr	{ "setuid", test_setuid },
675170268Sdarrenr};
676170268Sdarrenrstatic const int tests_count = sizeof(tests) / sizeof(tests[0]);
677170268Sdarrenr
678170268Sdarrenrstatic void
679170268Sdarrenrusage(void)
680170268Sdarrenr{
681170268Sdarrenr	int i;
682170268Sdarrenr
683255332Scy	fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
684170268Sdarrenr	    "[-p path] [-s seconds] test\n");
685255332Scy	for (i = 0; i < tests_count; i++)
686170268Sdarrenr		fprintf(stderr, "  %s\n", tests[i].t_name);
687170268Sdarrenr	exit(-1);
688170268Sdarrenr}
689170268Sdarrenr
690170268Sdarrenrint
691170268Sdarrenrmain(int argc, char *argv[])
692170268Sdarrenr{
693170268Sdarrenr	struct timespec ts_res;
694170268Sdarrenr	const struct test *the_test;
695170268Sdarrenr	const char *path;
696255332Scy	long long ll;
697255332Scy	char *endp;
698255332Scy	int ch, error, i, j, k;
699170268Sdarrenr	uintmax_t iterations, loops;
700170268Sdarrenr
701170268Sdarrenr	alarm_timeout = 1;
702170268Sdarrenr	iterations = 0;
703170268Sdarrenr	loops = 10;
704170268Sdarrenr	path = NULL;
705170268Sdarrenr	while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
706170268Sdarrenr		switch (ch) {
707170268Sdarrenr		case 'i':
708170268Sdarrenr			ll = strtol(optarg, &endp, 10);
709170268Sdarrenr			if (*endp != 0 || ll < 1 || ll > 100000)
710170268Sdarrenr				usage();
711170268Sdarrenr			iterations = ll;
712170268Sdarrenr			break;
713170268Sdarrenr
714170268Sdarrenr		case 'l':
715170268Sdarrenr			ll = strtol(optarg, &endp, 10);
716170268Sdarrenr			if (*endp != 0 || ll < 1 || ll > 100000)
717170268Sdarrenr				usage();
718170268Sdarrenr			loops = ll;
719170268Sdarrenr			break;
720170268Sdarrenr
721170268Sdarrenr		case 'p':
722170268Sdarrenr			path = optarg;
723170268Sdarrenr			break;
724170268Sdarrenr
725170268Sdarrenr		case 's':
726170268Sdarrenr			ll = strtol(optarg, &endp, 10);
727170268Sdarrenr			if (*endp != 0 || ll < 1 || ll > 60*60)
728170268Sdarrenr				usage();
729255332Scy			alarm_timeout = ll;
730170268Sdarrenr			break;
731255332Scy
732170268Sdarrenr		case '?':
733170268Sdarrenr		default:
734170268Sdarrenr			usage();
735170268Sdarrenr		}
736170268Sdarrenr	}
737170268Sdarrenr	argc -= optind;
738170268Sdarrenr	argv += optind;
739170268Sdarrenr
740170268Sdarrenr	if (iterations < 1 && alarm_timeout < 1)
741255332Scy		usage();
742170268Sdarrenr	if (iterations < 1)
743255332Scy		iterations = UINT64_MAX;
744255332Scy	if (loops < 1)
745255332Scy		loops = 1;
746255332Scy
747255332Scy	if (argc < 1)
748255332Scy		usage();
749255332Scy
750255332Scy	/*
751255332Scy	 * Validate test list and that, if a path is required, it is
752170268Sdarrenr	 * defined.
753255332Scy	 */
754170268Sdarrenr	for (j = 0; j < argc; j++) {
755170268Sdarrenr		the_test = NULL;
756170268Sdarrenr		for (i = 0; i < tests_count; i++) {
757170268Sdarrenr			if (strcmp(argv[j], tests[i].t_name) == 0)
758170268Sdarrenr				the_test = &tests[i];
759170268Sdarrenr		}
760170268Sdarrenr		if (the_test == NULL)
761170268Sdarrenr			usage();
762170268Sdarrenr		if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
763170268Sdarrenr			errx(-1, "%s requires -p", the_test->t_name);
764170268Sdarrenr		}
765170268Sdarrenr	}
766255332Scy
767255332Scy	error = clock_getres(CLOCK_REALTIME, &ts_res);
768255332Scy	assert(error == 0);
769255332Scy	printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
770255332Scy	    (uintmax_t)ts_res.tv_nsec);
771255332Scy	printf("test\tloop\ttime\titerations\tperiteration\n");
772255332Scy
773255332Scy	for (j = 0; j < argc; j++) {
774255332Scy		uintmax_t calls, nsecsperit;
775255332Scy
776255332Scy		the_test = NULL;
777255332Scy		for (i = 0; i < tests_count; i++) {
778255332Scy			if (strcmp(argv[j], tests[i].t_name) == 0)
779255332Scy				the_test = &tests[i];
780255332Scy		}
781255332Scy
782255332Scy		/*
783255332Scy		 * Run one warmup, then do the real thing (loops) times.
784255332Scy		 */
785255332Scy		the_test->t_func(iterations, the_test->t_int, path);
786255332Scy		calls = 0;
787255332Scy		for (k = 0; k < loops; k++) {
788255332Scy			calls = the_test->t_func(iterations, the_test->t_int,
789255332Scy			    path);
790255332Scy			timespecsub(&ts_end, &ts_start);
791255332Scy			printf("%s\t%d\t", the_test->t_name, k);
792255332Scy			printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
793255332Scy			    (uintmax_t)ts_end.tv_nsec, calls);
794255332Scy
795255332Scy		/*
796255332Scy		 * Note.  This assumes that each iteration takes less than
797255332Scy		 * a second, and that our total nanoseconds doesn't exceed
798255332Scy		 * the room in our arithmetic unit.  Fine for system calls,
799255332Scy		 * but not for long things.
800255332Scy		 */
801255332Scy			nsecsperit = ts_end.tv_sec * 1000000000;
802255332Scy			nsecsperit += ts_end.tv_nsec;
803255332Scy			nsecsperit /= calls;
804255332Scy			printf("0.%09ju\n", (uintmax_t)nsecsperit);
805255332Scy		}
806255332Scy	}
807255332Scy	return (0);
808255332Scy}
809255332Scy