]>
Commit | Line | Data |
---|---|---|
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 | ||
14 | typedef 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 | ||
23 | typedef 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 | ||
30 | parsed_args_t args; | |
31 | test_t test; | |
32 | int ready_thread_count; | |
33 | pthread_mutex_t ready_thread_count_lock; | |
34 | pthread_cond_t start_cvar; | |
35 | pthread_cond_t threads_ready_cvar; | |
36 | ||
37 | int 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 | ||
51 | void print_usage(char** argv) { | |
52 | printf("Usage: %s test_name threads length\n", argv[0]); | |
53 | } | |
54 | ||
55 | int 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 | ||
72 | int 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 | ||
99 | void start_timer(struct timeval *tp) { | |
100 | gettimeofday(tp, NULL); | |
101 | } | |
102 | ||
103 | void 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 | ||
116 | void print_timer(struct timeval *tp) { | |
117 | printf("%ld.%06d\n", tp->tv_sec, tp->tv_usec); | |
118 | } | |
119 | ||
120 | static 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 | ||
139 | int 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 | } |