]>
Commit | Line | Data |
---|---|---|
fe8ab488 A |
1 | // |
2 | // PITest.m | |
3 | // PerfIndex | |
4 | // | |
5 | // Created by Mark Hamilton on 8/21/13. | |
6 | // | |
7 | // | |
8 | ||
9 | #import "PITest.h" | |
10 | #include <dlfcn.h> | |
11 | #include <pthread.h> | |
12 | ||
13 | @implementation PITest | |
14 | ||
15 | + (id)testWithOptions:(NSDictionary *)options | |
16 | { | |
17 | PITest *instance = nil; | |
18 | if(instance == nil) | |
19 | instance = [[PITest alloc] init]; | |
20 | [instance setTestName:[options objectForKey:@"name"]]; | |
21 | return instance; | |
22 | } | |
23 | ||
24 | - (BOOL)loadPITestAtPath:(NSString*) path | |
25 | { | |
26 | void* handle; | |
27 | void* f; | |
28 | ||
29 | handle = dlopen([path UTF8String], RTLD_NOW | RTLD_LOCAL); | |
30 | if(!handle) { | |
31 | return NO; | |
32 | } | |
33 | ||
34 | ||
35 | f = dlsym(handle, "setup"); | |
36 | self->setup_func = (int (*)(int, long long, int, void **))f; | |
37 | ||
38 | f = dlsym(handle, "execute"); | |
39 | self->execute_func = (int (*)(int, int, long long, int, void **))f; | |
40 | if(!self->execute_func) | |
41 | return NO; | |
42 | ||
43 | f = dlsym(handle, "cleanup"); | |
44 | self->cleanup_func = (void (*)(int, long long))f; | |
45 | return YES; | |
46 | } | |
47 | ||
48 | - (long long)lengthForTest:(NSString*) testName | |
49 | { | |
50 | NSNumber* number; | |
51 | long long myLength; | |
52 | NSDictionary* lengths = [NSDictionary dictionaryWithObjectsAndKeys: | |
53 | @"cpu", [NSNumber numberWithLongLong:2000], | |
54 | @"syscall", [NSNumber numberWithLongLong:2500], | |
55 | @"memory", [NSNumber numberWithLongLong:1000000], | |
56 | @"fault", [NSNumber numberWithLongLong:500], | |
57 | @"zfod", [NSNumber numberWithLongLong:500], | |
58 | @"file_create", [NSNumber numberWithLongLong:10], | |
59 | @"file_read", [NSNumber numberWithLongLong:1000000], | |
60 | @"file_write", [NSNumber numberWithLongLong:1000000], | |
61 | nil]; | |
62 | ||
63 | number = (NSNumber*)[lengths objectForKey:testName]; | |
64 | if(!number) { | |
65 | myLength = 10; | |
66 | } else { | |
67 | myLength = [number longLongValue]; | |
68 | } | |
69 | ||
70 | return myLength; | |
71 | } | |
72 | ||
73 | - (BOOL)setup | |
74 | { | |
75 | BOOL success = NO; | |
76 | int retval; | |
77 | ||
78 | NSString* testPath = [NSString stringWithFormat:@"/AppleInternal/CoreOS/perf_index/%@.dylib", [self testName]]; | |
79 | success = [self loadPITestAtPath:testPath]; | |
80 | if(!success) { | |
81 | NSLog(@"Failed to load test %@", [self testName]); | |
82 | return NO; | |
83 | } | |
84 | ||
85 | self->length = [self lengthForTest:[self testName]]; | |
86 | self->numThreads = 1; | |
87 | self->testArgc = 0; | |
88 | self->testArgv = NULL; | |
89 | ||
90 | pthread_cond_init(&self->threadsReadyCvar, NULL); | |
91 | pthread_cond_init(&self->startCvar, NULL); | |
92 | pthread_mutex_init(&self->readyThreadCountLock, NULL); | |
93 | self->readyThreadCount = 0; | |
94 | ||
95 | if(self->setup_func) { | |
96 | retval = self->setup_func(1, self->length, 0, NULL); | |
97 | if(retval != 0) { | |
98 | NSLog(@"setup_func failed"); | |
99 | return NO; | |
100 | } | |
101 | } | |
102 | ||
103 | self->threads = (pthread_t*)malloc(sizeof(pthread_t)*self->numThreads); | |
104 | ||
105 | for(int thread_index = 0; thread_index < self->numThreads; thread_index++) { | |
106 | NSNumber* my_thread_index = [NSNumber numberWithInt:thread_index]; | |
107 | NSArray *arg = [NSArray arrayWithObjects:my_thread_index, self, nil]; | |
108 | retval = pthread_create(&threads[thread_index], NULL, thread_setup, (__bridge void*)arg); | |
109 | if(retval != 0) { | |
110 | NSLog(@"pthread_create failed"); | |
111 | free(self->threads); | |
112 | return NO; | |
113 | } | |
114 | } | |
115 | ||
116 | pthread_mutex_lock(&self->readyThreadCountLock); | |
117 | if(self->readyThreadCount != self->numThreads) { | |
118 | pthread_cond_wait(&self->threadsReadyCvar, &self->readyThreadCountLock); | |
119 | } | |
120 | pthread_mutex_unlock(&self->readyThreadCountLock); | |
121 | return YES; | |
122 | } | |
123 | ||
124 | - (BOOL)execute | |
125 | { | |
126 | pthread_cond_broadcast(&self->startCvar); | |
127 | for(int thread_index = 0; thread_index < self->numThreads; thread_index++) { | |
128 | pthread_join(self->threads[thread_index], NULL); | |
129 | } | |
130 | return YES; | |
131 | } | |
132 | ||
133 | - (void)cleanup | |
134 | { | |
135 | free(self->threads); | |
136 | if(self->cleanup_func) | |
137 | self->cleanup_func(0, self->length); | |
138 | } | |
139 | ||
140 | void* thread_setup(void* arg) | |
141 | { | |
142 | int my_index = (int)[(NSNumber*)[(__bridge NSArray*)arg objectAtIndex:0] integerValue]; | |
143 | PITest* test = (PITest*)[(__bridge NSArray*)arg objectAtIndex:1]; | |
144 | ||
145 | long long work_size = test->length / test->numThreads; | |
146 | int work_remainder = test->length % test->numThreads; | |
147 | ||
148 | if(work_remainder > my_index) { | |
149 | work_size++; | |
150 | } | |
151 | ||
152 | pthread_mutex_lock(&test->readyThreadCountLock); | |
153 | test->readyThreadCount++; | |
154 | ||
155 | if(test->readyThreadCount == test->numThreads) | |
156 | pthread_cond_signal(&test->threadsReadyCvar); | |
157 | pthread_cond_wait(&test->startCvar, &test->readyThreadCountLock); | |
158 | pthread_mutex_unlock(&test->readyThreadCountLock); | |
159 | test->execute_func(my_index, test->numThreads, work_size, test->testArgc, test->testArgv); | |
160 | ||
161 | return NULL; | |
162 | } | |
163 | ||
164 | @end |