2 * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/sysctl.h>
30 #include <kern/backtrace.h>
31 #include <kern/host.h>
32 #include <kern/zalloc.h>
34 #include <IOKit/system.h>
35 #include <libkern/c++/OSKext.h>
36 #include <libkern/OSAtomic.h>
38 #include <IOKit/IOStatisticsPrivate.h>
39 #include <IOKit/IOUserClient.h>
40 #include <IOKit/IOEventSource.h>
41 #include <IOKit/IOKitDebug.h>
44 bool IOStatistics::enabled
= false;
46 uint32_t IOStatistics::sequenceID
= 0;
48 uint32_t IOStatistics::lastClassIndex
= 0;
49 uint32_t IOStatistics::lastKextIndex
= 0;
51 uint32_t IOStatistics::loadedKexts
= 0;
52 uint32_t IOStatistics::registeredClasses
= 0;
53 uint32_t IOStatistics::registeredCounters
= 0;
54 uint32_t IOStatistics::registeredWorkloops
= 0;
56 uint32_t IOStatistics::attachedEventSources
= 0;
58 IOWorkLoopDependency
*IOStatistics::nextWorkLoopDependency
= NULL
;
64 #define LOG(level, format, ...) \
66 if (level <= LOG_LEVEL) \
67 printf(format, ##__VA_ARGS__); \
72 IORWLock
*IOStatistics::lock
= NULL
;
76 KextNode
*IOStatistics::kextHint
= NULL
;
78 IOStatistics::KextTreeHead
IOStatistics::kextHead
= RB_INITIALIZER(&IOStatistics::kextHead
);
81 IOStatistics::kextNodeCompare(KextNode
*e1
, KextNode
*e2
)
83 if (e1
->kext
< e2
->kext
) {
85 } else if (e1
->kext
> e2
->kext
) {
92 RB_GENERATE(IOStatistics::KextTree
, KextNode
, link
, kextNodeCompare
);
94 /* Kext tree ordered by address */
96 IOStatistics::KextAddressTreeHead
IOStatistics::kextAddressHead
= RB_INITIALIZER(&IOStatistics::kextAddressHead
);
99 IOStatistics::kextAddressNodeCompare(KextNode
*e1
, KextNode
*e2
)
101 if (e1
->address
< e2
->address
) {
103 } else if (e1
->address
> e2
->address
) {
110 RB_GENERATE(IOStatistics::KextAddressTree
, KextNode
, addressLink
, kextAddressNodeCompare
);
114 IOStatistics::ClassTreeHead
IOStatistics::classHead
= RB_INITIALIZER(&IOStatistics::classHead
);
117 IOStatistics::classNodeCompare(ClassNode
*e1
, ClassNode
*e2
)
119 if (e1
->metaClass
< e2
->metaClass
) {
121 } else if (e1
->metaClass
> e2
->metaClass
) {
128 RB_GENERATE(IOStatistics::ClassTree
, ClassNode
, tLink
, classNodeCompare
);
130 /* Workloop dependencies */
133 IOWorkLoopCounter::loadTagCompare(IOWorkLoopDependency
*e1
, IOWorkLoopDependency
*e2
)
135 if (e1
->loadTag
< e2
->loadTag
) {
137 } else if (e1
->loadTag
> e2
->loadTag
) {
144 RB_GENERATE(IOWorkLoopCounter::DependencyTree
, IOWorkLoopDependency
, link
, IOWorkLoopCounter::loadTagCompare
);
149 oid_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, int arg2
, struct sysctl_req
*req
)
152 uint32_t request
= arg2
;
155 case kIOStatisticsGeneral
:
156 error
= IOStatistics::getStatistics(req
);
158 case kIOStatisticsWorkLoop
:
159 error
= IOStatistics::getWorkLoopStatistics(req
);
161 case kIOStatisticsUserClient
:
162 error
= IOStatistics::getUserClientStatistics(req
);
171 SYSCTL_NODE(_debug
, OID_AUTO
, iokit_statistics
, CTLFLAG_RW
| CTLFLAG_LOCKED
, NULL
, "IOStatistics");
173 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, general
,
174 CTLTYPE_STRUCT
| CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
175 NULL
, kIOStatisticsGeneral
, oid_sysctl
, "S", "");
177 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, workloop
,
178 CTLTYPE_STRUCT
| CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
179 NULL
, kIOStatisticsWorkLoop
, oid_sysctl
, "S", "");
181 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, userclient
,
182 CTLTYPE_STRUCT
| CTLFLAG_RW
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
183 NULL
, kIOStatisticsUserClient
, oid_sysctl
, "S", "");
186 IOStatistics::initialize()
192 /* Only enabled if the boot argument is set. */
193 if (!(kIOStatistics
& gIOKitDebug
)) {
197 sysctl_register_oid(&sysctl__debug_iokit_statistics_general
);
198 sysctl_register_oid(&sysctl__debug_iokit_statistics_workloop
);
199 sysctl_register_oid(&sysctl__debug_iokit_statistics_userclient
);
201 lock
= IORWLockAlloc();
206 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
207 if (!nextWorkLoopDependency
) {
215 IOStatistics::onKextLoad(OSKext
*kext
, kmod_info_t
*kmod_info
)
219 assert(kext
&& kmod_info
);
225 LOG(1, "IOStatistics::onKextLoad: %s, tag %d, address 0x%llx, address end 0x%llx\n",
226 kext
->getIdentifierCString(), kmod_info
->id
, (uint64_t)kmod_info
->address
, (uint64_t)(kmod_info
->address
+ kmod_info
->size
));
228 ke
= (KextNode
*)kalloc(sizeof(KextNode
));
233 memset(ke
, 0, sizeof(KextNode
));
236 ke
->loadTag
= kmod_info
->id
;
237 ke
->address
= kmod_info
->address
;
238 ke
->address_end
= kmod_info
->address
+ kmod_info
->size
;
240 SLIST_INIT(&ke
->classList
);
241 TAILQ_INIT(&ke
->userClientCallList
);
245 RB_INSERT(KextTree
, &kextHead
, ke
);
246 RB_INSERT(KextAddressTree
, &kextAddressHead
, ke
);
252 IORWLockUnlock(lock
);
256 IOStatistics::onKextUnload(OSKext
*kext
)
258 KextNode sought
, *found
;
266 LOG(1, "IOStatistics::onKextUnload: %s\n", kext
->getIdentifierCString());
271 found
= RB_FIND(KextTree
, &kextHead
, &sought
);
273 IOWorkLoopCounter
*wlc
;
274 IOUserClientProcessEntry
*uce
;
276 /* Disconnect workloop counters; cleanup takes place in unregisterWorkLoop() */
277 while ((wlc
= SLIST_FIRST(&found
->workLoopList
))) {
278 SLIST_REMOVE_HEAD(&found
->workLoopList
, link
);
279 wlc
->parentKext
= NULL
;
282 /* Free up the user client list */
283 while ((uce
= TAILQ_FIRST(&found
->userClientCallList
))) {
284 TAILQ_REMOVE(&found
->userClientCallList
, uce
, link
);
285 kfree(uce
, sizeof(IOUserClientProcessEntry
));
288 /* Remove from kext trees */
289 RB_REMOVE(KextTree
, &kextHead
, found
);
290 RB_REMOVE(KextAddressTree
, &kextAddressHead
, found
);
293 * Clear a matching kextHint to avoid use after free in
294 * onClassAdded() for a class add after a KEXT unload.
296 if (found
== kextHint
) {
300 /* Finally, free the class node */
301 kfree(found
, sizeof(KextNode
));
306 panic("IOStatistics::onKextUnload: cannot find kext: %s", kext
->getIdentifierCString());
309 IORWLockUnlock(lock
);
313 IOStatistics::onClassAdded(OSKext
*parentKext
, OSMetaClass
*metaClass
)
316 KextNode soughtKext
, *foundKext
= NULL
;
318 assert(parentKext
&& metaClass
);
324 LOG(1, "IOStatistics::onClassAdded: %s\n", metaClass
->getClassName());
326 ce
= (ClassNode
*)kalloc(sizeof(ClassNode
));
331 memset(ce
, 0, sizeof(ClassNode
));
336 if (kextHint
&& kextHint
->kext
== parentKext
) {
337 foundKext
= kextHint
;
339 soughtKext
.kext
= parentKext
;
340 foundKext
= RB_FIND(KextTree
, &kextHead
, &soughtKext
);
344 ClassNode soughtClass
, *foundClass
= NULL
;
345 const OSMetaClass
*superClass
;
347 ce
->metaClass
= metaClass
;
348 ce
->classID
= lastClassIndex
++;
349 ce
->parentKext
= foundKext
;
351 /* Has superclass? */
352 superClass
= ce
->metaClass
->getSuperClass();
354 soughtClass
.metaClass
= superClass
;
355 foundClass
= RB_FIND(ClassTree
, &classHead
, &soughtClass
);
357 ce
->superClassID
= foundClass
? foundClass
->classID
: (uint32_t)(-1);
359 SLIST_INIT(&ce
->counterList
);
360 SLIST_INIT(&ce
->userClientList
);
362 RB_INSERT(ClassTree
, &classHead
, ce
);
363 SLIST_INSERT_HEAD(&foundKext
->classList
, ce
, lLink
);
365 foundKext
->classes
++;
367 kextHint
= foundKext
;
372 panic("IOStatistics::onClassAdded: cannot find parent kext: %s", parentKext
->getIdentifierCString());
375 IORWLockUnlock(lock
);
379 IOStatistics::onClassRemoved(OSKext
*parentKext
, OSMetaClass
*metaClass
)
381 ClassNode sought
, *found
;
383 assert(parentKext
&& metaClass
);
389 LOG(1, "IOStatistics::onClassRemoved: %s\n", metaClass
->getClassName());
393 sought
.metaClass
= metaClass
;
394 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
396 IOEventSourceCounter
*esc
;
397 IOUserClientCounter
*ucc
;
399 /* Free up the list of counters */
400 while ((esc
= SLIST_FIRST(&found
->counterList
))) {
401 SLIST_REMOVE_HEAD(&found
->counterList
, link
);
402 kfree(esc
, sizeof(IOEventSourceCounter
));
405 /* Free up the user client list */
406 while ((ucc
= SLIST_FIRST(&found
->userClientList
))) {
407 SLIST_REMOVE_HEAD(&found
->userClientList
, link
);
408 kfree(ucc
, sizeof(IOUserClientCounter
));
411 /* Remove from class tree */
412 RB_REMOVE(ClassTree
, &classHead
, found
);
414 /* Remove from parent */
415 SLIST_REMOVE(&found
->parentKext
->classList
, found
, ClassNode
, lLink
);
417 /* Finally, free the class node */
418 kfree(found
, sizeof(ClassNode
));
423 panic("IOStatistics::onClassRemoved: cannot find class: %s", metaClass
->getClassName());
426 IORWLockUnlock(lock
);
429 IOEventSourceCounter
*
430 IOStatistics::registerEventSource(OSObject
*inOwner
)
432 IOEventSourceCounter
*counter
= NULL
;
433 ClassNode sought
, *found
= NULL
;
434 boolean_t createDummyCounter
= FALSE
;
442 counter
= (IOEventSourceCounter
*)kalloc(sizeof(IOEventSourceCounter
));
447 memset(counter
, 0, sizeof(IOEventSourceCounter
));
451 /* Workaround for <rdar://problem/7158117> - create a dummy counter when inOwner is bad.
452 * We use retainCount here as our best indication that the pointer is awry.
454 if (inOwner
->retainCount
> 0xFFFFFF) {
455 kprintf("IOStatistics::registerEventSource - bad metaclass %p\n", inOwner
);
456 createDummyCounter
= TRUE
;
458 sought
.metaClass
= inOwner
->getMetaClass();
459 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
463 counter
->parentClass
= found
;
464 SLIST_INSERT_HEAD(&found
->counterList
, counter
, link
);
465 registeredCounters
++;
468 if (!(createDummyCounter
|| found
)) {
469 panic("IOStatistics::registerEventSource: cannot find parent class: %s", inOwner
->getMetaClass()->getClassName());
472 IORWLockUnlock(lock
);
478 IOStatistics::unregisterEventSource(IOEventSourceCounter
*counter
)
486 if (counter
->parentClass
) {
487 SLIST_REMOVE(&counter
->parentClass
->counterList
, counter
, IOEventSourceCounter
, link
);
488 registeredCounters
--;
490 kfree(counter
, sizeof(IOEventSourceCounter
));
492 IORWLockUnlock(lock
);
496 IOStatistics::registerWorkLoop(IOWorkLoop
*workLoop
)
498 IOWorkLoopCounter
*counter
= NULL
;
507 counter
= (IOWorkLoopCounter
*)kalloc(sizeof(IOWorkLoopCounter
));
512 memset(counter
, 0, sizeof(IOWorkLoopCounter
));
514 found
= getKextNodeFromBacktrace(TRUE
);
516 panic("IOStatistics::registerWorkLoop: cannot find parent kext");
519 counter
->parentKext
= found
;
520 counter
->workLoop
= workLoop
;
521 RB_INIT(&counter
->dependencyHead
);
522 SLIST_INSERT_HEAD(&found
->workLoopList
, counter
, link
);
523 registeredWorkloops
++;
525 releaseKextNode(found
);
531 IOStatistics::unregisterWorkLoop(IOWorkLoopCounter
*counter
)
538 if (counter
->parentKext
) {
539 SLIST_REMOVE(&counter
->parentKext
->workLoopList
, counter
, IOWorkLoopCounter
, link
);
541 kfree(counter
, sizeof(IOWorkLoopCounter
));
542 registeredWorkloops
--;
544 IORWLockUnlock(lock
);
547 IOUserClientCounter
*
548 IOStatistics::registerUserClient(IOUserClient
*userClient
)
550 ClassNode sought
, *found
;
551 IOUserClientCounter
*counter
= NULL
;
559 counter
= (IOUserClientCounter
*)kalloc(sizeof(IOUserClientCounter
));
564 memset(counter
, 0, sizeof(IOUserClientCounter
));
568 sought
.metaClass
= userClient
->getMetaClass();
570 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
572 counter
->parentClass
= found
;
573 SLIST_INSERT_HEAD(&found
->userClientList
, counter
, link
);
575 panic("IOStatistics::registerUserClient: cannot find parent class: %s", sought
.metaClass
->getClassName());
578 IORWLockUnlock(lock
);
584 IOStatistics::unregisterUserClient(IOUserClientCounter
*counter
)
592 SLIST_REMOVE(&counter
->parentClass
->userClientList
, counter
, IOUserClientCounter
, link
);
593 kfree(counter
, sizeof(IOUserClientCounter
));
595 IORWLockUnlock(lock
);
599 IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
607 if (!nextWorkLoopDependency
) {
611 attachedEventSources
++;
612 wlc
->attachedEventSources
++;
614 /* Track the kext dependency */
615 nextWorkLoopDependency
->loadTag
= esc
->parentClass
->parentKext
->loadTag
;
616 if (NULL
== RB_INSERT(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, nextWorkLoopDependency
)) {
617 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
620 IORWLockUnlock(lock
);
624 IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
626 IOWorkLoopDependency sought
, *found
;
634 attachedEventSources
--;
635 wlc
->attachedEventSources
--;
637 sought
.loadTag
= esc
->parentClass
->parentKext
->loadTag
;
639 found
= RB_FIND(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, &sought
);
641 RB_REMOVE(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, found
);
642 kfree(found
, sizeof(IOWorkLoopDependency
));
645 IORWLockUnlock(lock
);
649 IOStatistics::getStatistics(sysctl_req
*req
)
652 uint32_t calculatedSize
, size
;
654 IOStatisticsHeader
*header
;
656 assert(IOStatistics::enabled
&& req
);
658 IORWLockRead(IOStatistics::lock
);
660 /* Work out how much we need to allocate. IOStatisticsKext is of variable size. */
661 calculatedSize
= sizeof(IOStatisticsHeader
) +
662 sizeof(IOStatisticsGlobal
) +
663 (sizeof(IOStatisticsKext
) * loadedKexts
) + (sizeof(uint32_t) * registeredClasses
) +
664 (sizeof(IOStatisticsMemory
) * loadedKexts
) +
665 (sizeof(IOStatisticsClass
) * registeredClasses
) +
666 (sizeof(IOStatisticsCounter
) * registeredClasses
) +
667 (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
) +
668 (sizeof(IOStatisticsClassName
) * registeredClasses
);
671 if (req
->oldptr
== USER_ADDR_NULL
) {
672 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
677 if (req
->newptr
!= USER_ADDR_NULL
) {
682 buffer
= (char*)kheap_alloc(KHEAP_TEMP
, calculatedSize
,
683 (zalloc_flags_t
)(Z_WAITOK
| Z_ZERO
));
691 header
= (IOStatisticsHeader
*)((void*)ptr
);
693 header
->sig
= IOSTATISTICS_SIG
;
694 header
->ver
= IOSTATISTICS_VER
;
696 header
->seq
= sequenceID
;
698 ptr
+= sizeof(IOStatisticsHeader
);
700 /* Global data - seq, timers, interrupts, etc) */
701 header
->globalStatsOffset
= sizeof(IOStatisticsHeader
);
702 size
= copyGlobalStatistics((IOStatisticsGlobal
*)((void*)ptr
));
705 /* Kext statistics */
706 header
->kextStatsOffset
= header
->globalStatsOffset
+ size
;
707 size
= copyKextStatistics((IOStatisticsKext
*)((void*)ptr
));
710 /* Memory allocation info */
711 header
->memoryStatsOffset
= header
->kextStatsOffset
+ size
;
712 size
= copyMemoryStatistics((IOStatisticsMemory
*)((void*)ptr
));
715 /* Class statistics */
716 header
->classStatsOffset
= header
->memoryStatsOffset
+ size
;
717 size
= copyClassStatistics((IOStatisticsClass
*)((void*)ptr
));
720 /* Dynamic class counter data */
721 header
->counterStatsOffset
= header
->classStatsOffset
+ size
;
722 size
= copyCounterStatistics((IOStatisticsCounter
*)((void*)ptr
));
725 /* Kext identifiers */
726 header
->kextIdentifiersOffset
= header
->counterStatsOffset
+ size
;
727 size
= copyKextIdentifiers((IOStatisticsKextIdentifier
*)((void*)ptr
));
731 header
->classNamesOffset
= header
->kextIdentifiersOffset
+ size
;
732 size
= copyClassNames((IOStatisticsClassName
*)ptr
);
735 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
736 calculatedSize
, loadedKexts
, registeredClasses
);
738 assert((uint32_t)(ptr
- buffer
) == calculatedSize
);
740 error
= SYSCTL_OUT(req
, buffer
, calculatedSize
);
742 kheap_free(KHEAP_TEMP
, buffer
, calculatedSize
);
745 IORWLockUnlock(IOStatistics::lock
);
750 IOStatistics::getWorkLoopStatistics(sysctl_req
*req
)
753 uint32_t calculatedSize
, size
;
755 IOStatisticsWorkLoopHeader
*header
;
757 assert(IOStatistics::enabled
&& req
);
759 IORWLockRead(IOStatistics::lock
);
761 /* Approximate how much we need to allocate (worse case estimate) */
762 calculatedSize
= sizeof(IOStatisticsWorkLoop
) * registeredWorkloops
+
763 sizeof(uint32_t) * attachedEventSources
;
766 if (req
->oldptr
== USER_ADDR_NULL
) {
767 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
772 if (req
->newptr
!= USER_ADDR_NULL
) {
777 buffer
= (char*)kheap_alloc(KHEAP_TEMP
, calculatedSize
,
778 (zalloc_flags_t
)(Z_WAITOK
| Z_ZERO
));
783 header
= (IOStatisticsWorkLoopHeader
*)((void*)buffer
);
785 header
->sig
= IOSTATISTICS_SIG_WORKLOOP
;
786 header
->ver
= IOSTATISTICS_VER
;
788 header
->seq
= sequenceID
;
790 header
->workloopCount
= registeredWorkloops
;
792 size
= copyWorkLoopStatistics(&header
->workLoopStats
);
794 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize
, size
);
796 assert( size
<= calculatedSize
);
798 error
= SYSCTL_OUT(req
, buffer
, size
);
800 kheap_free(KHEAP_TEMP
, buffer
, calculatedSize
);
803 IORWLockUnlock(IOStatistics::lock
);
808 IOStatistics::getUserClientStatistics(sysctl_req
*req
)
811 uint32_t calculatedSize
, size
;
813 uint32_t requestedLoadTag
= 0;
814 IOStatisticsUserClientHeader
*header
;
816 assert(IOStatistics::enabled
&& req
);
818 IORWLockRead(IOStatistics::lock
);
820 /* Work out how much we need to allocate */
821 calculatedSize
= sizeof(IOStatisticsUserClientHeader
) +
822 sizeof(IOStatisticsUserClientCall
) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
* loadedKexts
;
825 if (req
->oldptr
== USER_ADDR_NULL
) {
826 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
830 /* Kext request (potentially) valid? */
831 if (!req
->newptr
|| req
->newlen
< sizeof(requestedLoadTag
)) {
836 error
= SYSCTL_IN(req
, &requestedLoadTag
, sizeof(requestedLoadTag
));
841 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag
);
843 buffer
= (char*)kheap_alloc(KHEAP_TEMP
, calculatedSize
,
844 (zalloc_flags_t
)(Z_WAITOK
| Z_ZERO
));
849 header
= (IOStatisticsUserClientHeader
*)((void*)buffer
);
851 header
->sig
= IOSTATISTICS_SIG_USERCLIENT
;
852 header
->ver
= IOSTATISTICS_VER
;
854 header
->seq
= sequenceID
;
856 header
->processes
= 0;
858 size
= copyUserClientStatistics(header
, requestedLoadTag
);
860 assert((sizeof(IOStatisticsUserClientHeader
) + size
) <= calculatedSize
);
863 error
= SYSCTL_OUT(req
, buffer
, sizeof(IOStatisticsUserClientHeader
) + size
);
868 kheap_free(KHEAP_TEMP
, buffer
, calculatedSize
);
871 IORWLockUnlock(IOStatistics::lock
);
876 IOStatistics::copyGlobalStatistics(IOStatisticsGlobal
*stats
)
878 stats
->kextCount
= loadedKexts
;
879 stats
->classCount
= registeredClasses
;
880 stats
->workloops
= registeredWorkloops
;
882 return sizeof(IOStatisticsGlobal
);
886 IOStatistics::copyKextStatistics(IOStatisticsKext
*stats
)
892 RB_FOREACH(ke
, KextTree
, &kextHead
) {
893 stats
->loadTag
= ke
->loadTag
;
894 ke
->kext
->getSizeInfo(&stats
->loadSize
, &stats
->wiredSize
);
896 stats
->classes
= ke
->classes
;
898 /* Append indices of owned classes */
899 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
900 stats
->classIndexes
[index
++] = ce
->classID
;
903 stats
= (IOStatisticsKext
*)((void*)((char*)stats
+ sizeof(IOStatisticsKext
) + (ke
->classes
* sizeof(uint32_t))));
906 return sizeof(IOStatisticsKext
) * loadedKexts
+ sizeof(uint32_t) * registeredClasses
;
910 IOStatistics::copyMemoryStatistics(IOStatisticsMemory
*stats
)
914 RB_FOREACH(ke
, KextTree
, &kextHead
) {
915 stats
->allocatedSize
= ke
->memoryCounters
[kIOStatisticsMalloc
];
916 stats
->freedSize
= ke
->memoryCounters
[kIOStatisticsFree
];
917 stats
->allocatedAlignedSize
= ke
->memoryCounters
[kIOStatisticsMallocAligned
];
918 stats
->freedAlignedSize
= ke
->memoryCounters
[kIOStatisticsFreeAligned
];
919 stats
->allocatedContiguousSize
= ke
->memoryCounters
[kIOStatisticsMallocContiguous
];
920 stats
->freedContiguousSize
= ke
->memoryCounters
[kIOStatisticsFreeContiguous
];
921 stats
->allocatedPageableSize
= ke
->memoryCounters
[kIOStatisticsMallocPageable
];
922 stats
->freedPageableSize
= ke
->memoryCounters
[kIOStatisticsFreePageable
];
926 return sizeof(IOStatisticsMemory
) * loadedKexts
;
930 IOStatistics::copyClassStatistics(IOStatisticsClass
*stats
)
935 RB_FOREACH(ke
, KextTree
, &kextHead
) {
936 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
937 stats
->classID
= ce
->classID
;
938 stats
->superClassID
= ce
->superClassID
;
939 stats
->classSize
= ce
->metaClass
->getClassSize();
945 return sizeof(IOStatisticsClass
) * registeredClasses
;
949 IOStatistics::copyCounterStatistics(IOStatisticsCounter
*stats
)
954 RB_FOREACH(ke
, KextTree
, &kextHead
) {
955 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
956 IOUserClientCounter
*userClientCounter
;
957 IOEventSourceCounter
*counter
;
959 stats
->classID
= ce
->classID
;
960 stats
->classInstanceCount
= ce
->metaClass
->getInstanceCount();
962 IOStatisticsUserClients
*uc
= &stats
->userClientStatistics
;
964 /* User client counters */
965 SLIST_FOREACH(userClientCounter
, &ce
->userClientList
, link
) {
966 uc
->clientCalls
+= userClientCounter
->clientCalls
;
970 IOStatisticsInterruptEventSources
*iec
= &stats
->interruptEventSourceStatistics
;
971 IOStatisticsInterruptEventSources
*fiec
= &stats
->filterInterruptEventSourceStatistics
;
972 IOStatisticsTimerEventSources
*tec
= &stats
->timerEventSourceStatistics
;
973 IOStatisticsCommandGates
*cgc
= &stats
->commandGateStatistics
;
974 IOStatisticsCommandQueues
*cqc
= &stats
->commandQueueStatistics
;
975 IOStatisticsDerivedEventSources
*dec
= &stats
->derivedEventSourceStatistics
;
977 /* Event source counters */
978 SLIST_FOREACH(counter
, &ce
->counterList
, link
) {
979 switch (counter
->type
) {
980 case kIOStatisticsInterruptEventSourceCounter
:
982 iec
->produced
+= counter
->u
.interrupt
.produced
;
983 iec
->checksForWork
+= counter
->u
.interrupt
.checksForWork
;
985 case kIOStatisticsFilterInterruptEventSourceCounter
:
987 fiec
->produced
+= counter
->u
.filter
.produced
;
988 fiec
->checksForWork
+= counter
->u
.filter
.checksForWork
;
990 case kIOStatisticsTimerEventSourceCounter
:
992 tec
->timeouts
+= counter
->u
.timer
.timeouts
;
993 tec
->checksForWork
+= counter
->u
.timer
.checksForWork
;
994 tec
->timeOnGate
+= counter
->timeOnGate
;
995 tec
->closeGateCalls
+= counter
->closeGateCalls
;
996 tec
->openGateCalls
+= counter
->openGateCalls
;
998 case kIOStatisticsCommandGateCounter
:
1000 cgc
->timeOnGate
+= counter
->timeOnGate
;
1001 cgc
->actionCalls
+= counter
->u
.commandGate
.actionCalls
;
1003 case kIOStatisticsCommandQueueCounter
:
1005 cqc
->actionCalls
+= counter
->u
.commandQueue
.actionCalls
;
1007 case kIOStatisticsDerivedEventSourceCounter
:
1009 dec
->timeOnGate
+= counter
->timeOnGate
;
1010 dec
->closeGateCalls
+= counter
->closeGateCalls
;
1011 dec
->openGateCalls
+= counter
->openGateCalls
;
1022 return sizeof(IOStatisticsCounter
) * registeredClasses
;
1026 IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier
*kextIDs
)
1030 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1031 strncpy(kextIDs
->identifier
, ke
->kext
->getIdentifierCString(), kIOStatisticsDriverNameLength
);
1035 return sizeof(IOStatisticsKextIdentifier
) * loadedKexts
;
1039 IOStatistics::copyClassNames(IOStatisticsClassName
*classNames
)
1044 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1045 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
1046 strncpy(classNames
->name
, ce
->metaClass
->getClassName(), kIOStatisticsClassNameLength
);
1051 return sizeof(IOStatisticsClassName
) * registeredClasses
;
1055 IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop
*stats
)
1058 IOWorkLoopCounter
*wlc
;
1059 IOWorkLoopDependency
*dependentNode
;
1060 uint32_t size
, accumulatedSize
= 0;
1062 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1063 SLIST_FOREACH(wlc
, &ke
->workLoopList
, link
) {
1064 stats
->kextLoadTag
= ke
->loadTag
;
1065 stats
->attachedEventSources
= wlc
->attachedEventSources
;
1066 stats
->timeOnGate
= wlc
->timeOnGate
;
1067 stats
->dependentKexts
= 0;
1068 RB_FOREACH(dependentNode
, IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
) {
1069 stats
->dependentKextLoadTags
[stats
->dependentKexts
] = dependentNode
->loadTag
;
1070 stats
->dependentKexts
++;
1073 size
= sizeof(IOStatisticsWorkLoop
) + (sizeof(uint32_t) * stats
->dependentKexts
);
1075 accumulatedSize
+= size
;
1076 stats
= (IOStatisticsWorkLoop
*)((void*)((char*)stats
+ size
));
1080 return accumulatedSize
;
1084 IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader
*stats
, uint32_t loadTag
)
1086 KextNode
*sought
, *found
= NULL
;
1088 IOUserClientProcessEntry
*processEntry
;
1090 RB_FOREACH(sought
, KextTree
, &kextHead
) {
1091 if (sought
->loadTag
== loadTag
) {
1101 TAILQ_FOREACH(processEntry
, &found
->userClientCallList
, link
) {
1102 strncpy(stats
->userClientCalls
[procs
].processName
, processEntry
->processName
, kIOStatisticsProcessNameLength
);
1103 stats
->userClientCalls
[procs
].pid
= processEntry
->pid
;
1104 stats
->userClientCalls
[procs
].calls
= processEntry
->calls
;
1109 return sizeof(IOStatisticsUserClientCall
) * stats
->processes
;
1113 IOStatistics::storeUserClientCallInfo(IOUserClient
*userClient
, IOUserClientCounter
*counter
)
1115 OSString
*ossUserClientCreator
= NULL
;
1117 KextNode
*parentKext
;
1118 IOUserClientProcessEntry
*entry
, *nextEntry
, *prevEntry
= NULL
;
1120 const char *ptr
= NULL
;
1123 /* TODO: see if this can be more efficient */
1124 obj
= userClient
->copyProperty("IOUserClientCreator",
1126 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
1132 ossUserClientCreator
= OSDynamicCast(OSString
, obj
);
1134 if (ossUserClientCreator
) {
1135 uint32_t len
, lenIter
= 0;
1137 ptr
= ossUserClientCreator
->getCStringNoCopy();
1138 len
= ossUserClientCreator
->getLength();
1140 while ((*ptr
!= ' ') && (lenIter
< len
)) {
1145 if (lenIter
< len
) {
1146 ptr
++; // Skip the space
1149 while ((*ptr
!= ',') && (lenIter
< len
)) {
1150 pid
= pid
* 10 + (*ptr
- '0');
1155 if (lenIter
== len
) {
1167 IORWLockWrite(lock
);
1169 parentKext
= counter
->parentClass
->parentKext
;
1171 TAILQ_FOREACH(entry
, &parentKext
->userClientCallList
, link
) {
1172 if (entry
->pid
== pid
) {
1173 /* Found, so increment count and move to the head */
1176 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1179 /* At the head already, so increment and return */
1188 if (count
== IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
) {
1189 /* Max elements hit, so reuse the last */
1190 entry
= TAILQ_LAST(&parentKext
->userClientCallList
, ProcessEntryList
);
1191 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1193 /* Otherwise, allocate a new entry */
1194 entry
= (IOUserClientProcessEntry
*)kalloc(sizeof(IOUserClientProcessEntry
));
1196 IORWLockUnlock(lock
);
1201 strncpy(entry
->processName
, ptr
, kIOStatisticsProcessNameLength
);
1206 TAILQ_FOREACH(nextEntry
, &parentKext
->userClientCallList
, link
) {
1207 if (nextEntry
->calls
<= entry
->calls
) {
1211 prevEntry
= nextEntry
;
1215 TAILQ_INSERT_HEAD(&parentKext
->userClientCallList
, entry
, link
);
1217 TAILQ_INSERT_AFTER(&parentKext
->userClientCallList
, prevEntry
, entry
, link
);
1221 IORWLockUnlock(lock
);
1230 IOStatistics::countUserClientCall(IOUserClient
*client
)
1232 IOUserClient::ExpansionData
*data
;
1233 IOUserClientCounter
*counter
;
1235 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1236 if (!(data
= client
->reserved
)) {
1240 if ((counter
= data
->counter
)) {
1241 storeUserClientCallInfo(client
, counter
);
1242 OSIncrementAtomic(&counter
->clientCalls
);
1247 IOStatistics::getKextNodeFromBacktrace(boolean_t write
)
1249 const uint32_t btMin
= 3;
1252 unsigned btCount
= sizeof(bt
) / sizeof(bt
[0]);
1253 vm_offset_t
*scanAddr
= NULL
;
1255 KextNode
*found
= NULL
, *ke
= NULL
;
1258 * Gathering the backtrace is a significant source of
1259 * overhead. OSBacktrace does many safety checks that
1260 * are not needed in this situation.
1262 btCount
= backtrace((uintptr_t*)bt
, btCount
, NULL
);
1265 IORWLockWrite(lock
);
1270 /* Ignore first levels */
1271 scanAddr
= (vm_offset_t
*)&bt
[btMin
- 1];
1273 for (i
= btMin
- 1; i
< btCount
; i
++, scanAddr
++) {
1274 ke
= RB_ROOT(&kextAddressHead
);
1276 if (*scanAddr
< ke
->address
) {
1277 ke
= RB_LEFT(ke
, addressLink
);
1279 if ((*scanAddr
< ke
->address_end
) && (*scanAddr
>= ke
->address
)) {
1280 if (!ke
->kext
->isKernelComponent()) {
1286 ke
= RB_RIGHT(ke
, addressLink
);
1292 IORWLockUnlock(lock
);
1299 IOStatistics::releaseKextNode(KextNode
*node
)
1301 #pragma unused(node)
1302 IORWLockUnlock(lock
);
1305 /* IOLib allocations */
1307 IOStatistics::countAlloc(uint32_t index
, vm_size_t size
)
1314 if (size
> INT_MAX
) {
1318 ke
= getKextNodeFromBacktrace(FALSE
);
1320 OSAddAtomic((SInt32
) size
, &ke
->memoryCounters
[index
]);
1321 releaseKextNode(ke
);
1325 #endif /* IOKITSTATS */