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/host.h>
32 #include <IOKit/system.h>
33 #include <libkern/c++/OSKext.h>
34 #include <libkern/OSAtomic.h>
36 #include <IOKit/IOStatisticsPrivate.h>
37 #include <IOKit/IOUserClient.h>
38 #include <IOKit/IOEventSource.h>
39 #include <IOKit/IOKitDebug.h>
43 bool IOStatistics::enabled
= false;
45 uint32_t IOStatistics::sequenceID
= 0;
47 uint32_t IOStatistics::lastClassIndex
= 0;
48 uint32_t IOStatistics::lastKextIndex
= 0;
50 uint32_t IOStatistics::loadedKexts
= 0;
51 uint32_t IOStatistics::registeredClasses
= 0;
52 uint32_t IOStatistics::registeredCounters
= 0;
53 uint32_t IOStatistics::registeredWorkloops
= 0;
55 uint32_t IOStatistics::attachedEventSources
= 0;
57 IOWorkLoopDependency
*IOStatistics::nextWorkLoopDependency
= NULL
;
63 #define LOG(level, format, ...) \
65 if (level <= LOG_LEVEL) \
66 printf(format, ##__VA_ARGS__); \
71 IORWLock
*IOStatistics::lock
= NULL
;
75 KextNode
*IOStatistics::kextHint
= NULL
;
77 IOStatistics::KextTreeHead
IOStatistics::kextHead
= RB_INITIALIZER(&IOStatistics::kextHead
);
79 int IOStatistics::kextNodeCompare(KextNode
*e1
, KextNode
*e2
)
81 if (e1
->kext
< e2
->kext
)
83 else if (e1
->kext
> e2
->kext
)
89 RB_GENERATE(IOStatistics::KextTree
, KextNode
, link
, kextNodeCompare
);
91 /* Kext tree ordered by address */
93 IOStatistics::KextAddressTreeHead
IOStatistics::kextAddressHead
= RB_INITIALIZER(&IOStatistics::kextAddressHead
);
95 int IOStatistics::kextAddressNodeCompare(KextNode
*e1
, KextNode
*e2
)
97 if (e1
->address
< e2
->address
)
99 else if (e1
->address
> e2
->address
)
105 RB_GENERATE(IOStatistics::KextAddressTree
, KextNode
, addressLink
, kextAddressNodeCompare
);
109 IOStatistics::ClassTreeHead
IOStatistics::classHead
= RB_INITIALIZER(&IOStatistics::classHead
);
111 int IOStatistics::classNodeCompare(ClassNode
*e1
, ClassNode
*e2
) {
112 if (e1
->metaClass
< e2
->metaClass
)
114 else if (e1
->metaClass
> e2
->metaClass
)
120 RB_GENERATE(IOStatistics::ClassTree
, ClassNode
, tLink
, classNodeCompare
);
122 /* Workloop dependencies */
124 int IOWorkLoopCounter::loadTagCompare(IOWorkLoopDependency
*e1
, IOWorkLoopDependency
*e2
) {
125 if (e1
->loadTag
< e2
->loadTag
)
127 else if (e1
->loadTag
> e2
->loadTag
)
133 RB_GENERATE(IOWorkLoopCounter::DependencyTree
, IOWorkLoopDependency
, link
, IOWorkLoopCounter::loadTagCompare
);
138 oid_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, int arg2
, struct sysctl_req
*req
)
141 uint32_t request
= arg2
;
145 case kIOStatisticsGeneral
:
146 error
= IOStatistics::getStatistics(req
);
148 case kIOStatisticsWorkLoop
:
149 error
= IOStatistics::getWorkLoopStatistics(req
);
151 case kIOStatisticsUserClient
:
152 error
= IOStatistics::getUserClientStatistics(req
);
161 SYSCTL_NODE(_debug
, OID_AUTO
, iokit_statistics
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "IOStatistics");
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", "");
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", "");
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", "");
175 void IOStatistics::initialize()
181 #if DEVELOPMENT || DEBUG
182 /* Always enabled in development and debug builds. */
184 /* Only enabled in release builds if the boot argument is set. */
185 if (!(kIOStatistics
& gIOKitDebug
)) {
190 sysctl_register_oid(&sysctl__debug_iokit_statistics_general
);
191 sysctl_register_oid(&sysctl__debug_iokit_statistics_workloop
);
192 sysctl_register_oid(&sysctl__debug_iokit_statistics_userclient
);
194 lock
= IORWLockAlloc();
199 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
200 if (!nextWorkLoopDependency
) {
207 void IOStatistics::onKextLoad(OSKext
*kext
, kmod_info_t
*kmod_info
)
211 assert(kext
&& kmod_info
);
217 LOG(1, "IOStatistics::onKextLoad: %s, tag %d, address 0x%llx, address end 0x%llx\n",
218 kext
->getIdentifierCString(), kmod_info
->id
, (uint64_t)kmod_info
->address
, (uint64_t)(kmod_info
->address
+ kmod_info
->size
));
220 ke
= (KextNode
*)kalloc(sizeof(KextNode
));
225 memset(ke
, 0, sizeof(KextNode
));
228 ke
->loadTag
= kmod_info
->id
;
229 ke
->address
= kmod_info
->address
;
230 ke
->address_end
= kmod_info
->address
+ kmod_info
->size
;
232 SLIST_INIT(&ke
->classList
);
233 TAILQ_INIT(&ke
->userClientCallList
);
237 RB_INSERT(KextTree
, &kextHead
, ke
);
238 RB_INSERT(KextAddressTree
, &kextAddressHead
, ke
);
244 IORWLockUnlock(lock
);
247 void IOStatistics::onKextUnload(OSKext
*kext
)
249 KextNode sought
, *found
;
257 LOG(1, "IOStatistics::onKextUnload: %s\n", kext
->getIdentifierCString());
262 found
= RB_FIND(KextTree
, &kextHead
, &sought
);
264 IOWorkLoopCounter
*wlc
;
265 IOUserClientProcessEntry
*uce
;
267 /* Free up the list of counters */
268 while ((wlc
= SLIST_FIRST(&found
->workLoopList
))) {
269 SLIST_REMOVE_HEAD(&found
->workLoopList
, link
);
270 kfree(wlc
, sizeof(IOWorkLoopCounter
));
273 /* Free up the user client list */
274 while ((uce
= TAILQ_FIRST(&found
->userClientCallList
))) {
275 TAILQ_REMOVE(&found
->userClientCallList
, uce
, link
);
276 kfree(uce
, sizeof(IOUserClientProcessEntry
));
279 /* Remove from kext trees */
280 RB_REMOVE(KextTree
, &kextHead
, found
);
281 RB_REMOVE(KextAddressTree
, &kextAddressHead
, found
);
284 * Clear a matching kextHint to avoid use after free in
285 * onClassAdded() for a class add after a KEXT unload.
287 if (found
== kextHint
) {
291 /* Finally, free the class node */
292 kfree(found
, sizeof(KextNode
));
298 panic("IOStatistics::onKextUnload: cannot find kext: %s", kext
->getIdentifierCString());
301 IORWLockUnlock(lock
);
304 void IOStatistics::onClassAdded(OSKext
*parentKext
, OSMetaClass
*metaClass
)
307 KextNode soughtKext
, *foundKext
= NULL
;
309 assert(parentKext
&& metaClass
);
315 LOG(1, "IOStatistics::onClassAdded: %s\n", metaClass
->getClassName());
317 ce
= (ClassNode
*)kalloc(sizeof(ClassNode
));
322 memset(ce
, 0, sizeof(ClassNode
));
327 if (kextHint
&& kextHint
->kext
== parentKext
) {
328 foundKext
= kextHint
;
331 soughtKext
.kext
= parentKext
;
332 foundKext
= RB_FIND(KextTree
, &kextHead
, &soughtKext
);
336 ClassNode soughtClass
, *foundClass
= NULL
;
337 const OSMetaClass
*superClass
;
339 ce
->metaClass
= metaClass
;
340 ce
->classID
= lastClassIndex
++;
341 ce
->parentKext
= foundKext
;
343 /* Has superclass? */
344 superClass
= ce
->metaClass
->getSuperClass();
346 soughtClass
.metaClass
= superClass
;
347 foundClass
= RB_FIND(ClassTree
, &classHead
, &soughtClass
);
349 ce
->superClassID
= foundClass
? foundClass
->classID
: (uint32_t)(-1);
351 SLIST_INIT(&ce
->counterList
);
352 SLIST_INIT(&ce
->userClientList
);
354 RB_INSERT(ClassTree
, &classHead
, ce
);
355 SLIST_INSERT_HEAD(&foundKext
->classList
, ce
, lLink
);
357 foundKext
->classes
++;
359 kextHint
= foundKext
;
365 panic("IOStatistics::onClassAdded: cannot find parent kext: %s", parentKext
->getIdentifierCString());
368 IORWLockUnlock(lock
);
371 void IOStatistics::onClassRemoved(OSKext
*parentKext
, OSMetaClass
*metaClass
)
373 ClassNode sought
, *found
;
375 assert(parentKext
&& metaClass
);
381 LOG(1, "IOStatistics::onClassRemoved: %s\n", metaClass
->getClassName());
385 sought
.metaClass
= metaClass
;
386 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
388 IOEventSourceCounter
*esc
;
389 IOUserClientCounter
*ucc
;
391 /* Free up the list of counters */
392 while ((esc
= SLIST_FIRST(&found
->counterList
))) {
393 SLIST_REMOVE_HEAD(&found
->counterList
, link
);
394 kfree(esc
, sizeof(IOEventSourceCounter
));
397 /* Free up the user client list */
398 while ((ucc
= SLIST_FIRST(&found
->userClientList
))) {
399 SLIST_REMOVE_HEAD(&found
->userClientList
, link
);
400 kfree(ucc
, sizeof(IOUserClientCounter
));
403 /* Remove from class tree */
404 RB_REMOVE(ClassTree
, &classHead
, found
);
406 /* Remove from parent */
407 SLIST_REMOVE(&found
->parentKext
->classList
, found
, ClassNode
, lLink
);
409 /* Finally, free the class node */
410 kfree(found
, sizeof(ClassNode
));
416 panic("IOStatistics::onClassRemoved: cannot find class: %s", metaClass
->getClassName());
419 IORWLockUnlock(lock
);
422 IOEventSourceCounter
*IOStatistics::registerEventSource(OSObject
*inOwner
)
424 IOEventSourceCounter
*counter
= NULL
;
425 ClassNode sought
, *found
= NULL
;
426 boolean_t createDummyCounter
= FALSE
;
434 counter
= (IOEventSourceCounter
*)kalloc(sizeof(IOEventSourceCounter
));
439 memset(counter
, 0, sizeof(IOEventSourceCounter
));
443 /* Workaround for <rdar://problem/7158117> - create a dummy counter when inOwner is bad.
444 * We use retainCount here as our best indication that the pointer is awry.
446 if (inOwner
->retainCount
> 0xFFFFFF) {
447 kprintf("IOStatistics::registerEventSource - bad metaclass %p\n", inOwner
);
448 createDummyCounter
= TRUE
;
451 sought
.metaClass
= inOwner
->getMetaClass();
452 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
456 counter
->parentClass
= found
;
457 SLIST_INSERT_HEAD(&found
->counterList
, counter
, link
);
458 registeredCounters
++;
461 if (!(createDummyCounter
|| found
)) {
462 panic("IOStatistics::registerEventSource: cannot find parent class: %s", inOwner
->getMetaClass()->getClassName());
465 IORWLockUnlock(lock
);
470 void IOStatistics::unregisterEventSource(IOEventSourceCounter
*counter
)
478 if (counter
->parentClass
) {
479 SLIST_REMOVE(&counter
->parentClass
->counterList
, counter
, IOEventSourceCounter
, link
);
480 registeredCounters
--;
482 kfree(counter
, sizeof(IOEventSourceCounter
));
484 IORWLockUnlock(lock
);
487 IOWorkLoopCounter
* IOStatistics::registerWorkLoop(IOWorkLoop
*workLoop
)
489 IOWorkLoopCounter
*counter
= NULL
;
498 counter
= (IOWorkLoopCounter
*)kalloc(sizeof(IOWorkLoopCounter
));
503 memset(counter
, 0, sizeof(IOWorkLoopCounter
));
505 found
= getKextNodeFromBacktrace(TRUE
);
507 panic("IOStatistics::registerWorkLoop: cannot find parent kext");
510 counter
->parentKext
= found
;
511 counter
->workLoop
= workLoop
;
512 RB_INIT(&counter
->dependencyHead
);
513 SLIST_INSERT_HEAD(&found
->workLoopList
, counter
, link
);
514 registeredWorkloops
++;
516 releaseKextNode(found
);
521 void IOStatistics::unregisterWorkLoop(IOWorkLoopCounter
*counter
)
529 SLIST_REMOVE(&counter
->parentKext
->workLoopList
, counter
, IOWorkLoopCounter
, link
);
530 kfree(counter
, sizeof(IOWorkLoopCounter
));
531 registeredWorkloops
--;
533 IORWLockUnlock(lock
);
536 IOUserClientCounter
*IOStatistics::registerUserClient(IOUserClient
*userClient
)
538 ClassNode sought
, *found
;
539 IOUserClientCounter
*counter
= NULL
;
547 counter
= (IOUserClientCounter
*)kalloc(sizeof(IOUserClientCounter
));
552 memset(counter
, 0, sizeof(IOUserClientCounter
));
556 sought
.metaClass
= userClient
->getMetaClass();
558 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
560 counter
->parentClass
= found
;
561 SLIST_INSERT_HEAD(&found
->userClientList
, counter
, link
);
564 panic("IOStatistics::registerUserClient: cannot find parent class: %s", sought
.metaClass
->getClassName());
567 IORWLockUnlock(lock
);
572 void IOStatistics::unregisterUserClient(IOUserClientCounter
*counter
)
580 SLIST_REMOVE(&counter
->parentClass
->userClientList
, counter
, IOUserClientCounter
, link
);
581 kfree(counter
, sizeof(IOUserClientCounter
));
583 IORWLockUnlock(lock
);
586 void IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
594 if (!nextWorkLoopDependency
) {
598 attachedEventSources
++;
599 wlc
->attachedEventSources
++;
601 /* Track the kext dependency */
602 nextWorkLoopDependency
->loadTag
= esc
->parentClass
->parentKext
->loadTag
;
603 if (NULL
== RB_INSERT(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, nextWorkLoopDependency
)) {
604 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
607 IORWLockUnlock(lock
);
610 void IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
612 IOWorkLoopDependency sought
, *found
;
620 attachedEventSources
--;
621 wlc
->attachedEventSources
--;
623 sought
.loadTag
= esc
->parentClass
->parentKext
->loadTag
;
625 found
= RB_FIND(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, &sought
);
627 RB_REMOVE(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, found
);
628 kfree(found
, sizeof(IOWorkLoopDependency
));
631 IORWLockUnlock(lock
);
634 int IOStatistics::getStatistics(sysctl_req
*req
)
637 uint32_t calculatedSize
, size
;
639 IOStatisticsHeader
*header
;
641 assert(IOStatistics::enabled
&& req
);
643 IORWLockRead(IOStatistics::lock
);
645 /* Work out how much we need to allocate. IOStatisticsKext is of variable size. */
646 calculatedSize
= sizeof(IOStatisticsHeader
) +
647 sizeof(IOStatisticsGlobal
) +
648 (sizeof(IOStatisticsKext
) * loadedKexts
) + (sizeof(uint32_t) * registeredClasses
) +
649 (sizeof(IOStatisticsMemory
) * loadedKexts
) +
650 (sizeof(IOStatisticsClass
) * registeredClasses
) +
651 (sizeof(IOStatisticsCounter
) * registeredClasses
) +
652 (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
) +
653 (sizeof(IOStatisticsClassName
) * registeredClasses
);
656 if (req
->oldptr
== USER_ADDR_NULL
) {
657 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
662 if (req
->newptr
!= USER_ADDR_NULL
) {
667 buffer
= (char*)kalloc(calculatedSize
);
673 memset(buffer
, 0, calculatedSize
);
677 header
= (IOStatisticsHeader
*)((void*)ptr
);
679 header
->sig
= IOSTATISTICS_SIG
;
680 header
->ver
= IOSTATISTICS_VER
;
682 header
->seq
= sequenceID
;
684 ptr
+= sizeof(IOStatisticsHeader
);
686 /* Global data - seq, timers, interrupts, etc) */
687 header
->globalStatsOffset
= sizeof(IOStatisticsHeader
);
688 size
= copyGlobalStatistics((IOStatisticsGlobal
*)((void*)ptr
));
691 /* Kext statistics */
692 header
->kextStatsOffset
= header
->globalStatsOffset
+ size
;
693 size
= copyKextStatistics((IOStatisticsKext
*)((void*)ptr
));
696 /* Memory allocation info */
697 header
->memoryStatsOffset
= header
->kextStatsOffset
+ size
;
698 size
= copyMemoryStatistics((IOStatisticsMemory
*)((void*)ptr
));
701 /* Class statistics */
702 header
->classStatsOffset
= header
->memoryStatsOffset
+ size
;
703 size
= copyClassStatistics((IOStatisticsClass
*)((void*)ptr
));
706 /* Dynamic class counter data */
707 header
->counterStatsOffset
= header
->classStatsOffset
+ size
;
708 size
= copyCounterStatistics((IOStatisticsCounter
*)((void*)ptr
));
711 /* Kext identifiers */
712 header
->kextIdentifiersOffset
= header
->counterStatsOffset
+ size
;
713 size
= copyKextIdentifiers((IOStatisticsKextIdentifier
*)((void*)ptr
));
717 header
->classNamesOffset
= header
->kextIdentifiersOffset
+ size
;
718 size
= copyClassNames((IOStatisticsClassName
*)ptr
);
721 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
722 calculatedSize
, loadedKexts
, registeredClasses
);
724 assert( (uint32_t)(ptr
- buffer
) == calculatedSize
);
726 error
= SYSCTL_OUT(req
, buffer
, calculatedSize
);
728 kfree(buffer
, calculatedSize
);
731 IORWLockUnlock(IOStatistics::lock
);
735 int IOStatistics::getWorkLoopStatistics(sysctl_req
*req
)
738 uint32_t calculatedSize
, size
;
740 IOStatisticsWorkLoopHeader
*header
;
742 assert(IOStatistics::enabled
&& req
);
744 IORWLockRead(IOStatistics::lock
);
746 /* Approximate how much we need to allocate (worse case estimate) */
747 calculatedSize
= sizeof(IOStatisticsWorkLoop
) * registeredWorkloops
+
748 sizeof(uint32_t) * attachedEventSources
;
751 if (req
->oldptr
== USER_ADDR_NULL
) {
752 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
757 if (req
->newptr
!= USER_ADDR_NULL
) {
762 buffer
= (char*)kalloc(calculatedSize
);
768 header
= (IOStatisticsWorkLoopHeader
*)((void*)buffer
);
770 header
->sig
= IOSTATISTICS_SIG_WORKLOOP
;
771 header
->ver
= IOSTATISTICS_VER
;
773 header
->seq
= sequenceID
;
775 header
->workloopCount
= registeredWorkloops
;
777 size
= copyWorkLoopStatistics(&header
->workLoopStats
);
779 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize
, size
);
781 assert( size
<= calculatedSize
);
783 error
= SYSCTL_OUT(req
, buffer
, size
);
785 kfree(buffer
, calculatedSize
);
788 IORWLockUnlock(IOStatistics::lock
);
792 int IOStatistics::getUserClientStatistics(sysctl_req
*req
)
795 uint32_t calculatedSize
, size
;
797 uint32_t requestedLoadTag
= 0;
798 IOStatisticsUserClientHeader
*header
;
800 assert(IOStatistics::enabled
&& req
);
802 IORWLockRead(IOStatistics::lock
);
804 /* Work out how much we need to allocate */
805 calculatedSize
= sizeof(IOStatisticsUserClientHeader
) +
806 sizeof(IOStatisticsUserClientCall
) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
* loadedKexts
;
809 if (req
->oldptr
== USER_ADDR_NULL
) {
810 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
814 /* Kext request (potentially) valid? */
815 if (!req
->newptr
|| req
->newlen
< sizeof(requestedLoadTag
)) {
820 SYSCTL_IN(req
, &requestedLoadTag
, sizeof(requestedLoadTag
));
822 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag
);
824 buffer
= (char*)kalloc(calculatedSize
);
830 header
= (IOStatisticsUserClientHeader
*)((void*)buffer
);
832 header
->sig
= IOSTATISTICS_SIG_USERCLIENT
;
833 header
->ver
= IOSTATISTICS_VER
;
835 header
->seq
= sequenceID
;
837 header
->processes
= 0;
839 size
= copyUserClientStatistics(header
, requestedLoadTag
);
841 assert((sizeof(IOStatisticsUserClientHeader
) + size
) <= calculatedSize
);
844 error
= SYSCTL_OUT(req
, buffer
, sizeof(IOStatisticsUserClientHeader
) + size
);
850 kfree(buffer
, calculatedSize
);
853 IORWLockUnlock(IOStatistics::lock
);
857 uint32_t IOStatistics::copyGlobalStatistics(IOStatisticsGlobal
*stats
)
859 stats
->kextCount
= loadedKexts
;
860 stats
->classCount
= registeredClasses
;
861 stats
->workloops
= registeredWorkloops
;
863 return sizeof(IOStatisticsGlobal
);
866 uint32_t IOStatistics::copyKextStatistics(IOStatisticsKext
*stats
)
872 RB_FOREACH(ke
, KextTree
, &kextHead
) {
873 stats
->loadTag
= ke
->loadTag
;
874 ke
->kext
->getSizeInfo(&stats
->loadSize
, &stats
->wiredSize
);
876 stats
->classes
= ke
->classes
;
878 /* Append indices of owned classes */
879 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
880 stats
->classIndexes
[index
++] = ce
->classID
;
883 stats
= (IOStatisticsKext
*)((void*)((char*)stats
+ sizeof(IOStatisticsKext
) + (ke
->classes
* sizeof(uint32_t))));
886 return (sizeof(IOStatisticsKext
) * loadedKexts
+ sizeof(uint32_t) * registeredClasses
);
889 uint32_t IOStatistics::copyMemoryStatistics(IOStatisticsMemory
*stats
)
893 RB_FOREACH(ke
, KextTree
, &kextHead
) {
894 stats
->allocatedSize
= ke
->memoryCounters
[kIOStatisticsMalloc
];
895 stats
->freedSize
= ke
->memoryCounters
[kIOStatisticsFree
];
896 stats
->allocatedAlignedSize
= ke
->memoryCounters
[kIOStatisticsMallocAligned
];
897 stats
->freedAlignedSize
= ke
->memoryCounters
[kIOStatisticsFreeAligned
];
898 stats
->allocatedContiguousSize
= ke
->memoryCounters
[kIOStatisticsMallocContiguous
];
899 stats
->freedContiguousSize
= ke
->memoryCounters
[kIOStatisticsFreeContiguous
];
900 stats
->allocatedPageableSize
= ke
->memoryCounters
[kIOStatisticsMallocPageable
];
901 stats
->freedPageableSize
= ke
->memoryCounters
[kIOStatisticsFreePageable
];
905 return (sizeof(IOStatisticsMemory
) * loadedKexts
);
908 uint32_t IOStatistics::copyClassStatistics(IOStatisticsClass
*stats
)
913 RB_FOREACH(ke
, KextTree
, &kextHead
) {
914 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
915 stats
->classID
= ce
->classID
;
916 stats
->superClassID
= ce
->superClassID
;
917 stats
->classSize
= ce
->metaClass
->getClassSize();
923 return sizeof(IOStatisticsClass
) * registeredClasses
;
926 uint32_t IOStatistics::copyCounterStatistics(IOStatisticsCounter
*stats
)
931 RB_FOREACH(ke
, KextTree
, &kextHead
) {
932 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
933 IOUserClientCounter
*userClientCounter
;
934 IOEventSourceCounter
*counter
;
936 stats
->classID
= ce
->classID
;
937 stats
->classInstanceCount
= ce
->metaClass
->getInstanceCount();
939 IOStatisticsUserClients
*uc
= &stats
->userClientStatistics
;
941 /* User client counters */
942 SLIST_FOREACH(userClientCounter
, &ce
->userClientList
, link
) {
943 uc
->clientCalls
+= userClientCounter
->clientCalls
;
947 IOStatisticsInterruptEventSources
*iec
= &stats
->interruptEventSourceStatistics
;
948 IOStatisticsInterruptEventSources
*fiec
= &stats
->filterInterruptEventSourceStatistics
;
949 IOStatisticsTimerEventSources
*tec
= &stats
->timerEventSourceStatistics
;
950 IOStatisticsCommandGates
*cgc
= &stats
->commandGateStatistics
;
951 IOStatisticsCommandQueues
*cqc
= &stats
->commandQueueStatistics
;
952 IOStatisticsDerivedEventSources
*dec
= &stats
->derivedEventSourceStatistics
;
954 /* Event source counters */
955 SLIST_FOREACH(counter
, &ce
->counterList
, link
) {
956 switch (counter
->type
) {
957 case kIOStatisticsInterruptEventSourceCounter
:
959 iec
->produced
+= counter
->u
.interrupt
.produced
;
960 iec
->checksForWork
+= counter
->u
.interrupt
.checksForWork
;
962 case kIOStatisticsFilterInterruptEventSourceCounter
:
964 fiec
->produced
+= counter
->u
.filter
.produced
;
965 fiec
->checksForWork
+= counter
->u
.filter
.checksForWork
;
967 case kIOStatisticsTimerEventSourceCounter
:
969 tec
->timeouts
+= counter
->u
.timer
.timeouts
;
970 tec
->checksForWork
+= counter
->u
.timer
.checksForWork
;
971 tec
->timeOnGate
+= counter
->timeOnGate
;
972 tec
->closeGateCalls
+= counter
->closeGateCalls
;
973 tec
->openGateCalls
+= counter
->openGateCalls
;
975 case kIOStatisticsCommandGateCounter
:
977 cgc
->timeOnGate
+= counter
->timeOnGate
;
978 cgc
->actionCalls
+= counter
->u
.commandGate
.actionCalls
;
980 case kIOStatisticsCommandQueueCounter
:
982 cqc
->actionCalls
+= counter
->u
.commandQueue
.actionCalls
;
984 case kIOStatisticsDerivedEventSourceCounter
:
986 dec
->timeOnGate
+= counter
->timeOnGate
;
987 dec
->closeGateCalls
+= counter
->closeGateCalls
;
988 dec
->openGateCalls
+= counter
->openGateCalls
;
999 return sizeof(IOStatisticsCounter
) * registeredClasses
;
1002 uint32_t IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier
*kextIDs
)
1006 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1007 strncpy(kextIDs
->identifier
, ke
->kext
->getIdentifierCString(), kIOStatisticsDriverNameLength
);
1011 return (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
);
1014 uint32_t IOStatistics::copyClassNames(IOStatisticsClassName
*classNames
)
1019 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1020 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
1021 strncpy(classNames
->name
, ce
->metaClass
->getClassName(), kIOStatisticsClassNameLength
);
1026 return (sizeof(IOStatisticsClassName
) * registeredClasses
);
1029 uint32_t IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop
*stats
)
1032 IOWorkLoopCounter
*wlc
;
1033 IOWorkLoopDependency
*dependentNode
;
1034 uint32_t size
, accumulatedSize
= 0;
1036 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1037 SLIST_FOREACH(wlc
, &ke
->workLoopList
, link
) {
1038 stats
->kextLoadTag
= ke
->loadTag
;
1039 stats
->attachedEventSources
= wlc
->attachedEventSources
;
1040 stats
->timeOnGate
= wlc
->timeOnGate
;
1041 stats
->dependentKexts
= 0;
1042 RB_FOREACH(dependentNode
, IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
) {
1043 stats
->dependentKextLoadTags
[stats
->dependentKexts
] = dependentNode
->loadTag
;
1044 stats
->dependentKexts
++;
1047 size
= sizeof(IOStatisticsWorkLoop
) + (sizeof(uint32_t) * stats
->dependentKexts
);
1049 accumulatedSize
+= size
;
1050 stats
= (IOStatisticsWorkLoop
*)((void*)((char*)stats
+ size
));
1054 return accumulatedSize
;
1057 uint32_t IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader
*stats
, uint32_t loadTag
)
1059 KextNode
*sought
, *found
= NULL
;
1061 IOUserClientProcessEntry
*processEntry
;
1063 RB_FOREACH(sought
, KextTree
, &kextHead
) {
1064 if (sought
->loadTag
== loadTag
) {
1074 TAILQ_FOREACH(processEntry
, &found
->userClientCallList
, link
) {
1075 strncpy(stats
->userClientCalls
[procs
].processName
, processEntry
->processName
, kIOStatisticsProcessNameLength
);
1076 stats
->userClientCalls
[procs
].pid
= processEntry
->pid
;
1077 stats
->userClientCalls
[procs
].calls
= processEntry
->calls
;
1082 return sizeof(IOStatisticsUserClientCall
) * stats
->processes
;
1085 void IOStatistics::storeUserClientCallInfo(IOUserClient
*userClient
, IOUserClientCounter
*counter
)
1087 OSString
*ossUserClientCreator
= NULL
;
1089 KextNode
*parentKext
;
1090 IOUserClientProcessEntry
*entry
, *nextEntry
, *prevEntry
= NULL
;
1092 const char *ptr
= NULL
;
1095 /* TODO: see if this can be more efficient */
1096 obj
= userClient
->copyProperty("IOUserClientCreator",
1098 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
1103 ossUserClientCreator
= OSDynamicCast(OSString
, obj
);
1105 if (ossUserClientCreator
) {
1106 uint32_t len
, lenIter
= 0;
1108 ptr
= ossUserClientCreator
->getCStringNoCopy();
1109 len
= ossUserClientCreator
->getLength();
1111 while ((*ptr
!= ' ') && (lenIter
< len
)) {
1116 if (lenIter
< len
) {
1117 ptr
++; // Skip the space
1120 while ( (*ptr
!= ',') && (lenIter
< len
)) {
1121 pid
= pid
*10 + (*ptr
- '0');
1126 if(lenIter
== len
) {
1137 IORWLockWrite(lock
);
1139 parentKext
= counter
->parentClass
->parentKext
;
1141 TAILQ_FOREACH(entry
, &parentKext
->userClientCallList
, link
) {
1142 if (entry
->pid
== pid
) {
1143 /* Found, so increment count and move to the head */
1146 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1150 /* At the head already, so increment and return */
1159 if (count
== IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
) {
1160 /* Max elements hit, so reuse the last */
1161 entry
= TAILQ_LAST(&parentKext
->userClientCallList
, ProcessEntryList
);
1162 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1165 /* Otherwise, allocate a new entry */
1166 entry
= (IOUserClientProcessEntry
*)kalloc(sizeof(IOUserClientProcessEntry
));
1168 IORWLockUnlock(lock
);
1173 strncpy(entry
->processName
, ptr
, kIOStatisticsProcessNameLength
);
1178 TAILQ_FOREACH(nextEntry
, &parentKext
->userClientCallList
, link
) {
1179 if (nextEntry
->calls
<= entry
->calls
)
1182 prevEntry
= nextEntry
;
1186 TAILQ_INSERT_HEAD(&parentKext
->userClientCallList
, entry
, link
);
1188 TAILQ_INSERT_AFTER(&parentKext
->userClientCallList
, prevEntry
, entry
, link
);
1191 IORWLockUnlock(lock
);
1198 void IOStatistics::countUserClientCall(IOUserClient
*client
) {
1199 IOUserClient::ExpansionData
*data
;
1200 IOUserClientCounter
*counter
;
1202 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1203 if (!(data
= client
->reserved
)) {
1207 if ((counter
= data
->counter
)) {
1208 storeUserClientCallInfo(client
, counter
);
1209 OSIncrementAtomic(&counter
->clientCalls
);
1213 KextNode
*IOStatistics::getKextNodeFromBacktrace(boolean_t write
) {
1214 const uint32_t btMin
= 3;
1217 unsigned btCount
= sizeof(bt
) / sizeof(bt
[0]);
1218 vm_offset_t
*scanAddr
= NULL
;
1220 KextNode
*found
= NULL
, *ke
= NULL
;
1222 btCount
= OSBacktrace(bt
, btCount
);
1225 IORWLockWrite(lock
);
1230 /* Ignore first levels */
1231 scanAddr
= (vm_offset_t
*)&bt
[btMin
- 1];
1233 for (i
= 0; i
< btCount
; i
++, scanAddr
++) {
1234 ke
= RB_ROOT(&kextAddressHead
);
1236 if (*scanAddr
< ke
->address
) {
1237 ke
= RB_LEFT(ke
, addressLink
);
1240 if ((*scanAddr
< ke
->address_end
) && (*scanAddr
>= ke
->address
)) {
1241 if (!ke
->kext
->isKernelComponent()) {
1247 ke
= RB_RIGHT(ke
, addressLink
);
1253 IORWLockUnlock(lock
);
1259 void IOStatistics::releaseKextNode(KextNode
*node
) {
1260 #pragma unused(node)
1261 IORWLockUnlock(lock
);
1264 /* IOLib allocations */
1265 void IOStatistics::countAlloc(uint32_t index
, vm_size_t size
) {
1272 ke
= getKextNodeFromBacktrace(FALSE
);
1274 OSAddAtomic(size
, &ke
->memoryCounters
[index
]);
1275 releaseKextNode(ke
);
1279 #endif /* IOKITSTATS */