1 | /* |
2 | * Copyright (c) 2010 Apple Computer, Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | |
29 | #include <sys/sysctl.h> |
30 | #include <kern/backtrace.h> |
31 | #include <kern/host.h> |
32 | #include <kern/zalloc.h> |
33 | |
34 | #include <IOKit/system.h> |
35 | #include <libkern/c++/OSKext.h> |
36 | #include <libkern/OSAtomic.h> |
37 | |
38 | #include <IOKit/IOStatisticsPrivate.h> |
39 | #include <IOKit/IOUserClient.h> |
40 | #include <IOKit/IOEventSource.h> |
41 | #include <IOKit/IOKitDebug.h> |
42 | |
43 | #if IOKITSTATS |
44 | bool IOStatistics::enabled = false; |
45 | |
46 | uint32_t IOStatistics::sequenceID = 0; |
47 | |
48 | uint32_t IOStatistics::lastClassIndex = 0; |
49 | uint32_t IOStatistics::lastKextIndex = 0; |
50 | |
51 | uint32_t IOStatistics::loadedKexts = 0; |
52 | uint32_t IOStatistics::registeredClasses = 0; |
53 | uint32_t IOStatistics::registeredCounters = 0; |
54 | uint32_t IOStatistics::registeredWorkloops = 0; |
55 | |
56 | uint32_t IOStatistics::attachedEventSources = 0; |
57 | |
58 | IOWorkLoopDependency *IOStatistics::nextWorkLoopDependency = NULL; |
59 | |
60 | /* Logging */ |
61 | |
62 | #define LOG_LEVEL 0 |
63 | |
64 | #define LOG(level, format, ...) \ |
65 | do { \ |
66 | if (level <= LOG_LEVEL) \ |
67 | printf(format, ##__VA_ARGS__); \ |
68 | } while (0) |
69 | |
70 | /* Locks */ |
71 | |
72 | IORWLock *IOStatistics::lock = NULL; |
73 | |
74 | /* Kext tree */ |
75 | |
76 | KextNode *IOStatistics::kextHint = NULL; |
77 | |
78 | IOStatistics::KextTreeHead IOStatistics::kextHead = RB_INITIALIZER(&IOStatistics::kextHead); |
79 | |
80 | int IOStatistics::kextNodeCompare(KextNode *e1, KextNode *e2) |
81 | { |
82 | if (e1->kext < e2->kext) |
83 | return -1; |
84 | else if (e1->kext > e2->kext) |
85 | return 1; |
86 | else |
87 | return 0; |
88 | } |
89 | |
90 | RB_GENERATE(IOStatistics::KextTree, KextNode, link, kextNodeCompare); |
91 | |
92 | /* Kext tree ordered by address */ |
93 | |
94 | IOStatistics::KextAddressTreeHead IOStatistics::kextAddressHead = RB_INITIALIZER(&IOStatistics::kextAddressHead); |
95 | |
96 | int IOStatistics::kextAddressNodeCompare(KextNode *e1, KextNode *e2) |
97 | { |
98 | if (e1->address < e2->address) |
99 | return -1; |
100 | else if (e1->address > e2->address) |
101 | return 1; |
102 | else |
103 | return 0; |
104 | } |
105 | |
106 | RB_GENERATE(IOStatistics::KextAddressTree, KextNode, addressLink, kextAddressNodeCompare); |
107 | |
108 | /* Class tree */ |
109 | |
110 | IOStatistics::ClassTreeHead IOStatistics::classHead = RB_INITIALIZER(&IOStatistics::classHead); |
111 | |
112 | int IOStatistics::classNodeCompare(ClassNode *e1, ClassNode *e2) { |
113 | if (e1->metaClass < e2->metaClass) |
114 | return -1; |
115 | else if (e1->metaClass > e2->metaClass) |
116 | return 1; |
117 | else |
118 | return 0; |
119 | } |
120 | |
121 | RB_GENERATE(IOStatistics::ClassTree, ClassNode, tLink, classNodeCompare); |
122 | |
123 | /* Workloop dependencies */ |
124 | |
125 | int IOWorkLoopCounter::loadTagCompare(IOWorkLoopDependency *e1, IOWorkLoopDependency *e2) { |
126 | if (e1->loadTag < e2->loadTag) |
127 | return -1; |
128 | else if (e1->loadTag > e2->loadTag) |
129 | return 1; |
130 | else |
131 | return 0; |
132 | } |
133 | |
134 | RB_GENERATE(IOWorkLoopCounter::DependencyTree, IOWorkLoopDependency, link, IOWorkLoopCounter::loadTagCompare); |
135 | |
136 | /* sysctl stuff */ |
137 | |
138 | static int |
139 | oid_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, int arg2, struct sysctl_req *req) |
140 | { |
141 | int error = EINVAL; |
142 | uint32_t request = arg2; |
143 | |
144 | switch (request) |
145 | { |
146 | case kIOStatisticsGeneral: |
147 | error = IOStatistics::getStatistics(req); |
148 | break; |
149 | case kIOStatisticsWorkLoop: |
150 | error = IOStatistics::getWorkLoopStatistics(req); |
151 | break; |
152 | case kIOStatisticsUserClient: |
153 | error = IOStatistics::getUserClientStatistics(req); |
154 | break; |
155 | default: |
156 | break; |
157 | } |
158 | |
159 | return error; |
160 | } |
161 | |
162 | SYSCTL_NODE(_debug, OID_AUTO, iokit_statistics, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "IOStatistics" ); |
163 | |
164 | static SYSCTL_PROC(_debug_iokit_statistics, OID_AUTO, general, |
165 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, |
166 | 0, kIOStatisticsGeneral, oid_sysctl, "S" , "" ); |
167 | |
168 | static SYSCTL_PROC(_debug_iokit_statistics, OID_AUTO, workloop, |
169 | CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, |
170 | 0, kIOStatisticsWorkLoop, oid_sysctl, "S" , "" ); |
171 | |
172 | static SYSCTL_PROC(_debug_iokit_statistics, OID_AUTO, userclient, |
173 | CTLTYPE_STRUCT | CTLFLAG_RW | CTLFLAG_NOAUTO | CTLFLAG_KERN | CTLFLAG_LOCKED, |
174 | 0, kIOStatisticsUserClient, oid_sysctl, "S" , "" ); |
175 | |
176 | void IOStatistics::initialize() |
177 | { |
178 | if (enabled) { |
179 | return; |
180 | } |
181 | |
182 | /* Only enabled if the boot argument is set. */ |
183 | if (!(kIOStatistics & gIOKitDebug)) { |
184 | return; |
185 | } |
186 | |
187 | sysctl_register_oid(&sysctl__debug_iokit_statistics_general); |
188 | sysctl_register_oid(&sysctl__debug_iokit_statistics_workloop); |
189 | sysctl_register_oid(&sysctl__debug_iokit_statistics_userclient); |
190 | |
191 | lock = IORWLockAlloc(); |
192 | if (!lock) { |
193 | return; |
194 | } |
195 | |
196 | nextWorkLoopDependency = (IOWorkLoopDependency*)kalloc(sizeof(IOWorkLoopDependency)); |
197 | if (!nextWorkLoopDependency) { |
198 | return; |
199 | } |
200 | |
201 | enabled = true; |
202 | } |
203 | |
204 | void IOStatistics::onKextLoad(OSKext *kext, kmod_info_t *kmod_info) |
205 | { |
206 | KextNode *ke; |
207 | |
208 | assert(kext && kmod_info); |
209 | |
210 | if (!enabled) { |
211 | return; |
212 | } |
213 | |
214 | LOG(1, "IOStatistics::onKextLoad: %s, tag %d, address 0x%llx, address end 0x%llx\n" , |
215 | kext->getIdentifierCString(), kmod_info->id, (uint64_t)kmod_info->address, (uint64_t)(kmod_info->address + kmod_info->size)); |
216 | |
217 | ke = (KextNode *)kalloc(sizeof(KextNode)); |
218 | if (!ke) { |
219 | return; |
220 | } |
221 | |
222 | memset(ke, 0, sizeof(KextNode)); |
223 | |
224 | ke->kext = kext; |
225 | ke->loadTag = kmod_info->id; |
226 | ke->address = kmod_info->address; |
227 | ke->address_end = kmod_info->address + kmod_info->size; |
228 | |
229 | SLIST_INIT(&ke->classList); |
230 | TAILQ_INIT(&ke->userClientCallList); |
231 | |
232 | IORWLockWrite(lock); |
233 | |
234 | RB_INSERT(KextTree, &kextHead, ke); |
235 | RB_INSERT(KextAddressTree, &kextAddressHead, ke); |
236 | |
237 | sequenceID++; |
238 | loadedKexts++; |
239 | lastKextIndex++; |
240 | |
241 | IORWLockUnlock(lock); |
242 | } |
243 | |
244 | void IOStatistics::onKextUnload(OSKext *kext) |
245 | { |
246 | KextNode sought, *found; |
247 | |
248 | assert(kext); |
249 | |
250 | if (!enabled) { |
251 | return; |
252 | } |
253 | |
254 | LOG(1, "IOStatistics::onKextUnload: %s\n" , kext->getIdentifierCString()); |
255 | |
256 | IORWLockWrite(lock); |
257 | |
258 | sought.kext = kext; |
259 | found = RB_FIND(KextTree, &kextHead, &sought); |
260 | if (found) { |
261 | IOWorkLoopCounter *wlc; |
262 | IOUserClientProcessEntry *uce; |
263 | |
264 | /* Disconnect workloop counters; cleanup takes place in unregisterWorkLoop() */ |
265 | while ((wlc = SLIST_FIRST(&found->workLoopList))) { |
266 | SLIST_REMOVE_HEAD(&found->workLoopList, link); |
267 | wlc->parentKext = NULL; |
268 | } |
269 | |
270 | /* Free up the user client list */ |
271 | while ((uce = TAILQ_FIRST(&found->userClientCallList))) { |
272 | TAILQ_REMOVE(&found->userClientCallList, uce, link); |
273 | kfree(uce, sizeof(IOUserClientProcessEntry)); |
274 | } |
275 | |
276 | /* Remove from kext trees */ |
277 | RB_REMOVE(KextTree, &kextHead, found); |
278 | RB_REMOVE(KextAddressTree, &kextAddressHead, found); |
279 | |
280 | /* |
281 | * Clear a matching kextHint to avoid use after free in |
282 | * onClassAdded() for a class add after a KEXT unload. |
283 | */ |
284 | if (found == kextHint) { |
285 | kextHint = NULL; |
286 | } |
287 | |
288 | /* Finally, free the class node */ |
289 | kfree(found, sizeof(KextNode)); |
290 | |
291 | sequenceID++; |
292 | loadedKexts--; |
293 | } |
294 | else { |
295 | panic("IOStatistics::onKextUnload: cannot find kext: %s" , kext->getIdentifierCString()); |
296 | } |
297 | |
298 | IORWLockUnlock(lock); |
299 | } |
300 | |
301 | void IOStatistics::onClassAdded(OSKext *parentKext, OSMetaClass *metaClass) |
302 | { |
303 | ClassNode *ce; |
304 | KextNode soughtKext, *foundKext = NULL; |
305 | |
306 | assert(parentKext && metaClass); |
307 | |
308 | if (!enabled) { |
309 | return; |
310 | } |
311 | |
312 | LOG(1, "IOStatistics::onClassAdded: %s\n" , metaClass->getClassName()); |
313 | |
314 | ce = (ClassNode *)kalloc(sizeof(ClassNode)); |
315 | if (!ce) { |
316 | return; |
317 | } |
318 | |
319 | memset(ce, 0, sizeof(ClassNode)); |
320 | |
321 | IORWLockWrite(lock); |
322 | |
323 | /* Hinted? */ |
324 | if (kextHint && kextHint->kext == parentKext) { |
325 | foundKext = kextHint; |
326 | } |
327 | else { |
328 | soughtKext.kext = parentKext; |
329 | foundKext = RB_FIND(KextTree, &kextHead, &soughtKext); |
330 | } |
331 | |
332 | if (foundKext) { |
333 | ClassNode soughtClass, *foundClass = NULL; |
334 | const OSMetaClass *superClass; |
335 | |
336 | ce->metaClass = metaClass; |
337 | ce->classID = lastClassIndex++; |
338 | ce->parentKext = foundKext; |
339 | |
340 | /* Has superclass? */ |
341 | superClass = ce->metaClass->getSuperClass(); |
342 | if (superClass) { |
343 | soughtClass.metaClass = superClass; |
344 | foundClass = RB_FIND(ClassTree, &classHead, &soughtClass); |
345 | } |
346 | ce->superClassID = foundClass ? foundClass->classID : (uint32_t)(-1); |
347 | |
348 | SLIST_INIT(&ce->counterList); |
349 | SLIST_INIT(&ce->userClientList); |
350 | |
351 | RB_INSERT(ClassTree, &classHead, ce); |
352 | SLIST_INSERT_HEAD(&foundKext->classList, ce, lLink); |
353 | |
354 | foundKext->classes++; |
355 | |
356 | kextHint = foundKext; |
357 | |
358 | sequenceID++; |
359 | registeredClasses++; |
360 | } |
361 | else { |
362 | panic("IOStatistics::onClassAdded: cannot find parent kext: %s" , parentKext->getIdentifierCString()); |
363 | } |
364 | |
365 | IORWLockUnlock(lock); |
366 | } |
367 | |
368 | void IOStatistics::onClassRemoved(OSKext *parentKext, OSMetaClass *metaClass) |
369 | { |
370 | ClassNode sought, *found; |
371 | |
372 | assert(parentKext && metaClass); |
373 | |
374 | if (!enabled) { |
375 | return; |
376 | } |
377 | |
378 | LOG(1, "IOStatistics::onClassRemoved: %s\n" , metaClass->getClassName()); |
379 | |
380 | IORWLockWrite(lock); |
381 | |
382 | sought.metaClass = metaClass; |
383 | found = RB_FIND(ClassTree, &classHead, &sought); |
384 | if (found) { |
385 | IOEventSourceCounter *esc; |
386 | IOUserClientCounter *ucc; |
387 | |
388 | /* Free up the list of counters */ |
389 | while ((esc = SLIST_FIRST(&found->counterList))) { |
390 | SLIST_REMOVE_HEAD(&found->counterList, link); |
391 | kfree(esc, sizeof(IOEventSourceCounter)); |
392 | } |
393 | |
394 | /* Free up the user client list */ |
395 | while ((ucc = SLIST_FIRST(&found->userClientList))) { |
396 | SLIST_REMOVE_HEAD(&found->userClientList, link); |
397 | kfree(ucc, sizeof(IOUserClientCounter)); |
398 | } |
399 | |
400 | /* Remove from class tree */ |
401 | RB_REMOVE(ClassTree, &classHead, found); |
402 | |
403 | /* Remove from parent */ |
404 | SLIST_REMOVE(&found->parentKext->classList, found, ClassNode, lLink); |
405 | |
406 | /* Finally, free the class node */ |
407 | kfree(found, sizeof(ClassNode)); |
408 | |
409 | sequenceID++; |
410 | registeredClasses--; |
411 | } |
412 | else { |
413 | panic("IOStatistics::onClassRemoved: cannot find class: %s" , metaClass->getClassName()); |
414 | } |
415 | |
416 | IORWLockUnlock(lock); |
417 | } |
418 | |
419 | IOEventSourceCounter *IOStatistics::registerEventSource(OSObject *inOwner) |
420 | { |
421 | IOEventSourceCounter *counter = NULL; |
422 | ClassNode sought, *found = NULL; |
423 | boolean_t createDummyCounter = FALSE; |
424 | |
425 | assert(inOwner); |
426 | |
427 | if (!enabled) { |
428 | return NULL; |
429 | } |
430 | |
431 | counter = (IOEventSourceCounter*)kalloc(sizeof(IOEventSourceCounter)); |
432 | if (!counter) { |
433 | return NULL; |
434 | } |
435 | |
436 | memset(counter, 0, sizeof(IOEventSourceCounter)); |
437 | |
438 | IORWLockWrite(lock); |
439 | |
440 | /* Workaround for <rdar://problem/7158117> - create a dummy counter when inOwner is bad. |
441 | * We use retainCount here as our best indication that the pointer is awry. |
442 | */ |
443 | if (inOwner->retainCount > 0xFFFFFF) { |
444 | kprintf("IOStatistics::registerEventSource - bad metaclass %p\n" , inOwner); |
445 | createDummyCounter = TRUE; |
446 | } |
447 | else { |
448 | sought.metaClass = inOwner->getMetaClass(); |
449 | found = RB_FIND(ClassTree, &classHead, &sought); |
450 | } |
451 | |
452 | if (found) { |
453 | counter->parentClass = found; |
454 | SLIST_INSERT_HEAD(&found->counterList, counter, link); |
455 | registeredCounters++; |
456 | } |
457 | |
458 | if (!(createDummyCounter || found)) { |
459 | panic("IOStatistics::registerEventSource: cannot find parent class: %s" , inOwner->getMetaClass()->getClassName()); |
460 | } |
461 | |
462 | IORWLockUnlock(lock); |
463 | |
464 | return counter; |
465 | } |
466 | |
467 | void IOStatistics::unregisterEventSource(IOEventSourceCounter *counter) |
468 | { |
469 | if (!counter) { |
470 | return; |
471 | } |
472 | |
473 | IORWLockWrite(lock); |
474 | |
475 | if (counter->parentClass) { |
476 | SLIST_REMOVE(&counter->parentClass->counterList, counter, IOEventSourceCounter, link); |
477 | registeredCounters--; |
478 | } |
479 | kfree(counter, sizeof(IOEventSourceCounter)); |
480 | |
481 | IORWLockUnlock(lock); |
482 | } |
483 | |
484 | IOWorkLoopCounter* IOStatistics::registerWorkLoop(IOWorkLoop *workLoop) |
485 | { |
486 | IOWorkLoopCounter *counter = NULL; |
487 | KextNode *found; |
488 | |
489 | assert(workLoop); |
490 | |
491 | if (!enabled) { |
492 | return NULL; |
493 | } |
494 | |
495 | counter = (IOWorkLoopCounter*)kalloc(sizeof(IOWorkLoopCounter)); |
496 | if (!counter) { |
497 | return NULL; |
498 | } |
499 | |
500 | memset(counter, 0, sizeof(IOWorkLoopCounter)); |
501 | |
502 | found = getKextNodeFromBacktrace(TRUE); |
503 | if (!found) { |
504 | panic("IOStatistics::registerWorkLoop: cannot find parent kext" ); |
505 | } |
506 | |
507 | counter->parentKext = found; |
508 | counter->workLoop = workLoop; |
509 | RB_INIT(&counter->dependencyHead); |
510 | SLIST_INSERT_HEAD(&found->workLoopList, counter, link); |
511 | registeredWorkloops++; |
512 | |
513 | releaseKextNode(found); |
514 | |
515 | return counter; |
516 | } |
517 | |
518 | void IOStatistics::unregisterWorkLoop(IOWorkLoopCounter *counter) |
519 | { |
520 | if (!counter) { |
521 | return; |
522 | } |
523 | |
524 | IORWLockWrite(lock); |
525 | if (counter->parentKext) { |
526 | SLIST_REMOVE(&counter->parentKext->workLoopList, counter, IOWorkLoopCounter, link); |
527 | } |
528 | kfree(counter, sizeof(IOWorkLoopCounter)); |
529 | registeredWorkloops--; |
530 | |
531 | IORWLockUnlock(lock); |
532 | } |
533 | |
534 | IOUserClientCounter *IOStatistics::registerUserClient(IOUserClient *userClient) |
535 | { |
536 | ClassNode sought, *found; |
537 | IOUserClientCounter *counter = NULL; |
538 | |
539 | assert(userClient); |
540 | |
541 | if (!enabled) { |
542 | return NULL; |
543 | } |
544 | |
545 | counter = (IOUserClientCounter*)kalloc(sizeof(IOUserClientCounter)); |
546 | if (!counter) { |
547 | return NULL; |
548 | } |
549 | |
550 | memset(counter, 0, sizeof(IOUserClientCounter)); |
551 | |
552 | IORWLockWrite(lock); |
553 | |
554 | sought.metaClass = userClient->getMetaClass(); |
555 | |
556 | found = RB_FIND(ClassTree, &classHead, &sought); |
557 | if (found) { |
558 | counter->parentClass = found; |
559 | SLIST_INSERT_HEAD(&found->userClientList, counter, link); |
560 | } |
561 | else { |
562 | panic("IOStatistics::registerUserClient: cannot find parent class: %s" , sought.metaClass->getClassName()); |
563 | } |
564 | |
565 | IORWLockUnlock(lock); |
566 | |
567 | return counter; |
568 | } |
569 | |
570 | void IOStatistics::unregisterUserClient(IOUserClientCounter *counter) |
571 | { |
572 | if (!counter) { |
573 | return; |
574 | } |
575 | |
576 | IORWLockWrite(lock); |
577 | |
578 | SLIST_REMOVE(&counter->parentClass->userClientList, counter, IOUserClientCounter, link); |
579 | kfree(counter, sizeof(IOUserClientCounter)); |
580 | |
581 | IORWLockUnlock(lock); |
582 | } |
583 | |
584 | void IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter *wlc, IOEventSourceCounter *esc) |
585 | { |
586 | if (!wlc) { |
587 | return; |
588 | } |
589 | |
590 | IORWLockWrite(lock); |
591 | |
592 | if (!nextWorkLoopDependency) { |
593 | return; |
594 | } |
595 | |
596 | attachedEventSources++; |
597 | wlc->attachedEventSources++; |
598 | |
599 | /* Track the kext dependency */ |
600 | nextWorkLoopDependency->loadTag = esc->parentClass->parentKext->loadTag; |
601 | if (NULL == RB_INSERT(IOWorkLoopCounter::DependencyTree, &wlc->dependencyHead, nextWorkLoopDependency)) { |
602 | nextWorkLoopDependency = (IOWorkLoopDependency*)kalloc(sizeof(IOWorkLoopDependency)); |
603 | } |
604 | |
605 | IORWLockUnlock(lock); |
606 | } |
607 | |
608 | void IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter *wlc, IOEventSourceCounter *esc) |
609 | { |
610 | IOWorkLoopDependency sought, *found; |
611 | |
612 | if (!wlc) { |
613 | return; |
614 | } |
615 | |
616 | IORWLockWrite(lock); |
617 | |
618 | attachedEventSources--; |
619 | wlc->attachedEventSources--; |
620 | |
621 | sought.loadTag = esc->parentClass->parentKext->loadTag; |
622 | |
623 | found = RB_FIND(IOWorkLoopCounter::DependencyTree, &wlc->dependencyHead, &sought); |
624 | if (found) { |
625 | RB_REMOVE(IOWorkLoopCounter::DependencyTree, &wlc->dependencyHead, found); |
626 | kfree(found, sizeof(IOWorkLoopDependency)); |
627 | } |
628 | |
629 | IORWLockUnlock(lock); |
630 | } |
631 | |
632 | int IOStatistics::getStatistics(sysctl_req *req) |
633 | { |
634 | int error; |
635 | uint32_t calculatedSize, size; |
636 | char *buffer, *ptr; |
637 | IOStatisticsHeader *; |
638 | |
639 | assert(IOStatistics::enabled && req); |
640 | |
641 | IORWLockRead(IOStatistics::lock); |
642 | |
643 | /* Work out how much we need to allocate. IOStatisticsKext is of variable size. */ |
644 | calculatedSize = sizeof(IOStatisticsHeader) + |
645 | sizeof(IOStatisticsGlobal) + |
646 | (sizeof(IOStatisticsKext) * loadedKexts) + (sizeof(uint32_t) * registeredClasses) + |
647 | (sizeof(IOStatisticsMemory) * loadedKexts) + |
648 | (sizeof(IOStatisticsClass) * registeredClasses) + |
649 | (sizeof(IOStatisticsCounter) * registeredClasses) + |
650 | (sizeof(IOStatisticsKextIdentifier) * loadedKexts) + |
651 | (sizeof(IOStatisticsClassName) * registeredClasses); |
652 | |
653 | /* Size request? */ |
654 | if (req->oldptr == USER_ADDR_NULL) { |
655 | error = SYSCTL_OUT(req, NULL, calculatedSize); |
656 | goto exit; |
657 | } |
658 | |
659 | /* Read only */ |
660 | if (req->newptr != USER_ADDR_NULL) { |
661 | error = EPERM; |
662 | goto exit; |
663 | } |
664 | |
665 | buffer = (char*)kalloc(calculatedSize); |
666 | if (!buffer) { |
667 | error = ENOMEM; |
668 | goto exit; |
669 | } |
670 | |
671 | memset(buffer, 0, calculatedSize); |
672 | |
673 | ptr = buffer; |
674 | |
675 | header = (IOStatisticsHeader*)((void*)ptr); |
676 | |
677 | header->sig = IOSTATISTICS_SIG; |
678 | header->ver = IOSTATISTICS_VER; |
679 | |
680 | header->seq = sequenceID; |
681 | |
682 | ptr += sizeof(IOStatisticsHeader); |
683 | |
684 | /* Global data - seq, timers, interrupts, etc) */ |
685 | header->globalStatsOffset = sizeof(IOStatisticsHeader); |
686 | size = copyGlobalStatistics((IOStatisticsGlobal*)((void*)ptr)); |
687 | ptr += size; |
688 | |
689 | /* Kext statistics */ |
690 | header->kextStatsOffset = header->globalStatsOffset + size; |
691 | size = copyKextStatistics((IOStatisticsKext*)((void*)ptr)); |
692 | ptr += size; |
693 | |
694 | /* Memory allocation info */ |
695 | header->memoryStatsOffset = header->kextStatsOffset + size; |
696 | size = copyMemoryStatistics((IOStatisticsMemory*)((void*)ptr)); |
697 | ptr += size; |
698 | |
699 | /* Class statistics */ |
700 | header->classStatsOffset = header->memoryStatsOffset + size; |
701 | size = copyClassStatistics((IOStatisticsClass*)((void*)ptr)); |
702 | ptr += size; |
703 | |
704 | /* Dynamic class counter data */ |
705 | header->counterStatsOffset = header->classStatsOffset + size; |
706 | size = copyCounterStatistics((IOStatisticsCounter*)((void*)ptr)); |
707 | ptr += size; |
708 | |
709 | /* Kext identifiers */ |
710 | header->kextIdentifiersOffset = header->counterStatsOffset + size; |
711 | size = copyKextIdentifiers((IOStatisticsKextIdentifier*)((void*)ptr)); |
712 | ptr += size; |
713 | |
714 | /* Class names */ |
715 | header->classNamesOffset = header->kextIdentifiersOffset + size; |
716 | size = copyClassNames((IOStatisticsClassName*)ptr); |
717 | ptr += size; |
718 | |
719 | LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n" , |
720 | calculatedSize, loadedKexts, registeredClasses); |
721 | |
722 | assert( (uint32_t)(ptr - buffer) == calculatedSize ); |
723 | |
724 | error = SYSCTL_OUT(req, buffer, calculatedSize); |
725 | |
726 | kfree(buffer, calculatedSize); |
727 | |
728 | exit: |
729 | IORWLockUnlock(IOStatistics::lock); |
730 | return error; |
731 | } |
732 | |
733 | int IOStatistics::getWorkLoopStatistics(sysctl_req *req) |
734 | { |
735 | int error; |
736 | uint32_t calculatedSize, size; |
737 | char *buffer; |
738 | IOStatisticsWorkLoopHeader *; |
739 | |
740 | assert(IOStatistics::enabled && req); |
741 | |
742 | IORWLockRead(IOStatistics::lock); |
743 | |
744 | /* Approximate how much we need to allocate (worse case estimate) */ |
745 | calculatedSize = sizeof(IOStatisticsWorkLoop) * registeredWorkloops + |
746 | sizeof(uint32_t) * attachedEventSources; |
747 | |
748 | /* Size request? */ |
749 | if (req->oldptr == USER_ADDR_NULL) { |
750 | error = SYSCTL_OUT(req, NULL, calculatedSize); |
751 | goto exit; |
752 | } |
753 | |
754 | /* Read only */ |
755 | if (req->newptr != USER_ADDR_NULL) { |
756 | error = EPERM; |
757 | goto exit; |
758 | } |
759 | |
760 | buffer = (char*)kalloc(calculatedSize); |
761 | if (!buffer) { |
762 | error = ENOMEM; |
763 | goto exit; |
764 | } |
765 | memset(buffer, 0, calculatedSize); |
766 | header = (IOStatisticsWorkLoopHeader*)((void*)buffer); |
767 | |
768 | header->sig = IOSTATISTICS_SIG_WORKLOOP; |
769 | header->ver = IOSTATISTICS_VER; |
770 | |
771 | header->seq = sequenceID; |
772 | |
773 | header->workloopCount = registeredWorkloops; |
774 | |
775 | size = copyWorkLoopStatistics(&header->workLoopStats); |
776 | |
777 | LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n" , calculatedSize, size); |
778 | |
779 | assert( size <= calculatedSize ); |
780 | |
781 | error = SYSCTL_OUT(req, buffer, size); |
782 | |
783 | kfree(buffer, calculatedSize); |
784 | |
785 | exit: |
786 | IORWLockUnlock(IOStatistics::lock); |
787 | return error; |
788 | } |
789 | |
790 | int IOStatistics::getUserClientStatistics(sysctl_req *req) |
791 | { |
792 | int error; |
793 | uint32_t calculatedSize, size; |
794 | char *buffer; |
795 | uint32_t requestedLoadTag = 0; |
796 | IOStatisticsUserClientHeader *; |
797 | |
798 | assert(IOStatistics::enabled && req); |
799 | |
800 | IORWLockRead(IOStatistics::lock); |
801 | |
802 | /* Work out how much we need to allocate */ |
803 | calculatedSize = sizeof(IOStatisticsUserClientHeader) + |
804 | sizeof(IOStatisticsUserClientCall) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS * loadedKexts; |
805 | |
806 | /* Size request? */ |
807 | if (req->oldptr == USER_ADDR_NULL) { |
808 | error = SYSCTL_OUT(req, NULL, calculatedSize); |
809 | goto exit; |
810 | } |
811 | |
812 | /* Kext request (potentially) valid? */ |
813 | if (!req->newptr || req->newlen < sizeof(requestedLoadTag)) { |
814 | error = EINVAL; |
815 | goto exit; |
816 | } |
817 | |
818 | error = SYSCTL_IN(req, &requestedLoadTag, sizeof(requestedLoadTag)); |
819 | if (error) { |
820 | goto exit; |
821 | } |
822 | |
823 | LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n" , requestedLoadTag); |
824 | |
825 | buffer = (char*)kalloc(calculatedSize); |
826 | if (!buffer) { |
827 | error = ENOMEM; |
828 | goto exit; |
829 | } |
830 | memset(buffer, 0, calculatedSize); |
831 | header = (IOStatisticsUserClientHeader*)((void*)buffer); |
832 | |
833 | header->sig = IOSTATISTICS_SIG_USERCLIENT; |
834 | header->ver = IOSTATISTICS_VER; |
835 | |
836 | header->seq = sequenceID; |
837 | |
838 | header->processes = 0; |
839 | |
840 | size = copyUserClientStatistics(header, requestedLoadTag); |
841 | |
842 | assert((sizeof(IOStatisticsUserClientHeader) + size) <= calculatedSize); |
843 | |
844 | if (size) { |
845 | error = SYSCTL_OUT(req, buffer, sizeof(IOStatisticsUserClientHeader) + size); |
846 | } |
847 | else { |
848 | error = EINVAL; |
849 | } |
850 | |
851 | kfree(buffer, calculatedSize); |
852 | |
853 | exit: |
854 | IORWLockUnlock(IOStatistics::lock); |
855 | return error; |
856 | } |
857 | |
858 | uint32_t IOStatistics::copyGlobalStatistics(IOStatisticsGlobal *stats) |
859 | { |
860 | stats->kextCount = loadedKexts; |
861 | stats->classCount = registeredClasses; |
862 | stats->workloops = registeredWorkloops; |
863 | |
864 | return sizeof(IOStatisticsGlobal); |
865 | } |
866 | |
867 | uint32_t IOStatistics::copyKextStatistics(IOStatisticsKext *stats) |
868 | { |
869 | KextNode *ke; |
870 | ClassNode *ce; |
871 | uint32_t index = 0; |
872 | |
873 | RB_FOREACH(ke, KextTree, &kextHead) { |
874 | stats->loadTag = ke->loadTag; |
875 | ke->kext->getSizeInfo(&stats->loadSize, &stats->wiredSize); |
876 | |
877 | stats->classes = ke->classes; |
878 | |
879 | /* Append indices of owned classes */ |
880 | SLIST_FOREACH(ce, &ke->classList, lLink) { |
881 | stats->classIndexes[index++] = ce->classID; |
882 | } |
883 | |
884 | stats = (IOStatisticsKext *)((void*)((char*)stats + sizeof(IOStatisticsKext) + (ke->classes * sizeof(uint32_t)))); |
885 | } |
886 | |
887 | return (sizeof(IOStatisticsKext) * loadedKexts + sizeof(uint32_t) * registeredClasses); |
888 | } |
889 | |
890 | uint32_t IOStatistics::copyMemoryStatistics(IOStatisticsMemory *stats) |
891 | { |
892 | KextNode *ke; |
893 | |
894 | RB_FOREACH(ke, KextTree, &kextHead) { |
895 | stats->allocatedSize = ke->memoryCounters[kIOStatisticsMalloc]; |
896 | stats->freedSize = ke->memoryCounters[kIOStatisticsFree]; |
897 | stats->allocatedAlignedSize = ke->memoryCounters[kIOStatisticsMallocAligned]; |
898 | stats->freedAlignedSize = ke->memoryCounters[kIOStatisticsFreeAligned]; |
899 | stats->allocatedContiguousSize = ke->memoryCounters[kIOStatisticsMallocContiguous]; |
900 | stats->freedContiguousSize = ke->memoryCounters[kIOStatisticsFreeContiguous]; |
901 | stats->allocatedPageableSize = ke->memoryCounters[kIOStatisticsMallocPageable]; |
902 | stats->freedPageableSize = ke->memoryCounters[kIOStatisticsFreePageable]; |
903 | stats++; |
904 | } |
905 | |
906 | return (sizeof(IOStatisticsMemory) * loadedKexts); |
907 | } |
908 | |
909 | uint32_t IOStatistics::copyClassStatistics(IOStatisticsClass *stats) |
910 | { |
911 | KextNode *ke; |
912 | ClassNode *ce; |
913 | |
914 | RB_FOREACH(ke, KextTree, &kextHead) { |
915 | SLIST_FOREACH(ce, &ke->classList, lLink) { |
916 | stats->classID = ce->classID; |
917 | stats->superClassID = ce->superClassID; |
918 | stats->classSize = ce->metaClass->getClassSize(); |
919 | |
920 | stats++; |
921 | } |
922 | } |
923 | |
924 | return sizeof(IOStatisticsClass) * registeredClasses; |
925 | } |
926 | |
927 | uint32_t IOStatistics::copyCounterStatistics(IOStatisticsCounter *stats) |
928 | { |
929 | KextNode *ke; |
930 | ClassNode *ce; |
931 | |
932 | RB_FOREACH(ke, KextTree, &kextHead) { |
933 | SLIST_FOREACH(ce, &ke->classList, lLink) { |
934 | IOUserClientCounter *userClientCounter; |
935 | IOEventSourceCounter *counter; |
936 | |
937 | stats->classID = ce->classID; |
938 | stats->classInstanceCount = ce->metaClass->getInstanceCount(); |
939 | |
940 | IOStatisticsUserClients *uc = &stats->userClientStatistics; |
941 | |
942 | /* User client counters */ |
943 | SLIST_FOREACH(userClientCounter, &ce->userClientList, link) { |
944 | uc->clientCalls += userClientCounter->clientCalls; |
945 | uc->created++; |
946 | } |
947 | |
948 | IOStatisticsInterruptEventSources *iec = &stats->interruptEventSourceStatistics; |
949 | IOStatisticsInterruptEventSources *fiec = &stats->filterInterruptEventSourceStatistics; |
950 | IOStatisticsTimerEventSources *tec = &stats->timerEventSourceStatistics; |
951 | IOStatisticsCommandGates *cgc = &stats->commandGateStatistics; |
952 | IOStatisticsCommandQueues *cqc = &stats->commandQueueStatistics; |
953 | IOStatisticsDerivedEventSources *dec = &stats->derivedEventSourceStatistics; |
954 | |
955 | /* Event source counters */ |
956 | SLIST_FOREACH(counter, &ce->counterList, link) { |
957 | switch (counter->type) { |
958 | case kIOStatisticsInterruptEventSourceCounter: |
959 | iec->created++; |
960 | iec->produced += counter->u.interrupt.produced; |
961 | iec->checksForWork += counter->u.interrupt.checksForWork; |
962 | break; |
963 | case kIOStatisticsFilterInterruptEventSourceCounter: |
964 | fiec->created++; |
965 | fiec->produced += counter->u.filter.produced; |
966 | fiec->checksForWork += counter->u.filter.checksForWork; |
967 | break; |
968 | case kIOStatisticsTimerEventSourceCounter: |
969 | tec->created++; |
970 | tec->timeouts += counter->u.timer.timeouts; |
971 | tec->checksForWork += counter->u.timer.checksForWork; |
972 | tec->timeOnGate += counter->timeOnGate; |
973 | tec->closeGateCalls += counter->closeGateCalls; |
974 | tec->openGateCalls += counter->openGateCalls; |
975 | break; |
976 | case kIOStatisticsCommandGateCounter: |
977 | cgc->created++; |
978 | cgc->timeOnGate += counter->timeOnGate; |
979 | cgc->actionCalls += counter->u.commandGate.actionCalls; |
980 | break; |
981 | case kIOStatisticsCommandQueueCounter: |
982 | cqc->created++; |
983 | cqc->actionCalls += counter->u.commandQueue.actionCalls; |
984 | break; |
985 | case kIOStatisticsDerivedEventSourceCounter: |
986 | dec->created++; |
987 | dec->timeOnGate += counter->timeOnGate; |
988 | dec->closeGateCalls += counter->closeGateCalls; |
989 | dec->openGateCalls += counter->openGateCalls; |
990 | break; |
991 | default: |
992 | break; |
993 | } |
994 | } |
995 | |
996 | stats++; |
997 | } |
998 | } |
999 | |
1000 | return sizeof(IOStatisticsCounter) * registeredClasses; |
1001 | } |
1002 | |
1003 | uint32_t IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier *kextIDs) |
1004 | { |
1005 | KextNode *ke; |
1006 | |
1007 | RB_FOREACH(ke, KextTree, &kextHead) { |
1008 | strncpy(kextIDs->identifier, ke->kext->getIdentifierCString(), kIOStatisticsDriverNameLength); |
1009 | kextIDs++; |
1010 | } |
1011 | |
1012 | return (sizeof(IOStatisticsKextIdentifier) * loadedKexts); |
1013 | } |
1014 | |
1015 | uint32_t IOStatistics::copyClassNames(IOStatisticsClassName *classNames) |
1016 | { |
1017 | KextNode *ke; |
1018 | ClassNode *ce; |
1019 | |
1020 | RB_FOREACH(ke, KextTree, &kextHead) { |
1021 | SLIST_FOREACH(ce, &ke->classList, lLink) { |
1022 | strncpy(classNames->name, ce->metaClass->getClassName(), kIOStatisticsClassNameLength); |
1023 | classNames++; |
1024 | } |
1025 | } |
1026 | |
1027 | return (sizeof(IOStatisticsClassName) * registeredClasses); |
1028 | } |
1029 | |
1030 | uint32_t IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop *stats) |
1031 | { |
1032 | KextNode *ke; |
1033 | IOWorkLoopCounter *wlc; |
1034 | IOWorkLoopDependency *dependentNode; |
1035 | uint32_t size, accumulatedSize = 0; |
1036 | |
1037 | RB_FOREACH(ke, KextTree, &kextHead) { |
1038 | SLIST_FOREACH(wlc, &ke->workLoopList, link) { |
1039 | stats->kextLoadTag = ke->loadTag; |
1040 | stats->attachedEventSources = wlc->attachedEventSources; |
1041 | stats->timeOnGate = wlc->timeOnGate; |
1042 | stats->dependentKexts = 0; |
1043 | RB_FOREACH(dependentNode, IOWorkLoopCounter::DependencyTree, &wlc->dependencyHead) { |
1044 | stats->dependentKextLoadTags[stats->dependentKexts] = dependentNode->loadTag; |
1045 | stats->dependentKexts++; |
1046 | } |
1047 | |
1048 | size = sizeof(IOStatisticsWorkLoop) + (sizeof(uint32_t) * stats->dependentKexts); |
1049 | |
1050 | accumulatedSize += size; |
1051 | stats = (IOStatisticsWorkLoop*)((void*)((char*)stats + size)); |
1052 | } |
1053 | } |
1054 | |
1055 | return accumulatedSize; |
1056 | } |
1057 | |
1058 | uint32_t IOStatistics::(IOStatisticsUserClientHeader *stats, uint32_t loadTag) |
1059 | { |
1060 | KextNode *sought, *found = NULL; |
1061 | uint32_t procs = 0; |
1062 | IOUserClientProcessEntry *processEntry; |
1063 | |
1064 | RB_FOREACH(sought, KextTree, &kextHead) { |
1065 | if (sought->loadTag == loadTag) { |
1066 | found = sought; |
1067 | break; |
1068 | } |
1069 | } |
1070 | |
1071 | if (!found) { |
1072 | return 0; |
1073 | } |
1074 | |
1075 | TAILQ_FOREACH(processEntry, &found->userClientCallList, link) { |
1076 | strncpy(stats->userClientCalls[procs].processName, processEntry->processName, kIOStatisticsProcessNameLength); |
1077 | stats->userClientCalls[procs].pid = processEntry->pid; |
1078 | stats->userClientCalls[procs].calls = processEntry->calls; |
1079 | stats->processes++; |
1080 | procs++; |
1081 | } |
1082 | |
1083 | return sizeof(IOStatisticsUserClientCall) * stats->processes; |
1084 | } |
1085 | |
1086 | void IOStatistics::storeUserClientCallInfo(IOUserClient *userClient, IOUserClientCounter *counter) |
1087 | { |
1088 | OSString *ossUserClientCreator = NULL; |
1089 | int32_t pid = -1; |
1090 | KextNode *parentKext; |
1091 | IOUserClientProcessEntry *entry, *nextEntry, *prevEntry = NULL; |
1092 | uint32_t count = 0; |
1093 | const char *ptr = NULL; |
1094 | OSObject *obj; |
1095 | |
1096 | /* TODO: see if this can be more efficient */ |
1097 | obj = userClient->copyProperty("IOUserClientCreator" , |
1098 | gIOServicePlane, |
1099 | kIORegistryIterateRecursively | kIORegistryIterateParents); |
1100 | |
1101 | if (!obj) |
1102 | goto err_nounlock; |
1103 | |
1104 | ossUserClientCreator = OSDynamicCast(OSString, obj); |
1105 | |
1106 | if (ossUserClientCreator) { |
1107 | uint32_t len, lenIter = 0; |
1108 | |
1109 | ptr = ossUserClientCreator->getCStringNoCopy(); |
1110 | len = ossUserClientCreator->getLength(); |
1111 | |
1112 | while ((*ptr != ' ') && (lenIter < len)) { |
1113 | ptr++; |
1114 | lenIter++; |
1115 | } |
1116 | |
1117 | if (lenIter < len) { |
1118 | ptr++; // Skip the space |
1119 | lenIter++; |
1120 | pid = 0; |
1121 | while ( (*ptr != ',') && (lenIter < len)) { |
1122 | pid = pid*10 + (*ptr - '0'); |
1123 | ptr++; |
1124 | lenIter++; |
1125 | } |
1126 | |
1127 | if(lenIter == len) { |
1128 | pid = -1; |
1129 | } else { |
1130 | ptr += 2; |
1131 | } |
1132 | } |
1133 | } |
1134 | |
1135 | if (-1 == pid) |
1136 | goto err_nounlock; |
1137 | |
1138 | IORWLockWrite(lock); |
1139 | |
1140 | parentKext = counter->parentClass->parentKext; |
1141 | |
1142 | TAILQ_FOREACH(entry, &parentKext->userClientCallList, link) { |
1143 | if (entry->pid == pid) { |
1144 | /* Found, so increment count and move to the head */ |
1145 | entry->calls++; |
1146 | if (count) { |
1147 | TAILQ_REMOVE(&parentKext->userClientCallList, entry, link); |
1148 | break; |
1149 | } |
1150 | else { |
1151 | /* At the head already, so increment and return */ |
1152 | goto err_unlock; |
1153 | } |
1154 | } |
1155 | |
1156 | count++; |
1157 | } |
1158 | |
1159 | if (!entry) { |
1160 | if (count == IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS) { |
1161 | /* Max elements hit, so reuse the last */ |
1162 | entry = TAILQ_LAST(&parentKext->userClientCallList, ProcessEntryList); |
1163 | TAILQ_REMOVE(&parentKext->userClientCallList, entry, link); |
1164 | } |
1165 | else { |
1166 | /* Otherwise, allocate a new entry */ |
1167 | entry = (IOUserClientProcessEntry*)kalloc(sizeof(IOUserClientProcessEntry)); |
1168 | if (!entry) { |
1169 | IORWLockUnlock(lock); |
1170 | return; |
1171 | } |
1172 | } |
1173 | |
1174 | strncpy(entry->processName, ptr, kIOStatisticsProcessNameLength); |
1175 | entry->pid = pid; |
1176 | entry->calls = 1; |
1177 | } |
1178 | |
1179 | TAILQ_FOREACH(nextEntry, &parentKext->userClientCallList, link) { |
1180 | if (nextEntry->calls <= entry->calls) |
1181 | break; |
1182 | |
1183 | prevEntry = nextEntry; |
1184 | } |
1185 | |
1186 | if (!prevEntry) |
1187 | TAILQ_INSERT_HEAD(&parentKext->userClientCallList, entry, link); |
1188 | else |
1189 | TAILQ_INSERT_AFTER(&parentKext->userClientCallList, prevEntry, entry, link); |
1190 | |
1191 | err_unlock: |
1192 | IORWLockUnlock(lock); |
1193 | |
1194 | err_nounlock: |
1195 | if (obj) |
1196 | obj->release(); |
1197 | } |
1198 | |
1199 | void IOStatistics::countUserClientCall(IOUserClient *client) { |
1200 | IOUserClient::ExpansionData *data; |
1201 | IOUserClientCounter *counter; |
1202 | |
1203 | /* Guard against an uninitialized client object - <rdar://problem/8577946> */ |
1204 | if (!(data = client->reserved)) { |
1205 | return; |
1206 | } |
1207 | |
1208 | if ((counter = data->counter)) { |
1209 | storeUserClientCallInfo(client, counter); |
1210 | OSIncrementAtomic(&counter->clientCalls); |
1211 | } |
1212 | } |
1213 | |
1214 | KextNode *IOStatistics::getKextNodeFromBacktrace(boolean_t write) { |
1215 | const uint32_t btMin = 3; |
1216 | |
1217 | void *bt[16]; |
1218 | unsigned btCount = sizeof(bt) / sizeof(bt[0]); |
1219 | vm_offset_t *scanAddr = NULL; |
1220 | uint32_t i; |
1221 | KextNode *found = NULL, *ke = NULL; |
1222 | |
1223 | /* |
1224 | * Gathering the backtrace is a significant source of |
1225 | * overhead. OSBacktrace does many safety checks that |
1226 | * are not needed in this situation. |
1227 | */ |
1228 | btCount = backtrace((uintptr_t*)bt, btCount); |
1229 | |
1230 | if (write) { |
1231 | IORWLockWrite(lock); |
1232 | } else { |
1233 | IORWLockRead(lock); |
1234 | } |
1235 | |
1236 | /* Ignore first levels */ |
1237 | scanAddr = (vm_offset_t *)&bt[btMin - 1]; |
1238 | |
1239 | for (i = btMin - 1; i < btCount; i++, scanAddr++) { |
1240 | ke = RB_ROOT(&kextAddressHead); |
1241 | while (ke) { |
1242 | if (*scanAddr < ke->address) { |
1243 | ke = RB_LEFT(ke, addressLink); |
1244 | } |
1245 | else { |
1246 | if ((*scanAddr < ke->address_end) && (*scanAddr >= ke->address)) { |
1247 | if (!ke->kext->isKernelComponent()) { |
1248 | return ke; |
1249 | } else { |
1250 | found = ke; |
1251 | } |
1252 | } |
1253 | ke = RB_RIGHT(ke, addressLink); |
1254 | } |
1255 | } |
1256 | } |
1257 | |
1258 | if (!found) { |
1259 | IORWLockUnlock(lock); |
1260 | } |
1261 | |
1262 | return found; |
1263 | } |
1264 | |
1265 | void IOStatistics::releaseKextNode(KextNode *node) { |
1266 | #pragma unused(node) |
1267 | IORWLockUnlock(lock); |
1268 | } |
1269 | |
1270 | /* IOLib allocations */ |
1271 | void IOStatistics::countAlloc(uint32_t index, vm_size_t size) { |
1272 | KextNode *ke; |
1273 | |
1274 | if (!enabled) { |
1275 | return; |
1276 | } |
1277 | |
1278 | ke = getKextNodeFromBacktrace(FALSE); |
1279 | if (ke) { |
1280 | OSAddAtomic(size, &ke->memoryCounters[index]); |
1281 | releaseKextNode(ke); |
1282 | } |
1283 | } |
1284 | |
1285 | #endif /* IOKITSTATS */ |
1286 | |