#include <sys/param.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/procdesc.h>
#include <sys/resource.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 <limits.h>
#ifdef WITH_PTHREAD
#include <pthread.h>
#endif
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static struct timespec ts_start, ts_end;
static int alarm_timeout;
static volatile int alarm_fired;
#define BENCHMARK_FOREACH(I, NUM) for (I = 0; I < NUM && alarm_fired == 0; I++)
static void
alarm_handler(int signum __unused)
{
alarm_fired = 1;
}
static void
benchmark_start(void)
{
int error;
alarm_fired = 0;
if (alarm_timeout) {
signal(SIGALRM, alarm_handler);
alarm(alarm_timeout);
}
error = clock_gettime(CLOCK_REALTIME, &ts_start);
assert(error == 0);
}
static void
benchmark_stop(void)
{
int error;
error = clock_gettime(CLOCK_REALTIME, &ts_end);
assert(error == 0);
}
static uintmax_t
test_access(uintmax_t num, uintmax_t int_arg __unused, const char *path)
{
uintmax_t i;
int fd;
fd = access(path, O_RDONLY);
if (fd < 0)
err(-1, "test_access: %s", path);
close(fd);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
access(path, O_RDONLY);
close(fd);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_bad_open(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
open("", O_RDONLY);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_chroot(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
if (chroot("/") < 0)
err(-1, "test_chroot: chroot");
benchmark_start();
BENCHMARK_FOREACH(i, num) {
if (chroot("/") < 0)
err(-1, "test_chroot: chroot");
}
benchmark_stop();
return (i);
}
static uintmax_t
test_clock_gettime(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
struct timespec ts;
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)clock_gettime(CLOCK_REALTIME, &ts);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_create_unlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
{
uintmax_t i;
int fd;
(void)unlink(path);
fd = open(path, O_RDWR | O_CREAT, 0600);
if (fd < 0)
err(-1, "test_create_unlink: create: %s", path);
close(fd);
if (unlink(path) < 0)
err(-1, "test_create_unlink: unlink: %s", path);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
fd = open(path, O_RDWR | O_CREAT, 0600);
if (fd < 0)
err(-1, "test_create_unlink: create: %s", path);
close(fd);
if (unlink(path) < 0)
err(-1, "test_create_unlink: unlink: %s", path);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_fork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
pid_t pid;
uintmax_t 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();
BENCHMARK_FOREACH(i, num) {
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();
return (i);
}
#define USR_BIN_TRUE "/usr/bin/true"
static char *execve_args[] = { __DECONST(char *, USR_BIN_TRUE), NULL};
extern char **environ;
static uintmax_t
test_fork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
pid_t pid;
uintmax_t i;
pid = fork();
if (pid < 0)
err(-1, "test_fork_exec: 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();
BENCHMARK_FOREACH(i, num) {
pid = fork();
if (pid < 0)
err(-1, "test_fork_exec: fork");
if (pid == 0) {
(void)execve(USR_BIN_TRUE, execve_args, environ);
err(-1, "test_fork_exec: execve");
}
if (waitpid(pid, NULL, 0) < 0)
err(-1, "test_fork_exec: waitpid");
}
benchmark_stop();
return (i);
}
static uintmax_t
test_getppid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
getppid();
}
benchmark_stop();
return (i);
}
static uintmax_t
test_getpriority(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)getpriority(PRIO_PROCESS, 0);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_getprogname(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)getprogname();
}
benchmark_stop();
return (i);
}
static uintmax_t
test_getresuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uid_t ruid, euid, suid;
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)getresuid(&ruid, &euid, &suid);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_gettimeofday(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
struct timeval tv;
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)gettimeofday(&tv, NULL);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_getuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
getuid();
}
benchmark_stop();
return (i);
}
static uintmax_t
test_lstat(uintmax_t num, uintmax_t int_arg __unused, const char *path)
{
struct stat sb;
uintmax_t i;
int error;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
error = lstat(path, &sb);
if (error != 0)
err(-1, "lstat");
}
benchmark_stop();
return (i);
}
static uintmax_t
test_memcpy(uintmax_t num, uintmax_t int_arg, const char *path __unused)
{
char buf[int_arg], buf2[int_arg];
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
memcpy(buf2, buf, int_arg);
memcpy(buf, buf2, int_arg);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_open_close(uintmax_t num, uintmax_t int_arg __unused, const char *path)
{
uintmax_t i;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
err(-1, "test_open_close: %s", path);
close(fd);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
fd = open(path, O_RDONLY);
if (fd < 0)
err(-1, "test_open_close: %s", path);
close(fd);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_open_read_close(uintmax_t num, uintmax_t int_arg, const char *path)
{
char buf[int_arg];
uintmax_t i;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
err(-1, "test_open_read_close: %s", path);
(void)read(fd, buf, int_arg);
close(fd);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
fd = open(path, O_RDONLY);
if (fd < 0)
err(-1, "test_open_read_close: %s", path);
(void)read(fd, buf, int_arg);
close(fd);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_pipe(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
int fd[2];
uintmax_t i;
if (pipe(fd) < 0)
err(-1, "test_pipe: pipe");
close(fd[0]);
close(fd[1]);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
if (pipe(fd) == -1)
err(-1, "test_pipe: pipe");
close(fd[0]);
close(fd[1]);
}
benchmark_stop();
return (i);
}
static void
readx(int fd, char *buf, size_t size)
{
ssize_t ret;
do {
ret = read(fd, buf, size);
if (ret == -1)
err(1, "read");
assert((size_t)ret <= size);
size -= ret;
buf += ret;
} while (size > 0);
}
static void
writex(int fd, const char *buf, size_t size)
{
ssize_t ret;
do {
ret = write(fd, buf, size);
if (ret == -1)
err(1, "write");
assert((size_t)ret <= size);
size -= ret;
buf += ret;
} while (size > 0);
}
static uintmax_t
test_pipeping(uintmax_t num, uintmax_t int_arg, const char *path __unused)
{
char buf[int_arg];
uintmax_t i;
pid_t pid;
int fd[2], procfd;
if (pipe(fd) < 0)
err(-1, "pipe");
pid = pdfork(&procfd, 0);
if (pid < 0)
err(1, "pdfork");
if (pid == 0) {
close(fd[0]);
for (;;) {
readx(fd[1], buf, int_arg);
writex(fd[1], buf, int_arg);
}
}
close(fd[1]);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
writex(fd[0], buf, int_arg);
readx(fd[0], buf, int_arg);
}
benchmark_stop();
close(procfd);
return (i);
}
#ifdef WITH_PTHREAD
struct pipepingtd_ctx {
int fd;
uintmax_t int_arg;
};
static void *
pipepingtd_proc(void *arg)
{
struct pipepingtd_ctx *ctxp;
int fd;
void *buf;
uintmax_t int_arg;
ctxp = arg;
fd = ctxp->fd;
int_arg = ctxp->int_arg;
buf = malloc(int_arg);
if (buf == NULL)
err(1, "malloc");
for (;;) {
readx(fd, buf, int_arg);
writex(fd, buf, int_arg);
}
}
static uintmax_t
test_pipepingtd(uintmax_t num, uintmax_t int_arg, const char *path __unused)
{
struct pipepingtd_ctx ctx;
char buf[int_arg];
pthread_t td;
uintmax_t i;
int error, fd[2];
if (pipe(fd) < 0)
err(-1, "pipe");
ctx.fd = fd[1];
ctx.int_arg = int_arg;
error = pthread_create(&td, NULL, pipepingtd_proc, &ctx);
if (error != 0)
err(1, "pthread_create");
benchmark_start();
BENCHMARK_FOREACH(i, num) {
writex(fd[0], buf, int_arg);
readx(fd[0], buf, int_arg);
}
benchmark_stop();
pthread_cancel(td);
return (i);
}
#endif
static uintmax_t
test_read(uintmax_t num, uintmax_t int_arg, const char *path)
{
char buf[int_arg];
uintmax_t i;
int fd;
fd = open(path, O_RDONLY);
if (fd < 0)
err(-1, "test_open_read: %s", path);
(void)pread(fd, buf, int_arg, 0);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)pread(fd, buf, int_arg, 0);
}
benchmark_stop();
close(fd);
return (i);
}
static uintmax_t
test_select(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
fd_set readfds, writefds, exceptfds;
struct timeval tv;
uintmax_t i;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
tv.tv_sec = 0;
tv.tv_usec = 0;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)select(0, &readfds, &writefds, &exceptfds, &tv);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_semaping(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
pid_t pid;
sem_t *buf;
int error, j, procfd;
buf = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
if (buf == MAP_FAILED)
err(1, "mmap");
for (j = 0; j < 2; j++) {
error = sem_init(&buf[j], 1, 0);
if (error != 0)
err(1, "sem_init");
}
pid = pdfork(&procfd, 0);
if (pid < 0)
err(1, "pdfork");
if (pid == 0) {
for (;;) {
error = sem_wait(&buf[0]);
if (error != 0)
err(1, "sem_wait");
error = sem_post(&buf[1]);
if (error != 0)
err(1, "sem_post");
}
}
benchmark_start();
BENCHMARK_FOREACH(i, num) {
error = sem_post(&buf[0]);
if (error != 0)
err(1, "sem_post");
error = sem_wait(&buf[1]);
if (error != 0)
err(1, "sem_wait");
}
benchmark_stop();
close(procfd);
for (j = 0; j < 2; j++) {
error = sem_destroy(&buf[j]);
if (error != 0)
err(1, "sem_destroy");
}
error = munmap(buf, PAGE_SIZE);
if (error != 0)
err(1, "munmap");
return (i);
}
static uintmax_t
test_setuid(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uid_t uid;
uintmax_t i;
uid = getuid();
if (setuid(uid) < 0)
err(-1, "test_setuid: setuid");
benchmark_start();
BENCHMARK_FOREACH(i, num) {
if (setuid(uid) < 0)
err(-1, "test_setuid: setuid");
}
benchmark_stop();
return (i);
}
static uintmax_t
test_shmfd(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
int shmfd;
shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
if (shmfd < 0)
err(-1, "test_shmfd: shm_open");
close(shmfd);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
if (shmfd < 0)
err(-1, "test_shmfd: shm_open");
close(shmfd);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_shmfd_dup(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
int fd, shmfd;
shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
if (shmfd < 0)
err(-1, "test_shmfd_dup: shm_open");
fd = dup(shmfd);
if (fd >= 0)
close(fd);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
fd = dup(shmfd);
if (fd >= 0)
close(fd);
}
benchmark_stop();
close(shmfd);
return (i);
}
static uintmax_t
test_shmfd_fstat(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
struct stat sb;
uintmax_t i;
int shmfd;
shmfd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
if (shmfd < 0)
err(-1, "test_shmfd_fstat: shm_open");
if (fstat(shmfd, &sb) < 0)
err(-1, "test_shmfd_fstat: fstat");
benchmark_start();
BENCHMARK_FOREACH(i, num) {
(void)fstat(shmfd, &sb);
}
benchmark_stop();
close(shmfd);
return (i);
}
static uintmax_t
test_socket_stream(uintmax_t num, uintmax_t int_arg, const char *path __unused)
{
uintmax_t i;
int so;
so = socket(int_arg, SOCK_STREAM, 0);
if (so < 0)
err(-1, "test_socket_stream: socket");
close(so);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
so = socket(int_arg, SOCK_STREAM, 0);
if (so == -1)
err(-1, "test_socket_stream: socket");
close(so);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_socket_dgram(uintmax_t num, uintmax_t int_arg, const char *path __unused)
{
uintmax_t i;
int so;
so = socket(int_arg, SOCK_DGRAM, 0);
if (so < 0)
err(-1, "test_socket_dgram: socket");
close(so);
benchmark_start();
BENCHMARK_FOREACH(i, num) {
so = socket(int_arg, SOCK_DGRAM, 0);
if (so == -1)
err(-1, "test_socket_dgram: socket");
close(so);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_socketpair_stream(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
int 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();
BENCHMARK_FOREACH(i, num) {
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, so) == -1)
err(-1, "test_socketpair_stream: socketpair");
close(so[0]);
close(so[1]);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_socketpair_dgram(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
uintmax_t i;
int 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();
BENCHMARK_FOREACH(i, num) {
if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, so) == -1)
err(-1, "test_socketpair_dgram: socketpair");
close(so[0]);
close(so[1]);
}
benchmark_stop();
return (i);
}
static uintmax_t
test_readlink(uintmax_t num, uintmax_t int_arg __unused, const char *path)
{
char buf[PATH_MAX];
ssize_t rv;
uintmax_t i;
benchmark_start();
BENCHMARK_FOREACH(i, num) {
rv = readlink(path, buf, sizeof(buf));
if (rv < 0 && errno != EINVAL)
err(-1, "readlink");
}
benchmark_stop();
return (i);
}
static uintmax_t
test_vfork(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
pid_t pid;
uintmax_t 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();
BENCHMARK_FOREACH(i, num) {
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();
return (i);
}
static uintmax_t
test_vfork_exec(uintmax_t num, uintmax_t int_arg __unused, const char *path __unused)
{
pid_t pid;
uintmax_t i;
pid = vfork();
if (pid < 0)
err(-1, "test_vfork_exec: vfork");
if (pid == 0) {
(void)execve(USR_BIN_TRUE, execve_args, environ);
err(-1, "test_vfork_exec: execve");
}
if (waitpid(pid, NULL, 0) < 0)
err(-1, "test_vfork_exec: waitpid");
benchmark_start();
BENCHMARK_FOREACH(i, num) {
pid = vfork();
if (pid < 0)
err(-1, "test_vfork_exec: 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_exec: waitpid");
}
benchmark_stop();
return (i);
}
struct test {
const char *t_name;
uintmax_t (*t_func)(uintmax_t, uintmax_t, const char *);
int t_flags;
uintmax_t t_int;
};
#define FLAG_PATH 0x00000001
static const struct test tests[] = {
{ "access", test_access, .t_flags = FLAG_PATH },
{ "bad_open", test_bad_open, .t_flags = 0 },
{ "chroot", test_chroot, .t_flags = 0 },
{ "clock_gettime", test_clock_gettime, .t_flags = 0 },
{ "create_unlink", test_create_unlink, .t_flags = FLAG_PATH },
{ "fork", test_fork, .t_flags = 0 },
{ "fork_exec", test_fork_exec, .t_flags = 0 },
{ "getppid", test_getppid, .t_flags = 0 },
{ "getpriority", test_getpriority, .t_flags = 0 },
{ "getprogname", test_getprogname, .t_flags = 0 },
{ "getresuid", test_getresuid, .t_flags = 0 },
{ "gettimeofday", test_gettimeofday, .t_flags = 0 },
{ "getuid", test_getuid, .t_flags = 0 },
{ "lstat", test_lstat, .t_flags = FLAG_PATH },
{ "memcpy_1", test_memcpy, .t_flags = 0, .t_int = 1 },
{ "memcpy_10", test_memcpy, .t_flags = 0, .t_int = 10 },
{ "memcpy_100", test_memcpy, .t_flags = 0, .t_int = 100 },
{ "memcpy_1000", test_memcpy, .t_flags = 0, .t_int = 1000 },
{ "memcpy_10000", test_memcpy, .t_flags = 0, .t_int = 10000 },
{ "memcpy_100000", test_memcpy, .t_flags = 0, .t_int = 100000 },
{ "memcpy_1000000", test_memcpy, .t_flags = 0, .t_int = 1000000 },
{ "open_close", test_open_close, .t_flags = FLAG_PATH },
{ "open_read_close_1", test_open_read_close, .t_flags = FLAG_PATH,
.t_int = 1 },
{ "open_read_close_10", test_open_read_close, .t_flags = FLAG_PATH,
.t_int = 10 },
{ "open_read_close_100", test_open_read_close, .t_flags = FLAG_PATH,
.t_int = 100 },
{ "open_read_close_1000", test_open_read_close, .t_flags = FLAG_PATH,
.t_int = 1000 },
{ "open_read_close_10000", test_open_read_close,
.t_flags = FLAG_PATH, .t_int = 10000 },
{ "open_read_close_100000", test_open_read_close,
.t_flags = FLAG_PATH, .t_int = 100000 },
{ "open_read_close_1000000", test_open_read_close,
.t_flags = FLAG_PATH, .t_int = 1000000 },
{ "pipe", test_pipe, .t_flags = 0 },
{ "pipeping_1", test_pipeping, .t_flags = 0, .t_int = 1 },
{ "pipeping_10", test_pipeping, .t_flags = 0, .t_int = 10 },
{ "pipeping_100", test_pipeping, .t_flags = 0, .t_int = 100 },
{ "pipeping_1000", test_pipeping, .t_flags = 0, .t_int = 1000 },
{ "pipeping_10000", test_pipeping, .t_flags = 0, .t_int = 10000 },
{ "pipeping_100000", test_pipeping, .t_flags = 0, .t_int = 100000 },
{ "pipeping_1000000", test_pipeping, .t_flags = 0, .t_int = 1000000 },
#ifdef WITH_PTHREAD
{ "pipepingtd_1", test_pipepingtd, .t_flags = 0, .t_int = 1 },
{ "pipepingtd_10", test_pipepingtd, .t_flags = 0, .t_int = 10 },
{ "pipepingtd_100", test_pipepingtd, .t_flags = 0, .t_int = 100 },
{ "pipepingtd_1000", test_pipepingtd, .t_flags = 0, .t_int = 1000 },
{ "pipepingtd_10000", test_pipepingtd, .t_flags = 0, .t_int = 10000 },
{ "pipepingtd_100000", test_pipepingtd, .t_flags = 0, .t_int = 100000 },
{ "pipepingtd_1000000", test_pipepingtd, .t_flags = 0, .t_int = 1000000 },
#endif
{ "read_1", test_read, .t_flags = FLAG_PATH, .t_int = 1 },
{ "read_10", test_read, .t_flags = FLAG_PATH, .t_int = 10 },
{ "read_100", test_read, .t_flags = FLAG_PATH, .t_int = 100 },
{ "read_1000", test_read, .t_flags = FLAG_PATH, .t_int = 1000 },
{ "read_10000", test_read, .t_flags = FLAG_PATH, .t_int = 10000 },
{ "read_100000", test_read, .t_flags = FLAG_PATH, .t_int = 100000 },
{ "read_1000000", test_read, .t_flags = FLAG_PATH, .t_int = 1000000 },
{ "select", test_select, .t_flags = 0 },
{ "semaping", test_semaping, .t_flags = 0 },
{ "setuid", test_setuid, .t_flags = 0 },
{ "shmfd", test_shmfd, .t_flags = 0 },
{ "shmfd_dup", test_shmfd_dup, .t_flags = 0 },
{ "shmfd_fstat", test_shmfd_fstat, .t_flags = 0 },
{ "socket_local_stream", test_socket_stream, .t_int = PF_LOCAL },
{ "socket_local_dgram", test_socket_dgram, .t_int = PF_LOCAL },
{ "socketpair_stream", test_socketpair_stream, .t_flags = 0 },
{ "socketpair_dgram", test_socketpair_dgram, .t_flags = 0 },
{ "socket_tcp", test_socket_stream, .t_int = PF_INET },
{ "socket_udp", test_socket_dgram, .t_int = PF_INET },
{ "readlink", test_readlink, .t_flags = FLAG_PATH },
{ "vfork", test_vfork, .t_flags = 0 },
{ "vfork_exec", test_vfork_exec, .t_flags = 0 },
};
static const int tests_count = sizeof(tests) / sizeof(tests[0]);
static void
usage(void)
{
int i;
fprintf(stderr, "syscall_timing [-i iterations] [-l loops] "
"[-p path] [-s seconds] 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_res;
const struct test *the_test;
const char *path;
char *tmp_dir, *tmp_path;
long long ll;
char *endp;
int ch, fd, error, i, j, rv;
uintmax_t iterations, k, loops;
alarm_timeout = 1;
iterations = 0;
loops = 10;
path = NULL;
tmp_path = NULL;
while ((ch = getopt(argc, argv, "i:l:p:s:")) != -1) {
switch (ch) {
case 'i':
ll = strtol(optarg, &endp, 10);
if (*endp != 0 || ll < 1)
usage();
iterations = ll;
break;
case 'l':
ll = strtol(optarg, &endp, 10);
if (*endp != 0 || ll < 1 || ll > 100000)
usage();
loops = ll;
break;
case 'p':
path = optarg;
break;
case 's':
ll = strtol(optarg, &endp, 10);
if (*endp != 0 || ll < 1 || ll > 60*60)
usage();
alarm_timeout = ll;
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
if (iterations < 1 && alarm_timeout < 1)
usage();
if (iterations < 1)
iterations = UINT64_MAX;
if (loops < 1)
loops = 1;
if (argc < 1)
usage();
for (j = 0; 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();
if ((the_test->t_flags & FLAG_PATH) && (path == NULL)) {
tmp_dir = strdup("/tmp/syscall_timing.XXXXXXXX");
if (tmp_dir == NULL)
err(1, "strdup");
tmp_dir = mkdtemp(tmp_dir);
if (tmp_dir == NULL)
err(1, "mkdtemp");
rv = asprintf(&tmp_path, "%s/testfile", tmp_dir);
if (rv <= 0)
err(1, "asprintf");
}
}
error = clock_getres(CLOCK_REALTIME, &ts_res);
assert(error == 0);
printf("Clock resolution: %ju.%09ju\n", (uintmax_t)ts_res.tv_sec,
(uintmax_t)ts_res.tv_nsec);
printf("test\tloop\ttime\titerations\tperiteration\n");
for (j = 0; j < argc; j++) {
uintmax_t calls, nsecsperit;
the_test = NULL;
for (i = 0; i < tests_count; i++) {
if (strcmp(argv[j], tests[i].t_name) == 0)
the_test = &tests[i];
}
if (tmp_path != NULL) {
fd = open(tmp_path, O_WRONLY | O_CREAT, 0700);
if (fd < 0)
err(1, "cannot open %s", tmp_path);
error = ftruncate(fd, 1000000);
if (error != 0)
err(1, "ftruncate");
error = close(fd);
if (error != 0)
err(1, "close");
path = tmp_path;
}
the_test->t_func(iterations, the_test->t_int, path);
calls = 0;
for (k = 0; k < loops; k++) {
calls = the_test->t_func(iterations, the_test->t_int,
path);
timespecsub(&ts_end, &ts_start, &ts_end);
printf("%s\t%ju\t", the_test->t_name, k);
printf("%ju.%09ju\t%ju\t", (uintmax_t)ts_end.tv_sec,
(uintmax_t)ts_end.tv_nsec, calls);
nsecsperit = ts_end.tv_sec * 1000000000;
nsecsperit += ts_end.tv_nsec;
nsecsperit /= calls;
printf("0.%09ju\n", (uintmax_t)nsecsperit);
}
}
if (tmp_path != NULL) {
error = unlink(tmp_path);
if (error != 0 && errno != ENOENT)
warn("cannot unlink %s", tmp_path);
error = rmdir(tmp_dir);
if (error != 0)
warn("cannot rmdir %s", tmp_dir);
}
return (0);
}