summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@mit.edu>2011-06-16 13:48:30 -0400
committerAndy Lutomirski <luto@mit.edu>2011-06-16 13:48:30 -0400
commit4e9447bf1aad5b35f099c1494d37d1fede184dfe (patch)
treeef42d43979b35837cfe507acfc221ef01ffdad05
parent8f7d7a6292b62fda37731dfc2e302f2644773325 (diff)
downloadmisc-tests-4e9447bf1aad5b35f099c1494d37d1fede184dfe.tar.gz
Add test_vsyscall
-rw-r--r--.gitignore1
-rw-r--r--Makefile5
-rw-r--r--test_vsyscall.cc291
3 files changed, 296 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 9cb133e..ee262c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@ dump-vdso
timing_test
time-warp-test
evil-clock-test
+test_vsyscall
*~
diff --git a/Makefile b/Makefile
index af12f50..575630d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,11 @@
.PHONY: all
-all: timing_test evil-clock-test
+all: timing_test evil-clock-test test_vsyscall
timing_test: timing_test.cc
g++ -o $@ -lrt -ldl -O2 -Wall -g $^
evil-clock-test: evil-clock-test.cc
g++ -o $@ -pthread -lrt -O2 -Wall -g $^
+
+test_vsyscall: test_vsyscall.cc
+ g++ -o $@ -std=gnu++0x -lrt -ldl -O2 -Wall -g $^
diff --git a/test_vsyscall.cc b/test_vsyscall.cc
new file mode 100644
index 0000000..c3976b9
--- /dev/null
+++ b/test_vsyscall.cc
@@ -0,0 +1,291 @@
+#define _POSIX_SOURCE
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+
+/* vsyscalls and vDSO */
+typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
+const gtod_t vgtod = (gtod_t)0xffffffffff600000;
+gtod_t vdso_gtod;
+
+typedef long (*time_func_t)(time_t *t);
+const time_func_t vtime = (time_func_t)0xffffffffff600400;
+time_func_t vdso_time;
+
+typedef long (*getcpu_t)(unsigned *, unsigned *, struct getcpu_cache*);
+const getcpu_t vgetcpu = (getcpu_t)0xffffffffff600800;
+getcpu_t vdso_getcpu;
+
+void init_vdso()
+{
+ void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!vdso) {
+ printf("Warning: failed to find vDSO\n");
+ return;
+ }
+
+ vdso_gtod = (gtod_t)dlsym(vdso, "gettimeofday");
+ if (!vdso_gtod)
+ printf("Warning: failed to find gettimeofday in vDSO\n");
+
+ vdso_time = (time_func_t)dlsym(vdso, "time");
+ if (!vdso_time)
+ printf("Warning: failed to find time in vDSO\n");
+
+ vdso_getcpu = (getcpu_t)dlsym(vdso, "getcpu");
+ if (!vdso_getcpu)
+ printf("Warning: failed to find getcpu in vDSO\n");
+}
+
+/* syscalls */
+static inline long sys_gtod(struct timeval *tv, struct timezone *tz)
+{
+ return syscall(__NR_gettimeofday, tv, tz);
+}
+
+static inline long sys_time(time_t *t)
+{
+ return syscall(__NR_time, t);
+}
+
+/* There is no sys_getcpu. */
+
+static void segv(int sig, siginfo_t *info, void *ctx_void)
+{
+ psiginfo(info, "Caught SIGSEGV");
+
+ ucontext_t *ctx = (ucontext_t*)ctx_void;
+ printf("RIP = %lx\n", ctx->uc_mcontext.gregs[REG_RIP]);
+
+ exit(1);
+}
+
+
+/* benchmark helper */
+template<typename Func> void benchmark(const char *desc, Func f)
+{
+ struct timespec start, end;
+ long loops = 0;
+
+ printf("Benchmarking %s ... ", desc);
+ fflush(stdout);
+
+ if (clock_gettime(CLOCK_MONOTONIC, &start)) {
+ perror("clock_gettime");
+ exit(1);
+ }
+
+ while(true)
+ {
+ long loops_now = 1000;
+ for(int i = 0; i < loops_now; i++)
+ f();
+ loops += loops_now;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &end)) {
+ perror("clock_gettime");
+ exit(1);
+ }
+
+ unsigned long long duration = (end.tv_nsec - start.tv_nsec) +
+ 1000000000ULL * (end.tv_sec - start.tv_sec);
+
+ if (duration < 500000000ULL)
+ continue;
+
+ printf("%9ld loops in %.5fs = %7.2f nsec / loop\n",
+ loops, float(duration) * 1e-9,
+ float(duration) / loops);
+ break;
+ }
+}
+
+static double tv_diff(const struct timeval &a, const struct timeval &b)
+{
+ return double(a.tv_sec - b.tv_sec) +
+ double((int)a.tv_usec - (int)b.tv_usec) * 1e-6;
+}
+
+int test(int argc, char **argv)
+{
+ printf("Testing gettimeofday...\n");
+ struct timeval tv_sys, tv_vdso, tv_vsys;
+ struct timezone tz_sys, tz_vdso, tz_vsys;
+ int ret_sys = sys_gtod(&tv_sys, &tz_sys);
+ int ret_vdso = -1;
+ if (vdso_gtod)
+ ret_vdso = vdso_gtod(&tv_vdso, &tz_vdso);
+ int ret_vsys = vgtod(&tv_vsys, &tz_vsys);
+
+ if (ret_sys) {
+ printf(" syscall failed\n");
+ } else {
+ if (ret_vdso == 0) {
+ if (tz_sys.tz_minuteswest != tz_vdso.tz_minuteswest || tz_sys.tz_dsttime != tz_vdso.tz_dsttime)
+ printf(" vDSO tz mismatch\n");
+ else
+ printf(" vDSO offset = %.6fs\n", tv_diff(tv_vdso, tv_sys));
+ } else if (vdso_gtod) {
+ printf(" vDSO failed\n");
+ }
+ if (ret_vsys == 0) {
+ if (tz_sys.tz_minuteswest != tz_vsys.tz_minuteswest || tz_sys.tz_dsttime != tz_vsys.tz_dsttime)
+ printf(" vsyscall tz mismatch\n");
+ else
+ printf(" vsyscall offset = %.6fs\n", tv_diff(tv_vsys, tv_sys));
+ }
+ }
+
+ printf("\nTesting time...\n");
+ long t_sys, t_vdso = 0, t_vsys;
+ long t2_sys = -1, t2_vdso = -1, t2_vsys = -1;
+ t_sys = sys_time(&t2_sys);
+ if (vdso_time)
+ t_vdso = vdso_time(&t2_vdso);
+ t_vsys = vtime(&t2_vsys);
+ if (t_sys < 0 || t_sys != t2_sys) {
+ printf(" syscall failed (ret:%ld output:%ld)\n", t_sys, t2_sys);
+ } else {
+ if (vdso_time) {
+ if (t_vdso < 0 || t_vdso != t2_vdso)
+ printf(" vDSO failed (ret:%ld output:%ld)\n", t_vdso, t2_vdso);
+ else
+ printf(" vDSO offset = %ld\n", t_vdso - t_sys);
+ }
+
+ if (t_vsys < 0 || t_vsys != t2_vsys)
+ printf(" vsyscall failed (ret:%ld output:%ld)\n", t_vsys, t2_vsys);
+ else
+ printf(" vsyscall offset = %ld\n", t_vsys - t_sys);
+ }
+
+ printf("Testing getcpu...\n");
+ unsigned cpu_vdso, cpu_vsys, node_vdso, node_vsys;
+ ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
+ ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
+ if (ret_vdso)
+ printf(" vDSO failed (ret:%ld)\n", (unsigned long)ret_vdso);
+ if (ret_vsys)
+ printf(" vsyscall failed (ret:%ld)\n", (unsigned long)ret_vdso);
+ if (ret_vdso == 0 && ret_vsys == 0) {
+ if (cpu_vdso != cpu_vsys)
+ printf(" cpu mismatch (vdso:%u vsyscall:%u)!\n", cpu_vdso, cpu_vsys);
+ else if (node_vdso != node_vsys)
+ printf(" node mismatch (vdso:%u vsyscall:%u)!\n", node_vdso, node_vsys);
+ else
+ printf(" ok! cpu=%u node=%u\n", cpu_vdso, node_vdso);
+ }
+
+ return 0;
+}
+
+int bench(int argc, char **argv)
+{
+ struct timeval tv;
+ struct timezone tz;
+ benchmark(" syscall gettimeofday", [&]{sys_gtod(&tv, &tz);});
+ benchmark(" vdso gettimeofday", [&]{vdso_gtod(&tv, &tz);});
+ benchmark("vsyscall gettimeofday", [&]{vgtod(&tv, &tz);});
+
+ printf("\n");
+ time_t t;
+ benchmark(" syscall time ", [&]{sys_time(&t);});
+ if (vdso_time)
+ benchmark(" vdso time ", [&]{vdso_time(&t);});
+ benchmark("vsyscall time ", [&]{vtime(&t);});
+
+ printf("\n");
+ unsigned cpu, node;
+ benchmark(" vdso getcpu ", [&]{vdso_getcpu(&cpu, &node, 0);});
+ benchmark("vsyscall getcpu ", [&]{vgetcpu(&cpu, &node, 0);});
+
+ printf("\n");
+ benchmark("dummy syscall ", [&]{syscall(0xffffffff);});
+
+ return 0;
+}
+
+int call(int argc, char **argv)
+{
+ if (argc != 5) {
+ printf("Usage: call <addr> <rax> <arg1> <arg2> <arg3>\n");
+ return 1;
+ }
+
+ unsigned long addr, rax, arg1, arg2, arg3;
+ char *end;
+ addr = strtoull(argv[0], &end, 0);
+ if (*end)
+ goto bad;
+
+ rax = strtoull(argv[1], &end, 0);
+ if (*end)
+ goto bad;
+
+ arg1 = strtoull(argv[2], &end, 0);
+ if (*end)
+ goto bad;
+
+ arg2 = strtoull(argv[3], &end, 0);
+ if (*end)
+ goto bad;
+
+ arg3 = strtoull(argv[4], &end, 0);
+ if (*end)
+ goto bad;
+
+ unsigned long ret;
+ asm volatile("call *%[addr]" : "=a" (ret) : [addr] "rm" (addr), "a" (rax),
+ "D" (arg1), "S" (arg2), "d" (arg3));
+ printf("Return value = %ld\n", ret);
+
+ return 0;
+
+ bad:
+ printf("Bad arg\n");
+ return 1;
+}
+
+int intcc(int argc, char **argv)
+{
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct sigaction sa_segv;
+ memset(&sa_segv, 0, sizeof(sa_segv));
+ sa_segv.sa_sigaction = segv;
+ sa_segv.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa_segv.sa_mask);
+ if (sigaction(SIGSEGV, &sa_segv, 0))
+ perror("sigaction");
+
+ init_vdso();
+ if (argc < 2) {
+ printf("Usage: test_vsyscall <command> ...\n"
+ "command := { test, bench, intcc, call }\n");
+ return 1;
+ }
+
+ if (!strcmp(argv[1], "test"))
+ return test(argc - 2, argv + 2);
+ if (!strcmp(argv[1], "bench"))
+ return bench(argc - 2, argv + 2);
+ if (!strcmp(argv[1], "intcc"))
+ return intcc(argc - 2, argv + 2);
+ if (!strcmp(argv[1], "call"))
+ return call(argc - 2, argv + 2);
+
+ printf("Unknown command\n");
+ return 1;
+}