forked from codykrieger/gfxCardStatus
-
Notifications
You must be signed in to change notification settings - Fork 20
/
GSProcess.m
182 lines (145 loc) · 5.49 KB
/
GSProcess.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//
// GSProcess.m
// gfxCardStatus
//
// Created by Cody Krieger on 6/21/11.
// Copyright 2011 Cody Krieger. All rights reserved.
//
// Original task list functionality Copyright 2010 Thierry Coppey.
// (look back in repo history at proc.h/m)
//
#import "GSProcess.h"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <sys/sysctl.h>
#include <unistd.h>
static const CFStringRef kProcTaskKey = (const CFStringRef)__builtin___CFStringMakeConstantString("task-list");
static struct kinfo_proc *_procInfo = NULL;
static size_t _procSize = 0;
static size_t _procNum = 0;
#pragma mark - Static C methods
static void _procTask(const void *value, void *param) {
NSMutableArray *arr = (__bridge NSMutableArray *)param;
NSNumber *key = NULL;
NSString *procName = NULL;
int mib[3] = { CTL_KERN, KERN_ARGMAX, 0 };
struct kinfo_proc *k = NULL;
size_t i, sz;
long long pid;
char *buf, *sp, *cp;
// return if we don't have any processes, or our current process pid is rubbish
if (_procInfo == NULL)
return;
if (!CFNumberGetValue(value, kCFNumberLongLongType, &pid))
return;
// loop through the kernel process list, find the one that matches our current
// pid (must conform to AppleGraphicsControl), then break
for (i = 0; i < _procNum; i++) {
if (_procInfo[i].kp_proc.p_pid == pid) {
k = &_procInfo[i];
break;
}
}
// return if we haven't found a matching service in the kernel task list
if (k == NULL) return;
key = [[NSNumber alloc] initWithLongLong:pid];
sz = sizeof(i);
if (sysctl(mib, 2, &i, &sz, NULL, 0) == -1)
goto err;
// create a buffer for reading in the process name
buf = (char *)malloc(i);
if (buf == NULL)
goto err;
mib[1] = KERN_PROCARGS2;
mib[2] = k->kp_proc.p_pid;
sz = (size_t)i;
if (sysctl(mib, 3, buf, &sz, NULL, 0) == -1) {
free(buf);
goto err;
}
// buffer buffer buffer
cp = buf + sizeof(int);
if ((sp = strrchr(cp, '/'))) cp = sp + 1;
// we finally have the proc name!
procName = [[NSString alloc] initWithUTF8String:cp];
[arr addObject:[NSDictionary dictionaryWithObjectsAndKeys:
procName, kTaskItemName,
[key stringValue], kTaskItemPID, nil]];
free(buf);
goto done;
err:
[arr addObject:[NSDictionary dictionaryWithObjectsAndKeys:
[[NSString alloc] initWithUTF8String:k->kp_proc.p_comm], kTaskItemName,
@"", kTaskItemPID, nil]];
done:
return;
}
// update the current list of kernel tasks
static void _procUpdate() {
// we want all process entries from the kernel
int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
struct kinfo_proc *info = NULL;
size_t sz;
// return if unsuccessful
if (sysctl(mib, 3, NULL, &sz, NULL, 0) < 0)
return;
// if procInfo has stale data, reallocate it in preparation for new task list
if (_procInfo == NULL || sz != _procSize) {
info = realloc(_procInfo, sz);
if (info == NULL) return;
_procInfo = info; _procSize = sz;
}
// read tasks into procInfo and return if unsuccessful
if (sysctl(mib, 3, _procInfo, &sz, NULL, 0) < 0)
return;
// update number of processes in the list
_procNum = sz / sizeof(struct kinfo_proc);
}
static void _procScan(io_registry_entry_t service, NSMutableArray *arr) {
io_registry_entry_t child = 0;
io_iterator_t children = 0;
CFMutableDictionaryRef props = NULL;
// get all tasks that conform to AppleGraphicsControl service
if (IOObjectConformsTo(service, "AppleGraphicsControl")) {
kern_return_t status = IORegistryEntryCreateCFProperties(service, &props, kCFAllocatorDefault, kNilOptions);
if (status == KERN_SUCCESS && CFGetTypeID(props) == CFDictionaryGetTypeID()) {
CFTypeRef array = CFDictionaryGetValue(props, kProcTaskKey);
CFRange range = { 0, CFArrayGetCount(array) };
CFArrayApplyFunction(array, range, _procTask, (__bridge void *)arr);
CFRelease(props);
}
}
if (IORegistryEntryGetChildIterator(service, kIOPowerPlane, &children) == KERN_SUCCESS) { // kIOServicePlane
while ((child = IOIteratorNext(children))) {
_procScan(child, arr);
IOObjectRelease(child);
}
IOObjectRelease(children);
}
}
@implementation GSProcess
#pragma mark - GSProcess API
+ (NSArray *)getTaskList {
NSMutableArray *list = [NSMutableArray array];
// find out if an external monitor is forcing the discrete gpu on
CGDirectDisplayID displays[8];
CGDisplayCount displayCount = 0;
if (CGGetOnlineDisplayList(8, displays, &displayCount) == noErr) {
for (int i = 0; i < displayCount; i++) {
if ( ! CGDisplayIsBuiltin(displays[i]))
[list addObject:[NSDictionary dictionaryWithObjectsAndKeys:
Str(@"External Display"), kTaskItemName,
@"", kTaskItemPID, nil]];
}
}
// scan the kernel process list for discrete gpu-using tasks
io_registry_entry_t service = 0;
service = IORegistryGetRootEntry(kIOMasterPortDefault);
if (!service)
return [NSArray array];
_procUpdate();
_procScan(service, list);
IOObjectRelease(service);
return list;
}
@end