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