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