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*)kalloc(calculatedSize
);
688 memset(buffer
, 0, calculatedSize
);
692 header
= (IOStatisticsHeader
*)((void*)ptr
);
694 header
->sig
= IOSTATISTICS_SIG
;
695 header
->ver
= IOSTATISTICS_VER
;
697 header
->seq
= sequenceID
;
699 ptr
+= sizeof(IOStatisticsHeader
);
701 /* Global data - seq, timers, interrupts, etc) */
702 header
->globalStatsOffset
= sizeof(IOStatisticsHeader
);
703 size
= copyGlobalStatistics((IOStatisticsGlobal
*)((void*)ptr
));
706 /* Kext statistics */
707 header
->kextStatsOffset
= header
->globalStatsOffset
+ size
;
708 size
= copyKextStatistics((IOStatisticsKext
*)((void*)ptr
));
711 /* Memory allocation info */
712 header
->memoryStatsOffset
= header
->kextStatsOffset
+ size
;
713 size
= copyMemoryStatistics((IOStatisticsMemory
*)((void*)ptr
));
716 /* Class statistics */
717 header
->classStatsOffset
= header
->memoryStatsOffset
+ size
;
718 size
= copyClassStatistics((IOStatisticsClass
*)((void*)ptr
));
721 /* Dynamic class counter data */
722 header
->counterStatsOffset
= header
->classStatsOffset
+ size
;
723 size
= copyCounterStatistics((IOStatisticsCounter
*)((void*)ptr
));
726 /* Kext identifiers */
727 header
->kextIdentifiersOffset
= header
->counterStatsOffset
+ size
;
728 size
= copyKextIdentifiers((IOStatisticsKextIdentifier
*)((void*)ptr
));
732 header
->classNamesOffset
= header
->kextIdentifiersOffset
+ size
;
733 size
= copyClassNames((IOStatisticsClassName
*)ptr
);
736 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
737 calculatedSize
, loadedKexts
, registeredClasses
);
739 assert((uint32_t)(ptr
- buffer
) == calculatedSize
);
741 error
= SYSCTL_OUT(req
, buffer
, calculatedSize
);
743 kfree(buffer
, calculatedSize
);
746 IORWLockUnlock(IOStatistics::lock
);
751 IOStatistics::getWorkLoopStatistics(sysctl_req
*req
)
754 uint32_t calculatedSize
, size
;
756 IOStatisticsWorkLoopHeader
*header
;
758 assert(IOStatistics::enabled
&& req
);
760 IORWLockRead(IOStatistics::lock
);
762 /* Approximate how much we need to allocate (worse case estimate) */
763 calculatedSize
= sizeof(IOStatisticsWorkLoop
) * registeredWorkloops
+
764 sizeof(uint32_t) * attachedEventSources
;
767 if (req
->oldptr
== USER_ADDR_NULL
) {
768 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
773 if (req
->newptr
!= USER_ADDR_NULL
) {
778 buffer
= (char*)kalloc(calculatedSize
);
783 memset(buffer
, 0, calculatedSize
);
784 header
= (IOStatisticsWorkLoopHeader
*)((void*)buffer
);
786 header
->sig
= IOSTATISTICS_SIG_WORKLOOP
;
787 header
->ver
= IOSTATISTICS_VER
;
789 header
->seq
= sequenceID
;
791 header
->workloopCount
= registeredWorkloops
;
793 size
= copyWorkLoopStatistics(&header
->workLoopStats
);
795 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize
, size
);
797 assert( size
<= calculatedSize
);
799 error
= SYSCTL_OUT(req
, buffer
, size
);
801 kfree(buffer
, calculatedSize
);
804 IORWLockUnlock(IOStatistics::lock
);
809 IOStatistics::getUserClientStatistics(sysctl_req
*req
)
812 uint32_t calculatedSize
, size
;
814 uint32_t requestedLoadTag
= 0;
815 IOStatisticsUserClientHeader
*header
;
817 assert(IOStatistics::enabled
&& req
);
819 IORWLockRead(IOStatistics::lock
);
821 /* Work out how much we need to allocate */
822 calculatedSize
= sizeof(IOStatisticsUserClientHeader
) +
823 sizeof(IOStatisticsUserClientCall
) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
* loadedKexts
;
826 if (req
->oldptr
== USER_ADDR_NULL
) {
827 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
831 /* Kext request (potentially) valid? */
832 if (!req
->newptr
|| req
->newlen
< sizeof(requestedLoadTag
)) {
837 error
= SYSCTL_IN(req
, &requestedLoadTag
, sizeof(requestedLoadTag
));
842 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag
);
844 buffer
= (char*)kalloc(calculatedSize
);
849 memset(buffer
, 0, calculatedSize
);
850 header
= (IOStatisticsUserClientHeader
*)((void*)buffer
);
852 header
->sig
= IOSTATISTICS_SIG_USERCLIENT
;
853 header
->ver
= IOSTATISTICS_VER
;
855 header
->seq
= sequenceID
;
857 header
->processes
= 0;
859 size
= copyUserClientStatistics(header
, requestedLoadTag
);
861 assert((sizeof(IOStatisticsUserClientHeader
) + size
) <= calculatedSize
);
864 error
= SYSCTL_OUT(req
, buffer
, sizeof(IOStatisticsUserClientHeader
) + size
);
869 kfree(buffer
, calculatedSize
);
872 IORWLockUnlock(IOStatistics::lock
);
877 IOStatistics::copyGlobalStatistics(IOStatisticsGlobal
*stats
)
879 stats
->kextCount
= loadedKexts
;
880 stats
->classCount
= registeredClasses
;
881 stats
->workloops
= registeredWorkloops
;
883 return sizeof(IOStatisticsGlobal
);
887 IOStatistics::copyKextStatistics(IOStatisticsKext
*stats
)
893 RB_FOREACH(ke
, KextTree
, &kextHead
) {
894 stats
->loadTag
= ke
->loadTag
;
895 ke
->kext
->getSizeInfo(&stats
->loadSize
, &stats
->wiredSize
);
897 stats
->classes
= ke
->classes
;
899 /* Append indices of owned classes */
900 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
901 stats
->classIndexes
[index
++] = ce
->classID
;
904 stats
= (IOStatisticsKext
*)((void*)((char*)stats
+ sizeof(IOStatisticsKext
) + (ke
->classes
* sizeof(uint32_t))));
907 return sizeof(IOStatisticsKext
) * loadedKexts
+ sizeof(uint32_t) * registeredClasses
;
911 IOStatistics::copyMemoryStatistics(IOStatisticsMemory
*stats
)
915 RB_FOREACH(ke
, KextTree
, &kextHead
) {
916 stats
->allocatedSize
= ke
->memoryCounters
[kIOStatisticsMalloc
];
917 stats
->freedSize
= ke
->memoryCounters
[kIOStatisticsFree
];
918 stats
->allocatedAlignedSize
= ke
->memoryCounters
[kIOStatisticsMallocAligned
];
919 stats
->freedAlignedSize
= ke
->memoryCounters
[kIOStatisticsFreeAligned
];
920 stats
->allocatedContiguousSize
= ke
->memoryCounters
[kIOStatisticsMallocContiguous
];
921 stats
->freedContiguousSize
= ke
->memoryCounters
[kIOStatisticsFreeContiguous
];
922 stats
->allocatedPageableSize
= ke
->memoryCounters
[kIOStatisticsMallocPageable
];
923 stats
->freedPageableSize
= ke
->memoryCounters
[kIOStatisticsFreePageable
];
927 return sizeof(IOStatisticsMemory
) * loadedKexts
;
931 IOStatistics::copyClassStatistics(IOStatisticsClass
*stats
)
936 RB_FOREACH(ke
, KextTree
, &kextHead
) {
937 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
938 stats
->classID
= ce
->classID
;
939 stats
->superClassID
= ce
->superClassID
;
940 stats
->classSize
= ce
->metaClass
->getClassSize();
946 return sizeof(IOStatisticsClass
) * registeredClasses
;
950 IOStatistics::copyCounterStatistics(IOStatisticsCounter
*stats
)
955 RB_FOREACH(ke
, KextTree
, &kextHead
) {
956 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
957 IOUserClientCounter
*userClientCounter
;
958 IOEventSourceCounter
*counter
;
960 stats
->classID
= ce
->classID
;
961 stats
->classInstanceCount
= ce
->metaClass
->getInstanceCount();
963 IOStatisticsUserClients
*uc
= &stats
->userClientStatistics
;
965 /* User client counters */
966 SLIST_FOREACH(userClientCounter
, &ce
->userClientList
, link
) {
967 uc
->clientCalls
+= userClientCounter
->clientCalls
;
971 IOStatisticsInterruptEventSources
*iec
= &stats
->interruptEventSourceStatistics
;
972 IOStatisticsInterruptEventSources
*fiec
= &stats
->filterInterruptEventSourceStatistics
;
973 IOStatisticsTimerEventSources
*tec
= &stats
->timerEventSourceStatistics
;
974 IOStatisticsCommandGates
*cgc
= &stats
->commandGateStatistics
;
975 IOStatisticsCommandQueues
*cqc
= &stats
->commandQueueStatistics
;
976 IOStatisticsDerivedEventSources
*dec
= &stats
->derivedEventSourceStatistics
;
978 /* Event source counters */
979 SLIST_FOREACH(counter
, &ce
->counterList
, link
) {
980 switch (counter
->type
) {
981 case kIOStatisticsInterruptEventSourceCounter
:
983 iec
->produced
+= counter
->u
.interrupt
.produced
;
984 iec
->checksForWork
+= counter
->u
.interrupt
.checksForWork
;
986 case kIOStatisticsFilterInterruptEventSourceCounter
:
988 fiec
->produced
+= counter
->u
.filter
.produced
;
989 fiec
->checksForWork
+= counter
->u
.filter
.checksForWork
;
991 case kIOStatisticsTimerEventSourceCounter
:
993 tec
->timeouts
+= counter
->u
.timer
.timeouts
;
994 tec
->checksForWork
+= counter
->u
.timer
.checksForWork
;
995 tec
->timeOnGate
+= counter
->timeOnGate
;
996 tec
->closeGateCalls
+= counter
->closeGateCalls
;
997 tec
->openGateCalls
+= counter
->openGateCalls
;
999 case kIOStatisticsCommandGateCounter
:
1001 cgc
->timeOnGate
+= counter
->timeOnGate
;
1002 cgc
->actionCalls
+= counter
->u
.commandGate
.actionCalls
;
1004 case kIOStatisticsCommandQueueCounter
:
1006 cqc
->actionCalls
+= counter
->u
.commandQueue
.actionCalls
;
1008 case kIOStatisticsDerivedEventSourceCounter
:
1010 dec
->timeOnGate
+= counter
->timeOnGate
;
1011 dec
->closeGateCalls
+= counter
->closeGateCalls
;
1012 dec
->openGateCalls
+= counter
->openGateCalls
;
1023 return sizeof(IOStatisticsCounter
) * registeredClasses
;
1027 IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier
*kextIDs
)
1031 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1032 strncpy(kextIDs
->identifier
, ke
->kext
->getIdentifierCString(), kIOStatisticsDriverNameLength
);
1036 return sizeof(IOStatisticsKextIdentifier
) * loadedKexts
;
1040 IOStatistics::copyClassNames(IOStatisticsClassName
*classNames
)
1045 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1046 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
1047 strncpy(classNames
->name
, ce
->metaClass
->getClassName(), kIOStatisticsClassNameLength
);
1052 return sizeof(IOStatisticsClassName
) * registeredClasses
;
1056 IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop
*stats
)
1059 IOWorkLoopCounter
*wlc
;
1060 IOWorkLoopDependency
*dependentNode
;
1061 uint32_t size
, accumulatedSize
= 0;
1063 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1064 SLIST_FOREACH(wlc
, &ke
->workLoopList
, link
) {
1065 stats
->kextLoadTag
= ke
->loadTag
;
1066 stats
->attachedEventSources
= wlc
->attachedEventSources
;
1067 stats
->timeOnGate
= wlc
->timeOnGate
;
1068 stats
->dependentKexts
= 0;
1069 RB_FOREACH(dependentNode
, IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
) {
1070 stats
->dependentKextLoadTags
[stats
->dependentKexts
] = dependentNode
->loadTag
;
1071 stats
->dependentKexts
++;
1074 size
= sizeof(IOStatisticsWorkLoop
) + (sizeof(uint32_t) * stats
->dependentKexts
);
1076 accumulatedSize
+= size
;
1077 stats
= (IOStatisticsWorkLoop
*)((void*)((char*)stats
+ size
));
1081 return accumulatedSize
;
1085 IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader
*stats
, uint32_t loadTag
)
1087 KextNode
*sought
, *found
= NULL
;
1089 IOUserClientProcessEntry
*processEntry
;
1091 RB_FOREACH(sought
, KextTree
, &kextHead
) {
1092 if (sought
->loadTag
== loadTag
) {
1102 TAILQ_FOREACH(processEntry
, &found
->userClientCallList
, link
) {
1103 strncpy(stats
->userClientCalls
[procs
].processName
, processEntry
->processName
, kIOStatisticsProcessNameLength
);
1104 stats
->userClientCalls
[procs
].pid
= processEntry
->pid
;
1105 stats
->userClientCalls
[procs
].calls
= processEntry
->calls
;
1110 return sizeof(IOStatisticsUserClientCall
) * stats
->processes
;
1114 IOStatistics::storeUserClientCallInfo(IOUserClient
*userClient
, IOUserClientCounter
*counter
)
1116 OSString
*ossUserClientCreator
= NULL
;
1118 KextNode
*parentKext
;
1119 IOUserClientProcessEntry
*entry
, *nextEntry
, *prevEntry
= NULL
;
1121 const char *ptr
= NULL
;
1124 /* TODO: see if this can be more efficient */
1125 obj
= userClient
->copyProperty("IOUserClientCreator",
1127 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
1133 ossUserClientCreator
= OSDynamicCast(OSString
, obj
);
1135 if (ossUserClientCreator
) {
1136 uint32_t len
, lenIter
= 0;
1138 ptr
= ossUserClientCreator
->getCStringNoCopy();
1139 len
= ossUserClientCreator
->getLength();
1141 while ((*ptr
!= ' ') && (lenIter
< len
)) {
1146 if (lenIter
< len
) {
1147 ptr
++; // Skip the space
1150 while ((*ptr
!= ',') && (lenIter
< len
)) {
1151 pid
= pid
* 10 + (*ptr
- '0');
1156 if (lenIter
== len
) {
1168 IORWLockWrite(lock
);
1170 parentKext
= counter
->parentClass
->parentKext
;
1172 TAILQ_FOREACH(entry
, &parentKext
->userClientCallList
, link
) {
1173 if (entry
->pid
== pid
) {
1174 /* Found, so increment count and move to the head */
1177 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1180 /* At the head already, so increment and return */
1189 if (count
== IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
) {
1190 /* Max elements hit, so reuse the last */
1191 entry
= TAILQ_LAST(&parentKext
->userClientCallList
, ProcessEntryList
);
1192 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1194 /* Otherwise, allocate a new entry */
1195 entry
= (IOUserClientProcessEntry
*)kalloc(sizeof(IOUserClientProcessEntry
));
1197 IORWLockUnlock(lock
);
1202 strncpy(entry
->processName
, ptr
, kIOStatisticsProcessNameLength
);
1207 TAILQ_FOREACH(nextEntry
, &parentKext
->userClientCallList
, link
) {
1208 if (nextEntry
->calls
<= entry
->calls
) {
1212 prevEntry
= nextEntry
;
1216 TAILQ_INSERT_HEAD(&parentKext
->userClientCallList
, entry
, link
);
1218 TAILQ_INSERT_AFTER(&parentKext
->userClientCallList
, prevEntry
, entry
, link
);
1222 IORWLockUnlock(lock
);
1231 IOStatistics::countUserClientCall(IOUserClient
*client
)
1233 IOUserClient::ExpansionData
*data
;
1234 IOUserClientCounter
*counter
;
1236 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1237 if (!(data
= client
->reserved
)) {
1241 if ((counter
= data
->counter
)) {
1242 storeUserClientCallInfo(client
, counter
);
1243 OSIncrementAtomic(&counter
->clientCalls
);
1248 IOStatistics::getKextNodeFromBacktrace(boolean_t write
)
1250 const uint32_t btMin
= 3;
1253 unsigned btCount
= sizeof(bt
) / sizeof(bt
[0]);
1254 vm_offset_t
*scanAddr
= NULL
;
1256 KextNode
*found
= NULL
, *ke
= NULL
;
1259 * Gathering the backtrace is a significant source of
1260 * overhead. OSBacktrace does many safety checks that
1261 * are not needed in this situation.
1263 btCount
= backtrace((uintptr_t*)bt
, btCount
, NULL
);
1266 IORWLockWrite(lock
);
1271 /* Ignore first levels */
1272 scanAddr
= (vm_offset_t
*)&bt
[btMin
- 1];
1274 for (i
= btMin
- 1; i
< btCount
; i
++, scanAddr
++) {
1275 ke
= RB_ROOT(&kextAddressHead
);
1277 if (*scanAddr
< ke
->address
) {
1278 ke
= RB_LEFT(ke
, addressLink
);
1280 if ((*scanAddr
< ke
->address_end
) && (*scanAddr
>= ke
->address
)) {
1281 if (!ke
->kext
->isKernelComponent()) {
1287 ke
= RB_RIGHT(ke
, addressLink
);
1293 IORWLockUnlock(lock
);
1300 IOStatistics::releaseKextNode(KextNode
*node
)
1302 #pragma unused(node)
1303 IORWLockUnlock(lock
);
1306 /* IOLib allocations */
1308 IOStatistics::countAlloc(uint32_t index
, vm_size_t size
)
1316 ke
= getKextNodeFromBacktrace(FALSE
);
1318 OSAddAtomic(size
, &ke
->memoryCounters
[index
]);
1319 releaseKextNode(ke
);
1323 #endif /* IOKITSTATS */