]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/tests/perf_index/perf_index.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / tools / tests / perf_index / perf_index.c
diff --git a/tools/tests/perf_index/perf_index.c b/tools/tests/perf_index/perf_index.c
new file mode 100644 (file)
index 0000000..1953288
--- /dev/null
@@ -0,0 +1,214 @@
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <pthread.h>
+#include <assert.h>
+#include <mach-o/dyld.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include "fail.h"
+
+typedef struct parsed_args_struct {
+    char* my_name;
+    char* test_name;
+    int num_threads;
+    long long length;
+    int test_argc;
+    void** test_argv;
+} parsed_args_t;
+
+typedef struct test_struct {
+    int (*setup)(int, long long, int, void**);
+    int (*execute)(int, int, long long, int, void**);
+    int (*cleanup)(int, long long);
+    char** error_str_ptr;
+} test_t;
+
+parsed_args_t args;
+test_t test;
+int ready_thread_count;
+pthread_mutex_t ready_thread_count_lock;
+pthread_cond_t start_cvar;
+pthread_cond_t threads_ready_cvar;
+
+int parse_args(int argc, char** argv, parsed_args_t* parsed_args) {
+    if(argc != 4) {
+        return -1;
+    }
+
+    parsed_args->my_name = argv[0];
+    parsed_args->test_name = argv[1];
+    parsed_args->num_threads = atoi(argv[2]);
+    parsed_args->length = strtoll(argv[3], NULL, 10);
+    parsed_args->test_argc = 0;
+    parsed_args->test_argv = NULL;
+    return 0;
+}
+
+void print_usage(char** argv) {
+    printf("Usage: %s test_name threads length\n", argv[0]);
+}
+
+int find_test(char* test_name, char* test_path) {
+    char binpath[MAXPATHLEN];
+    char* dirpath;
+    uint32_t size = sizeof(binpath);
+    int retval;
+
+    retval = _NSGetExecutablePath(binpath, &size);
+    assert(retval == 0);
+    dirpath = dirname(binpath);
+
+    snprintf(test_path, MAXPATHLEN, "%s/perfindex-%s.dylib", dirpath, test_name);
+    if(access(test_path, F_OK) == 0)
+        return 0;
+    else
+        return -1;
+}
+
+int load_test(char* path, test_t* test) {
+    void* handle;
+    void* p;
+
+    handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+    if(!handle) {
+        return -1;
+    }
+
+
+    p = dlsym(handle, "setup");
+    test->setup = (int (*)(int, long long, int, void **))p;
+
+    p = dlsym(handle, "execute");
+    test->execute = (int (*)(int, int, long long, int, void **))p;
+    if(p == NULL)
+        return -1;
+
+    p = dlsym(handle, "cleanup");
+    test->cleanup = (int (*)(int, long long))p;
+
+    p = dlsym(handle, "error_str");
+    test->error_str_ptr = (char**)p;
+
+    return 0;
+}
+
+void start_timer(struct timeval *tp) {
+  gettimeofday(tp, NULL);
+}
+
+void end_timer(struct timeval *tp) {
+  struct timeval tend;
+  gettimeofday(&tend, NULL);
+  if(tend.tv_usec >= tp->tv_usec) {
+    tp->tv_sec = tend.tv_sec - tp->tv_sec;
+    tp->tv_usec = tend.tv_usec - tp->tv_usec;
+  }
+  else {
+    tp->tv_sec = tend.tv_sec - tp->tv_sec - 1;
+    tp->tv_usec = tend.tv_usec - tp->tv_usec + 1000000;
+  }
+}
+
+void print_timer(struct timeval *tp) {
+  printf("%ld.%06d\n", tp->tv_sec, tp->tv_usec);
+}
+
+static void* thread_setup(void *arg) {
+  int my_index = (int)arg;
+  long long work_size = args.length / args.num_threads;
+  int work_remainder = args.length % args.num_threads;
+
+  if(work_remainder > my_index) {
+    work_size++;
+  }
+
+  pthread_mutex_lock(&ready_thread_count_lock);
+  ready_thread_count++;
+  if(ready_thread_count == args.num_threads)
+    pthread_cond_signal(&threads_ready_cvar);
+  pthread_cond_wait(&start_cvar, &ready_thread_count_lock);
+  pthread_mutex_unlock(&ready_thread_count_lock);
+  test.execute(my_index, args.num_threads, work_size, args.test_argc, args.test_argv);
+  return NULL;
+}
+
+int main(int argc, char** argv) {
+    int retval;
+    int thread_index;
+    struct timeval timer;
+    pthread_t* threads;
+    int thread_retval;
+    void* thread_retval_ptr = &thread_retval;
+    char test_path[MAXPATHLEN];
+
+    retval = parse_args(argc, argv, &args);
+    if(retval) {
+        print_usage(argv);
+        return -1;
+    }
+
+    retval = find_test(args.test_name, test_path);
+    if(retval) {
+        printf("Unable to find test %s\n", args.test_name);
+        return -1;
+    }
+
+    load_test(test_path, &test);
+    if(retval) {
+        printf("Unable to load test %s\n", args.test_name);
+        return -1;
+    }
+
+    pthread_cond_init(&threads_ready_cvar, NULL);
+    pthread_cond_init(&start_cvar, NULL);
+    pthread_mutex_init(&ready_thread_count_lock, NULL);
+    ready_thread_count = 0;
+
+    if(test.setup) {
+        retval = test.setup(args.num_threads, args.length, 0, NULL);
+        if(retval == PERFINDEX_FAILURE) {
+            fprintf(stderr, "Test setup failed: %s\n", *test.error_str_ptr);
+            return -1;
+        }
+    }
+
+    threads = (pthread_t*)malloc(sizeof(pthread_t)*args.num_threads);
+    for(thread_index = 0; thread_index < args.num_threads; thread_index++) {
+        retval = pthread_create(&threads[thread_index], NULL, thread_setup, (void*)(long)thread_index);
+        assert(retval == 0);
+    }
+
+    pthread_mutex_lock(&ready_thread_count_lock);
+    if(ready_thread_count != args.num_threads) {
+        pthread_cond_wait(&threads_ready_cvar, &ready_thread_count_lock);
+    }
+    pthread_mutex_unlock(&ready_thread_count_lock);
+
+    start_timer(&timer);
+    pthread_cond_broadcast(&start_cvar);
+    for(thread_index = 0; thread_index < args.num_threads; thread_index++) {
+        pthread_join(threads[thread_index], &thread_retval_ptr);
+        if(**test.error_str_ptr) {
+            printf("Test failed: %s\n", *test.error_str_ptr);
+        }
+    }
+    end_timer(&timer);
+
+    if(test.cleanup)
+        retval = test.cleanup(args.num_threads, args.length);
+        if(retval == PERFINDEX_FAILURE) {
+            fprintf(stderr, "Test cleanup failed: %s\n", *test.error_str_ptr);
+            free(threads);
+            return -1;
+        }
+
+    print_timer(&timer);
+
+    free(threads);
+
+    return 0;
+}