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
140void* 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
165