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>
42 bool IOStatistics::enabled
= false;
44 uint32_t IOStatistics::sequenceID
= 0;
46 uint32_t IOStatistics::lastClassIndex
= 0;
47 uint32_t IOStatistics::lastKextIndex
= 0;
49 uint32_t IOStatistics::loadedKexts
= 0;
50 uint32_t IOStatistics::registeredClasses
= 0;
51 uint32_t IOStatistics::registeredCounters
= 0;
52 uint32_t IOStatistics::registeredWorkloops
= 0;
54 uint32_t IOStatistics::attachedEventSources
= 0;
56 IOWorkLoopDependency
*IOStatistics::nextWorkLoopDependency
= NULL
;
62 #define LOG(level, format, ...) \
64 if (level <= LOG_LEVEL) \
65 printf(format, ##__VA_ARGS__); \
70 IORWLock
*IOStatistics::lock
= NULL
;
74 KextNode
*IOStatistics::kextHint
= NULL
;
76 IOStatistics::KextTreeHead
IOStatistics::kextHead
= RB_INITIALIZER(&IOStatistics::kextHead
);
78 int IOStatistics::kextNodeCompare(KextNode
*e1
, KextNode
*e2
)
80 if (e1
->kext
< e2
->kext
)
82 else if (e1
->kext
> e2
->kext
)
88 RB_GENERATE(IOStatistics::KextTree
, KextNode
, link
, kextNodeCompare
);
90 /* Kext tree ordered by address */
92 IOStatistics::KextAddressTreeHead
IOStatistics::kextAddressHead
= RB_INITIALIZER(&IOStatistics::kextAddressHead
);
94 int IOStatistics::kextAddressNodeCompare(KextNode
*e1
, KextNode
*e2
)
96 if (e1
->address
< e2
->address
)
98 else if (e1
->address
> e2
->address
)
104 RB_GENERATE(IOStatistics::KextAddressTree
, KextNode
, addressLink
, kextAddressNodeCompare
);
108 IOStatistics::ClassTreeHead
IOStatistics::classHead
= RB_INITIALIZER(&IOStatistics::classHead
);
110 int IOStatistics::classNodeCompare(ClassNode
*e1
, ClassNode
*e2
) {
111 if (e1
->metaClass
< e2
->metaClass
)
113 else if (e1
->metaClass
> e2
->metaClass
)
119 RB_GENERATE(IOStatistics::ClassTree
, ClassNode
, tLink
, classNodeCompare
);
121 /* Workloop dependencies */
123 int IOWorkLoopCounter::loadTagCompare(IOWorkLoopDependency
*e1
, IOWorkLoopDependency
*e2
) {
124 if (e1
->loadTag
< e2
->loadTag
)
126 else if (e1
->loadTag
> e2
->loadTag
)
132 RB_GENERATE(IOWorkLoopCounter::DependencyTree
, IOWorkLoopDependency
, link
, IOWorkLoopCounter::loadTagCompare
);
137 oid_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, int arg2
, struct sysctl_req
*req
)
140 uint32_t request
= arg2
;
144 case kIOStatisticsGeneral
:
145 error
= IOStatistics::getStatistics(req
);
147 case kIOStatisticsWorkLoop
:
148 error
= IOStatistics::getWorkLoopStatistics(req
);
150 case kIOStatisticsUserClient
:
151 error
= IOStatistics::getUserClientStatistics(req
);
160 SYSCTL_NODE(_debug
, OID_AUTO
, iokit_statistics
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "IOStatistics");
162 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, general
,
163 CTLTYPE_STRUCT
| CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
164 0, kIOStatisticsGeneral
, oid_sysctl
, "S", "");
166 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, workloop
,
167 CTLTYPE_STRUCT
| CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
168 0, kIOStatisticsWorkLoop
, oid_sysctl
, "S", "");
170 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, userclient
,
171 CTLTYPE_STRUCT
| CTLFLAG_RW
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
172 0, kIOStatisticsUserClient
, oid_sysctl
, "S", "");
174 void IOStatistics::initialize()
180 /* Only enabled if the boot argument is set. */
181 if (!(kIOStatistics
& gIOKitDebug
)) {
185 sysctl_register_oid(&sysctl__debug_iokit_statistics_general
);
186 sysctl_register_oid(&sysctl__debug_iokit_statistics_workloop
);
187 sysctl_register_oid(&sysctl__debug_iokit_statistics_userclient
);
189 lock
= IORWLockAlloc();
194 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
195 if (!nextWorkLoopDependency
) {
202 void IOStatistics::onKextLoad(OSKext
*kext
, kmod_info_t
*kmod_info
)
206 assert(kext
&& kmod_info
);
212 LOG(1, "IOStatistics::onKextLoad: %s, tag %d, address 0x%llx, address end 0x%llx\n",
213 kext
->getIdentifierCString(), kmod_info
->id
, (uint64_t)kmod_info
->address
, (uint64_t)(kmod_info
->address
+ kmod_info
->size
));
215 ke
= (KextNode
*)kalloc(sizeof(KextNode
));
220 memset(ke
, 0, sizeof(KextNode
));
223 ke
->loadTag
= kmod_info
->id
;
224 ke
->address
= kmod_info
->address
;
225 ke
->address_end
= kmod_info
->address
+ kmod_info
->size
;
227 SLIST_INIT(&ke
->classList
);
228 TAILQ_INIT(&ke
->userClientCallList
);
232 RB_INSERT(KextTree
, &kextHead
, ke
);
233 RB_INSERT(KextAddressTree
, &kextAddressHead
, ke
);
239 IORWLockUnlock(lock
);
242 void IOStatistics::onKextUnload(OSKext
*kext
)
244 KextNode sought
, *found
;
252 LOG(1, "IOStatistics::onKextUnload: %s\n", kext
->getIdentifierCString());
257 found
= RB_FIND(KextTree
, &kextHead
, &sought
);
259 IOWorkLoopCounter
*wlc
;
260 IOUserClientProcessEntry
*uce
;
262 /* Free up the list of counters */
263 while ((wlc
= SLIST_FIRST(&found
->workLoopList
))) {
264 SLIST_REMOVE_HEAD(&found
->workLoopList
, link
);
265 kfree(wlc
, sizeof(IOWorkLoopCounter
));
268 /* Free up the user client list */
269 while ((uce
= TAILQ_FIRST(&found
->userClientCallList
))) {
270 TAILQ_REMOVE(&found
->userClientCallList
, uce
, link
);
271 kfree(uce
, sizeof(IOUserClientProcessEntry
));
274 /* Remove from kext trees */
275 RB_REMOVE(KextTree
, &kextHead
, found
);
276 RB_REMOVE(KextAddressTree
, &kextAddressHead
, found
);
279 * Clear a matching kextHint to avoid use after free in
280 * onClassAdded() for a class add after a KEXT unload.
282 if (found
== kextHint
) {
286 /* Finally, free the class node */
287 kfree(found
, sizeof(KextNode
));
293 panic("IOStatistics::onKextUnload: cannot find kext: %s", kext
->getIdentifierCString());
296 IORWLockUnlock(lock
);
299 void IOStatistics::onClassAdded(OSKext
*parentKext
, OSMetaClass
*metaClass
)
302 KextNode soughtKext
, *foundKext
= NULL
;
304 assert(parentKext
&& metaClass
);
310 LOG(1, "IOStatistics::onClassAdded: %s\n", metaClass
->getClassName());
312 ce
= (ClassNode
*)kalloc(sizeof(ClassNode
));
317 memset(ce
, 0, sizeof(ClassNode
));
322 if (kextHint
&& kextHint
->kext
== parentKext
) {
323 foundKext
= kextHint
;
326 soughtKext
.kext
= parentKext
;
327 foundKext
= RB_FIND(KextTree
, &kextHead
, &soughtKext
);
331 ClassNode soughtClass
, *foundClass
= NULL
;
332 const OSMetaClass
*superClass
;
334 ce
->metaClass
= metaClass
;
335 ce
->classID
= lastClassIndex
++;
336 ce
->parentKext
= foundKext
;
338 /* Has superclass? */
339 superClass
= ce
->metaClass
->getSuperClass();
341 soughtClass
.metaClass
= superClass
;
342 foundClass
= RB_FIND(ClassTree
, &classHead
, &soughtClass
);
344 ce
->superClassID
= foundClass
? foundClass
->classID
: (uint32_t)(-1);
346 SLIST_INIT(&ce
->counterList
);
347 SLIST_INIT(&ce
->userClientList
);
349 RB_INSERT(ClassTree
, &classHead
, ce
);
350 SLIST_INSERT_HEAD(&foundKext
->classList
, ce
, lLink
);
352 foundKext
->classes
++;
354 kextHint
= foundKext
;
360 panic("IOStatistics::onClassAdded: cannot find parent kext: %s", parentKext
->getIdentifierCString());
363 IORWLockUnlock(lock
);
366 void IOStatistics::onClassRemoved(OSKext
*parentKext
, OSMetaClass
*metaClass
)
368 ClassNode sought
, *found
;
370 assert(parentKext
&& metaClass
);
376 LOG(1, "IOStatistics::onClassRemoved: %s\n", metaClass
->getClassName());
380 sought
.metaClass
= metaClass
;
381 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
383 IOEventSourceCounter
*esc
;
384 IOUserClientCounter
*ucc
;
386 /* Free up the list of counters */
387 while ((esc
= SLIST_FIRST(&found
->counterList
))) {
388 SLIST_REMOVE_HEAD(&found
->counterList
, link
);
389 kfree(esc
, sizeof(IOEventSourceCounter
));
392 /* Free up the user client list */
393 while ((ucc
= SLIST_FIRST(&found
->userClientList
))) {
394 SLIST_REMOVE_HEAD(&found
->userClientList
, link
);
395 kfree(ucc
, sizeof(IOUserClientCounter
));
398 /* Remove from class tree */
399 RB_REMOVE(ClassTree
, &classHead
, found
);
401 /* Remove from parent */
402 SLIST_REMOVE(&found
->parentKext
->classList
, found
, ClassNode
, lLink
);
404 /* Finally, free the class node */
405 kfree(found
, sizeof(ClassNode
));
411 panic("IOStatistics::onClassRemoved: cannot find class: %s", metaClass
->getClassName());
414 IORWLockUnlock(lock
);
417 IOEventSourceCounter
*IOStatistics::registerEventSource(OSObject
*inOwner
)
419 IOEventSourceCounter
*counter
= NULL
;
420 ClassNode sought
, *found
= NULL
;
421 boolean_t createDummyCounter
= FALSE
;
429 counter
= (IOEventSourceCounter
*)kalloc(sizeof(IOEventSourceCounter
));
434 memset(counter
, 0, sizeof(IOEventSourceCounter
));
438 /* Workaround for <rdar://problem/7158117> - create a dummy counter when inOwner is bad.
439 * We use retainCount here as our best indication that the pointer is awry.
441 if (inOwner
->retainCount
> 0xFFFFFF) {
442 kprintf("IOStatistics::registerEventSource - bad metaclass %p\n", inOwner
);
443 createDummyCounter
= TRUE
;
446 sought
.metaClass
= inOwner
->getMetaClass();
447 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
451 counter
->parentClass
= found
;
452 SLIST_INSERT_HEAD(&found
->counterList
, counter
, link
);
453 registeredCounters
++;
456 if (!(createDummyCounter
|| found
)) {
457 panic("IOStatistics::registerEventSource: cannot find parent class: %s", inOwner
->getMetaClass()->getClassName());
460 IORWLockUnlock(lock
);
465 void IOStatistics::unregisterEventSource(IOEventSourceCounter
*counter
)
473 if (counter
->parentClass
) {
474 SLIST_REMOVE(&counter
->parentClass
->counterList
, counter
, IOEventSourceCounter
, link
);
475 registeredCounters
--;
477 kfree(counter
, sizeof(IOEventSourceCounter
));
479 IORWLockUnlock(lock
);
482 IOWorkLoopCounter
* IOStatistics::registerWorkLoop(IOWorkLoop
*workLoop
)
484 IOWorkLoopCounter
*counter
= NULL
;
493 counter
= (IOWorkLoopCounter
*)kalloc(sizeof(IOWorkLoopCounter
));
498 memset(counter
, 0, sizeof(IOWorkLoopCounter
));
500 found
= getKextNodeFromBacktrace(TRUE
);
502 panic("IOStatistics::registerWorkLoop: cannot find parent kext");
505 counter
->parentKext
= found
;
506 counter
->workLoop
= workLoop
;
507 RB_INIT(&counter
->dependencyHead
);
508 SLIST_INSERT_HEAD(&found
->workLoopList
, counter
, link
);
509 registeredWorkloops
++;
511 releaseKextNode(found
);
516 void IOStatistics::unregisterWorkLoop(IOWorkLoopCounter
*counter
)
524 SLIST_REMOVE(&counter
->parentKext
->workLoopList
, counter
, IOWorkLoopCounter
, link
);
525 kfree(counter
, sizeof(IOWorkLoopCounter
));
526 registeredWorkloops
--;
528 IORWLockUnlock(lock
);
531 IOUserClientCounter
*IOStatistics::registerUserClient(IOUserClient
*userClient
)
533 ClassNode sought
, *found
;
534 IOUserClientCounter
*counter
= NULL
;
542 counter
= (IOUserClientCounter
*)kalloc(sizeof(IOUserClientCounter
));
547 memset(counter
, 0, sizeof(IOUserClientCounter
));
551 sought
.metaClass
= userClient
->getMetaClass();
553 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
555 counter
->parentClass
= found
;
556 SLIST_INSERT_HEAD(&found
->userClientList
, counter
, link
);
559 panic("IOStatistics::registerUserClient: cannot find parent class: %s", sought
.metaClass
->getClassName());
562 IORWLockUnlock(lock
);
567 void IOStatistics::unregisterUserClient(IOUserClientCounter
*counter
)
575 SLIST_REMOVE(&counter
->parentClass
->userClientList
, counter
, IOUserClientCounter
, link
);
576 kfree(counter
, sizeof(IOUserClientCounter
));
578 IORWLockUnlock(lock
);
581 void IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
589 if (!nextWorkLoopDependency
) {
593 attachedEventSources
++;
594 wlc
->attachedEventSources
++;
596 /* Track the kext dependency */
597 nextWorkLoopDependency
->loadTag
= esc
->parentClass
->parentKext
->loadTag
;
598 if (NULL
== RB_INSERT(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, nextWorkLoopDependency
)) {
599 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
602 IORWLockUnlock(lock
);
605 void IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
607 IOWorkLoopDependency sought
, *found
;
615 attachedEventSources
--;
616 wlc
->attachedEventSources
--;
618 sought
.loadTag
= esc
->parentClass
->parentKext
->loadTag
;
620 found
= RB_FIND(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, &sought
);
622 RB_REMOVE(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, found
);
623 kfree(found
, sizeof(IOWorkLoopDependency
));
626 IORWLockUnlock(lock
);
629 int IOStatistics::getStatistics(sysctl_req
*req
)
632 uint32_t calculatedSize
, size
;
634 IOStatisticsHeader
*header
;
636 assert(IOStatistics::enabled
&& req
);
638 IORWLockRead(IOStatistics::lock
);
640 /* Work out how much we need to allocate. IOStatisticsKext is of variable size. */
641 calculatedSize
= sizeof(IOStatisticsHeader
) +
642 sizeof(IOStatisticsGlobal
) +
643 (sizeof(IOStatisticsKext
) * loadedKexts
) + (sizeof(uint32_t) * registeredClasses
) +
644 (sizeof(IOStatisticsMemory
) * loadedKexts
) +
645 (sizeof(IOStatisticsClass
) * registeredClasses
) +
646 (sizeof(IOStatisticsCounter
) * registeredClasses
) +
647 (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
) +
648 (sizeof(IOStatisticsClassName
) * registeredClasses
);
651 if (req
->oldptr
== USER_ADDR_NULL
) {
652 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
657 if (req
->newptr
!= USER_ADDR_NULL
) {
662 buffer
= (char*)kalloc(calculatedSize
);
668 memset(buffer
, 0, calculatedSize
);
672 header
= (IOStatisticsHeader
*)((void*)ptr
);
674 header
->sig
= IOSTATISTICS_SIG
;
675 header
->ver
= IOSTATISTICS_VER
;
677 header
->seq
= sequenceID
;
679 ptr
+= sizeof(IOStatisticsHeader
);
681 /* Global data - seq, timers, interrupts, etc) */
682 header
->globalStatsOffset
= sizeof(IOStatisticsHeader
);
683 size
= copyGlobalStatistics((IOStatisticsGlobal
*)((void*)ptr
));
686 /* Kext statistics */
687 header
->kextStatsOffset
= header
->globalStatsOffset
+ size
;
688 size
= copyKextStatistics((IOStatisticsKext
*)((void*)ptr
));
691 /* Memory allocation info */
692 header
->memoryStatsOffset
= header
->kextStatsOffset
+ size
;
693 size
= copyMemoryStatistics((IOStatisticsMemory
*)((void*)ptr
));
696 /* Class statistics */
697 header
->classStatsOffset
= header
->memoryStatsOffset
+ size
;
698 size
= copyClassStatistics((IOStatisticsClass
*)((void*)ptr
));
701 /* Dynamic class counter data */
702 header
->counterStatsOffset
= header
->classStatsOffset
+ size
;
703 size
= copyCounterStatistics((IOStatisticsCounter
*)((void*)ptr
));
706 /* Kext identifiers */
707 header
->kextIdentifiersOffset
= header
->counterStatsOffset
+ size
;
708 size
= copyKextIdentifiers((IOStatisticsKextIdentifier
*)((void*)ptr
));
712 header
->classNamesOffset
= header
->kextIdentifiersOffset
+ size
;
713 size
= copyClassNames((IOStatisticsClassName
*)ptr
);
716 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
717 calculatedSize
, loadedKexts
, registeredClasses
);
719 assert( (uint32_t)(ptr
- buffer
) == calculatedSize
);
721 error
= SYSCTL_OUT(req
, buffer
, calculatedSize
);
723 kfree(buffer
, calculatedSize
);
726 IORWLockUnlock(IOStatistics::lock
);
730 int IOStatistics::getWorkLoopStatistics(sysctl_req
*req
)
733 uint32_t calculatedSize
, size
;
735 IOStatisticsWorkLoopHeader
*header
;
737 assert(IOStatistics::enabled
&& req
);
739 IORWLockRead(IOStatistics::lock
);
741 /* Approximate how much we need to allocate (worse case estimate) */
742 calculatedSize
= sizeof(IOStatisticsWorkLoop
) * registeredWorkloops
+
743 sizeof(uint32_t) * attachedEventSources
;
746 if (req
->oldptr
== USER_ADDR_NULL
) {
747 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
752 if (req
->newptr
!= USER_ADDR_NULL
) {
757 buffer
= (char*)kalloc(calculatedSize
);
763 header
= (IOStatisticsWorkLoopHeader
*)((void*)buffer
);
765 header
->sig
= IOSTATISTICS_SIG_WORKLOOP
;
766 header
->ver
= IOSTATISTICS_VER
;
768 header
->seq
= sequenceID
;
770 header
->workloopCount
= registeredWorkloops
;
772 size
= copyWorkLoopStatistics(&header
->workLoopStats
);
774 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize
, size
);
776 assert( size
<= calculatedSize
);
778 error
= SYSCTL_OUT(req
, buffer
, size
);
780 kfree(buffer
, calculatedSize
);
783 IORWLockUnlock(IOStatistics::lock
);
787 int IOStatistics::getUserClientStatistics(sysctl_req
*req
)
790 uint32_t calculatedSize
, size
;
792 uint32_t requestedLoadTag
= 0;
793 IOStatisticsUserClientHeader
*header
;
795 assert(IOStatistics::enabled
&& req
);
797 IORWLockRead(IOStatistics::lock
);
799 /* Work out how much we need to allocate */
800 calculatedSize
= sizeof(IOStatisticsUserClientHeader
) +
801 sizeof(IOStatisticsUserClientCall
) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
* loadedKexts
;
804 if (req
->oldptr
== USER_ADDR_NULL
) {
805 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
809 /* Kext request (potentially) valid? */
810 if (!req
->newptr
|| req
->newlen
< sizeof(requestedLoadTag
)) {
815 SYSCTL_IN(req
, &requestedLoadTag
, sizeof(requestedLoadTag
));
817 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag
);
819 buffer
= (char*)kalloc(calculatedSize
);
825 header
= (IOStatisticsUserClientHeader
*)((void*)buffer
);
827 header
->sig
= IOSTATISTICS_SIG_USERCLIENT
;
828 header
->ver
= IOSTATISTICS_VER
;
830 header
->seq
= sequenceID
;
832 header
->processes
= 0;
834 size
= copyUserClientStatistics(header
, requestedLoadTag
);
836 assert((sizeof(IOStatisticsUserClientHeader
) + size
) <= calculatedSize
);
839 error
= SYSCTL_OUT(req
, buffer
, sizeof(IOStatisticsUserClientHeader
) + size
);
845 kfree(buffer
, calculatedSize
);
848 IORWLockUnlock(IOStatistics::lock
);
852 uint32_t IOStatistics::copyGlobalStatistics(IOStatisticsGlobal
*stats
)
854 stats
->kextCount
= loadedKexts
;
855 stats
->classCount
= registeredClasses
;
856 stats
->workloops
= registeredWorkloops
;
858 return sizeof(IOStatisticsGlobal
);
861 uint32_t IOStatistics::copyKextStatistics(IOStatisticsKext
*stats
)
867 RB_FOREACH(ke
, KextTree
, &kextHead
) {
868 stats
->loadTag
= ke
->loadTag
;
869 ke
->kext
->getSizeInfo(&stats
->loadSize
, &stats
->wiredSize
);
871 stats
->classes
= ke
->classes
;
873 /* Append indices of owned classes */
874 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
875 stats
->classIndexes
[index
++] = ce
->classID
;
878 stats
= (IOStatisticsKext
*)((void*)((char*)stats
+ sizeof(IOStatisticsKext
) + (ke
->classes
* sizeof(uint32_t))));
881 return (sizeof(IOStatisticsKext
) * loadedKexts
+ sizeof(uint32_t) * registeredClasses
);
884 uint32_t IOStatistics::copyMemoryStatistics(IOStatisticsMemory
*stats
)
888 RB_FOREACH(ke
, KextTree
, &kextHead
) {
889 stats
->allocatedSize
= ke
->memoryCounters
[kIOStatisticsMalloc
];
890 stats
->freedSize
= ke
->memoryCounters
[kIOStatisticsFree
];
891 stats
->allocatedAlignedSize
= ke
->memoryCounters
[kIOStatisticsMallocAligned
];
892 stats
->freedAlignedSize
= ke
->memoryCounters
[kIOStatisticsFreeAligned
];
893 stats
->allocatedContiguousSize
= ke
->memoryCounters
[kIOStatisticsMallocContiguous
];
894 stats
->freedContiguousSize
= ke
->memoryCounters
[kIOStatisticsFreeContiguous
];
895 stats
->allocatedPageableSize
= ke
->memoryCounters
[kIOStatisticsMallocPageable
];
896 stats
->freedPageableSize
= ke
->memoryCounters
[kIOStatisticsFreePageable
];
900 return (sizeof(IOStatisticsMemory
) * loadedKexts
);
903 uint32_t IOStatistics::copyClassStatistics(IOStatisticsClass
*stats
)
908 RB_FOREACH(ke
, KextTree
, &kextHead
) {
909 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
910 stats
->classID
= ce
->classID
;
911 stats
->superClassID
= ce
->superClassID
;
912 stats
->classSize
= ce
->metaClass
->getClassSize();
918 return sizeof(IOStatisticsClass
) * registeredClasses
;
921 uint32_t IOStatistics::copyCounterStatistics(IOStatisticsCounter
*stats
)
926 RB_FOREACH(ke
, KextTree
, &kextHead
) {
927 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
928 IOUserClientCounter
*userClientCounter
;
929 IOEventSourceCounter
*counter
;
931 stats
->classID
= ce
->classID
;
932 stats
->classInstanceCount
= ce
->metaClass
->getInstanceCount();
934 IOStatisticsUserClients
*uc
= &stats
->userClientStatistics
;
936 /* User client counters */
937 SLIST_FOREACH(userClientCounter
, &ce
->userClientList
, link
) {
938 uc
->clientCalls
+= userClientCounter
->clientCalls
;
942 IOStatisticsInterruptEventSources
*iec
= &stats
->interruptEventSourceStatistics
;
943 IOStatisticsInterruptEventSources
*fiec
= &stats
->filterInterruptEventSourceStatistics
;
944 IOStatisticsTimerEventSources
*tec
= &stats
->timerEventSourceStatistics
;
945 IOStatisticsCommandGates
*cgc
= &stats
->commandGateStatistics
;
946 IOStatisticsCommandQueues
*cqc
= &stats
->commandQueueStatistics
;
947 IOStatisticsDerivedEventSources
*dec
= &stats
->derivedEventSourceStatistics
;
949 /* Event source counters */
950 SLIST_FOREACH(counter
, &ce
->counterList
, link
) {
951 switch (counter
->type
) {
952 case kIOStatisticsInterruptEventSourceCounter
:
954 iec
->produced
+= counter
->u
.interrupt
.produced
;
955 iec
->checksForWork
+= counter
->u
.interrupt
.checksForWork
;
957 case kIOStatisticsFilterInterruptEventSourceCounter
:
959 fiec
->produced
+= counter
->u
.filter
.produced
;
960 fiec
->checksForWork
+= counter
->u
.filter
.checksForWork
;
962 case kIOStatisticsTimerEventSourceCounter
:
964 tec
->timeouts
+= counter
->u
.timer
.timeouts
;
965 tec
->checksForWork
+= counter
->u
.timer
.checksForWork
;
966 tec
->timeOnGate
+= counter
->timeOnGate
;
967 tec
->closeGateCalls
+= counter
->closeGateCalls
;
968 tec
->openGateCalls
+= counter
->openGateCalls
;
970 case kIOStatisticsCommandGateCounter
:
972 cgc
->timeOnGate
+= counter
->timeOnGate
;
973 cgc
->actionCalls
+= counter
->u
.commandGate
.actionCalls
;
975 case kIOStatisticsCommandQueueCounter
:
977 cqc
->actionCalls
+= counter
->u
.commandQueue
.actionCalls
;
979 case kIOStatisticsDerivedEventSourceCounter
:
981 dec
->timeOnGate
+= counter
->timeOnGate
;
982 dec
->closeGateCalls
+= counter
->closeGateCalls
;
983 dec
->openGateCalls
+= counter
->openGateCalls
;
994 return sizeof(IOStatisticsCounter
) * registeredClasses
;
997 uint32_t IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier
*kextIDs
)
1001 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1002 strncpy(kextIDs
->identifier
, ke
->kext
->getIdentifierCString(), kIOStatisticsDriverNameLength
);
1006 return (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
);
1009 uint32_t IOStatistics::copyClassNames(IOStatisticsClassName
*classNames
)
1014 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1015 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
1016 strncpy(classNames
->name
, ce
->metaClass
->getClassName(), kIOStatisticsClassNameLength
);
1021 return (sizeof(IOStatisticsClassName
) * registeredClasses
);
1024 uint32_t IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop
*stats
)
1027 IOWorkLoopCounter
*wlc
;
1028 IOWorkLoopDependency
*dependentNode
;
1029 uint32_t size
, accumulatedSize
= 0;
1031 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1032 SLIST_FOREACH(wlc
, &ke
->workLoopList
, link
) {
1033 stats
->kextLoadTag
= ke
->loadTag
;
1034 stats
->attachedEventSources
= wlc
->attachedEventSources
;
1035 stats
->timeOnGate
= wlc
->timeOnGate
;
1036 stats
->dependentKexts
= 0;
1037 RB_FOREACH(dependentNode
, IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
) {
1038 stats
->dependentKextLoadTags
[stats
->dependentKexts
] = dependentNode
->loadTag
;
1039 stats
->dependentKexts
++;
1042 size
= sizeof(IOStatisticsWorkLoop
) + (sizeof(uint32_t) * stats
->dependentKexts
);
1044 accumulatedSize
+= size
;
1045 stats
= (IOStatisticsWorkLoop
*)((void*)((char*)stats
+ size
));
1049 return accumulatedSize
;
1052 uint32_t IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader
*stats
, uint32_t loadTag
)
1054 KextNode
*sought
, *found
= NULL
;
1056 IOUserClientProcessEntry
*processEntry
;
1058 RB_FOREACH(sought
, KextTree
, &kextHead
) {
1059 if (sought
->loadTag
== loadTag
) {
1069 TAILQ_FOREACH(processEntry
, &found
->userClientCallList
, link
) {
1070 strncpy(stats
->userClientCalls
[procs
].processName
, processEntry
->processName
, kIOStatisticsProcessNameLength
);
1071 stats
->userClientCalls
[procs
].pid
= processEntry
->pid
;
1072 stats
->userClientCalls
[procs
].calls
= processEntry
->calls
;
1077 return sizeof(IOStatisticsUserClientCall
) * stats
->processes
;
1080 void IOStatistics::storeUserClientCallInfo(IOUserClient
*userClient
, IOUserClientCounter
*counter
)
1082 OSString
*ossUserClientCreator
= NULL
;
1084 KextNode
*parentKext
;
1085 IOUserClientProcessEntry
*entry
, *nextEntry
, *prevEntry
= NULL
;
1087 const char *ptr
= NULL
;
1090 /* TODO: see if this can be more efficient */
1091 obj
= userClient
->copyProperty("IOUserClientCreator",
1093 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
1098 ossUserClientCreator
= OSDynamicCast(OSString
, obj
);
1100 if (ossUserClientCreator
) {
1101 uint32_t len
, lenIter
= 0;
1103 ptr
= ossUserClientCreator
->getCStringNoCopy();
1104 len
= ossUserClientCreator
->getLength();
1106 while ((*ptr
!= ' ') && (lenIter
< len
)) {
1111 if (lenIter
< len
) {
1112 ptr
++; // Skip the space
1115 while ( (*ptr
!= ',') && (lenIter
< len
)) {
1116 pid
= pid
*10 + (*ptr
- '0');
1121 if(lenIter
== len
) {
1132 IORWLockWrite(lock
);
1134 parentKext
= counter
->parentClass
->parentKext
;
1136 TAILQ_FOREACH(entry
, &parentKext
->userClientCallList
, link
) {
1137 if (entry
->pid
== pid
) {
1138 /* Found, so increment count and move to the head */
1141 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1145 /* At the head already, so increment and return */
1154 if (count
== IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
) {
1155 /* Max elements hit, so reuse the last */
1156 entry
= TAILQ_LAST(&parentKext
->userClientCallList
, ProcessEntryList
);
1157 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1160 /* Otherwise, allocate a new entry */
1161 entry
= (IOUserClientProcessEntry
*)kalloc(sizeof(IOUserClientProcessEntry
));
1163 IORWLockUnlock(lock
);
1168 strncpy(entry
->processName
, ptr
, kIOStatisticsProcessNameLength
);
1173 TAILQ_FOREACH(nextEntry
, &parentKext
->userClientCallList
, link
) {
1174 if (nextEntry
->calls
<= entry
->calls
)
1177 prevEntry
= nextEntry
;
1181 TAILQ_INSERT_HEAD(&parentKext
->userClientCallList
, entry
, link
);
1183 TAILQ_INSERT_AFTER(&parentKext
->userClientCallList
, prevEntry
, entry
, link
);
1186 IORWLockUnlock(lock
);
1193 void IOStatistics::countUserClientCall(IOUserClient
*client
) {
1194 IOUserClient::ExpansionData
*data
;
1195 IOUserClientCounter
*counter
;
1197 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1198 if (!(data
= client
->reserved
)) {
1202 if ((counter
= data
->counter
)) {
1203 storeUserClientCallInfo(client
, counter
);
1204 OSIncrementAtomic(&counter
->clientCalls
);
1208 KextNode
*IOStatistics::getKextNodeFromBacktrace(boolean_t write
) {
1209 const uint32_t btMin
= 3;
1212 unsigned btCount
= sizeof(bt
) / sizeof(bt
[0]);
1213 vm_offset_t
*scanAddr
= NULL
;
1215 KextNode
*found
= NULL
, *ke
= NULL
;
1217 btCount
= OSBacktrace(bt
, btCount
);
1220 IORWLockWrite(lock
);
1225 /* Ignore first levels */
1226 scanAddr
= (vm_offset_t
*)&bt
[btMin
- 1];
1228 for (i
= btMin
- 1; i
< btCount
; i
++, scanAddr
++) {
1229 ke
= RB_ROOT(&kextAddressHead
);
1231 if (*scanAddr
< ke
->address
) {
1232 ke
= RB_LEFT(ke
, addressLink
);
1235 if ((*scanAddr
< ke
->address_end
) && (*scanAddr
>= ke
->address
)) {
1236 if (!ke
->kext
->isKernelComponent()) {
1242 ke
= RB_RIGHT(ke
, addressLink
);
1248 IORWLockUnlock(lock
);
1254 void IOStatistics::releaseKextNode(KextNode
*node
) {
1255 #pragma unused(node)
1256 IORWLockUnlock(lock
);
1259 /* IOLib allocations */
1260 void IOStatistics::countAlloc(uint32_t index
, vm_size_t size
) {
1267 ke
= getKextNodeFromBacktrace(FALSE
);
1269 OSAddAtomic(size
, &ke
->memoryCounters
[index
]);
1270 releaseKextNode(ke
);
1274 #endif /* IOKITSTATS */