#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; }