xref: /xnu-10002.1.13/tools/tests/perf_index/perf_index.c (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
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 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
print_usage(char ** argv)54 print_usage(char** argv)
55 {
56 	printf("Usage: %s test_name threads length\n", argv[0]);
57 }
58 
59 int
find_test(char * test_name,char * test_path)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
load_test(char * path,test_t * test)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
start_timer(struct timeval * tp)110 start_timer(struct timeval *tp)
111 {
112 	gettimeofday(tp, NULL);
113 }
114 
115 void
end_timer(struct timeval * tp)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
print_timer(struct timeval * tp)130 print_timer(struct timeval *tp)
131 {
132 	printf("%ld.%06d\n", tp->tv_sec, tp->tv_usec);
133 }
134 
135 static void*
thread_setup(void * arg)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
main(int argc,char ** argv)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 }
236