1#include <stdlib.h> 2#include <dlfcn.h> 3#include <stdio.h> 4#include <sys/param.h> 5#include <sys/time.h> 6#include <pthread.h> 7#include <assert.h> 8#include <mach-o/dyld.h> 9#include <string.h> 10#include <libgen.h> 11#include <unistd.h> 12#include "fail.h" 13 14typedef struct parsed_args_struct { 15 char* my_name; 16 char* test_name; 17 int num_threads; 18 long long length; 19 int test_argc; 20 void** test_argv; 21} parsed_args_t; 22 23typedef struct test_struct { 24 int (*setup)(int, long long, int, void**); 25 int (*execute)(int, int, long long, int, void**); 26 int (*cleanup)(int, long long); 27 char** error_str_ptr; 28} test_t; 29 30parsed_args_t args; 31test_t test; 32int ready_thread_count; 33pthread_mutex_t ready_thread_count_lock; 34pthread_cond_t start_cvar; 35pthread_cond_t threads_ready_cvar; 36 37int parse_args(int argc, char** argv, parsed_args_t* parsed_args) { 38 if(argc != 4) { 39 return -1; 40 } 41 42 parsed_args->my_name = argv[0]; 43 parsed_args->test_name = argv[1]; 44 parsed_args->num_threads = atoi(argv[2]); 45 parsed_args->length = strtoll(argv[3], NULL, 10); 46 parsed_args->test_argc = 0; 47 parsed_args->test_argv = NULL; 48 return 0; 49} 50 51void print_usage(char** argv) { 52 printf("Usage: %s test_name threads length\n", argv[0]); 53} 54 55int find_test(char* test_name, char* test_path) { 56 char binpath[MAXPATHLEN]; 57 char* dirpath; 58 uint32_t size = sizeof(binpath); 59 int retval; 60 61 retval = _NSGetExecutablePath(binpath, &size); 62 assert(retval == 0); 63 dirpath = dirname(binpath); 64 65 snprintf(test_path, MAXPATHLEN, "%s/perfindex-%s.dylib", dirpath, test_name); 66 if(access(test_path, F_OK) == 0) 67 return 0; 68 else 69 return -1; 70} 71 72int load_test(char* path, test_t* test) { 73 void* handle; 74 void* p; 75 76 handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); 77 if(!handle) { 78 return -1; 79 } 80 81 82 p = dlsym(handle, "setup"); 83 test->setup = (int (*)(int, long long, int, void **))p; 84 85 p = dlsym(handle, "execute"); 86 test->execute = (int (*)(int, int, long long, int, void **))p; 87 if(p == NULL) 88 return -1; 89 90 p = dlsym(handle, "cleanup"); 91 test->cleanup = (int (*)(int, long long))p; 92 93 p = dlsym(handle, "error_str"); 94 test->error_str_ptr = (char**)p; 95 96 return 0; 97} 98 99void start_timer(struct timeval *tp) { 100 gettimeofday(tp, NULL); 101} 102 103void end_timer(struct timeval *tp) { 104 struct timeval tend; 105 gettimeofday(&tend, NULL); 106 if(tend.tv_usec >= tp->tv_usec) { 107 tp->tv_sec = tend.tv_sec - tp->tv_sec; 108 tp->tv_usec = tend.tv_usec - tp->tv_usec; 109 } 110 else { 111 tp->tv_sec = tend.tv_sec - tp->tv_sec - 1; 112 tp->tv_usec = tend.tv_usec - tp->tv_usec + 1000000; 113 } 114} 115 116void print_timer(struct timeval *tp) { 117 printf("%ld.%06d\n", tp->tv_sec, tp->tv_usec); 118} 119 120static void* thread_setup(void *arg) { 121 int my_index = (int)arg; 122 long long work_size = args.length / args.num_threads; 123 int work_remainder = args.length % args.num_threads; 124 125 if(work_remainder > my_index) { 126 work_size++; 127 } 128 129 pthread_mutex_lock(&ready_thread_count_lock); 130 ready_thread_count++; 131 if(ready_thread_count == args.num_threads) 132 pthread_cond_signal(&threads_ready_cvar); 133 pthread_cond_wait(&start_cvar, &ready_thread_count_lock); 134 pthread_mutex_unlock(&ready_thread_count_lock); 135 test.execute(my_index, args.num_threads, work_size, args.test_argc, args.test_argv); 136 return NULL; 137} 138 139int main(int argc, char** argv) { 140 int retval; 141 int thread_index; 142 struct timeval timer; 143 pthread_t* threads; 144 int thread_retval; 145 void* thread_retval_ptr = &thread_retval; 146 char test_path[MAXPATHLEN]; 147 148 retval = parse_args(argc, argv, &args); 149 if(retval) { 150 print_usage(argv); 151 return -1; 152 } 153 154 retval = find_test(args.test_name, test_path); 155 if(retval) { 156 printf("Unable to find test %s\n", args.test_name); 157 return -1; 158 } 159 160 load_test(test_path, &test); 161 if(retval) { 162 printf("Unable to load test %s\n", args.test_name); 163 return -1; 164 } 165 166 pthread_cond_init(&threads_ready_cvar, NULL); 167 pthread_cond_init(&start_cvar, NULL); 168 pthread_mutex_init(&ready_thread_count_lock, NULL); 169 ready_thread_count = 0; 170 171 if(test.setup) { 172 retval = test.setup(args.num_threads, args.length, 0, NULL); 173 if(retval == PERFINDEX_FAILURE) { 174 fprintf(stderr, "Test setup failed: %s\n", *test.error_str_ptr); 175 return -1; 176 } 177 } 178 179 threads = (pthread_t*)malloc(sizeof(pthread_t)*args.num_threads); 180 for(thread_index = 0; thread_index < args.num_threads; thread_index++) { 181 retval = pthread_create(&threads[thread_index], NULL, thread_setup, (void*)(long)thread_index); 182 assert(retval == 0); 183 } 184 185 pthread_mutex_lock(&ready_thread_count_lock); 186 if(ready_thread_count != args.num_threads) { 187 pthread_cond_wait(&threads_ready_cvar, &ready_thread_count_lock); 188 } 189 pthread_mutex_unlock(&ready_thread_count_lock); 190 191 start_timer(&timer); 192 pthread_cond_broadcast(&start_cvar); 193 for(thread_index = 0; thread_index < args.num_threads; thread_index++) { 194 pthread_join(threads[thread_index], &thread_retval_ptr); 195 if(**test.error_str_ptr) { 196 printf("Test failed: %s\n", *test.error_str_ptr); 197 } 198 } 199 end_timer(&timer); 200 201 if(test.cleanup) 202 retval = test.cleanup(args.num_threads, args.length); 203 if(retval == PERFINDEX_FAILURE) { 204 fprintf(stderr, "Test cleanup failed: %s\n", *test.error_str_ptr); 205 free(threads); 206 return -1; 207 } 208 209 print_timer(&timer); 210 211 free(threads); 212 213 return 0; 214} 215