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>
31 #include <kern/zalloc.h>
33 #include <IOKit/system.h>
34 #include <libkern/c++/OSKext.h>
35 #include <libkern/OSAtomic.h>
37 #include <IOKit/IOStatisticsPrivate.h>
38 #include <IOKit/IOUserClient.h>
39 #include <IOKit/IOEventSource.h>
40 #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 /* Only enabled if the boot argument is set. */
182 if (!(kIOStatistics
& gIOKitDebug
)) {
186 sysctl_register_oid(&sysctl__debug_iokit_statistics_general
);
187 sysctl_register_oid(&sysctl__debug_iokit_statistics_workloop
);
188 sysctl_register_oid(&sysctl__debug_iokit_statistics_userclient
);
190 lock
= IORWLockAlloc();
195 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
196 if (!nextWorkLoopDependency
) {
203 void IOStatistics::onKextLoad(OSKext
*kext
, kmod_info_t
*kmod_info
)
207 assert(kext
&& kmod_info
);
213 LOG(1, "IOStatistics::onKextLoad: %s, tag %d, address 0x%llx, address end 0x%llx\n",
214 kext
->getIdentifierCString(), kmod_info
->id
, (uint64_t)kmod_info
->address
, (uint64_t)(kmod_info
->address
+ kmod_info
->size
));
216 ke
= (KextNode
*)kalloc(sizeof(KextNode
));
221 memset(ke
, 0, sizeof(KextNode
));
224 ke
->loadTag
= kmod_info
->id
;
225 ke
->address
= kmod_info
->address
;
226 ke
->address_end
= kmod_info
->address
+ kmod_info
->size
;
228 SLIST_INIT(&ke
->classList
);
229 TAILQ_INIT(&ke
->userClientCallList
);
233 RB_INSERT(KextTree
, &kextHead
, ke
);
234 RB_INSERT(KextAddressTree
, &kextAddressHead
, ke
);
240 IORWLockUnlock(lock
);
243 void IOStatistics::onKextUnload(OSKext
*kext
)
245 KextNode sought
, *found
;
253 LOG(1, "IOStatistics::onKextUnload: %s\n", kext
->getIdentifierCString());
258 found
= RB_FIND(KextTree
, &kextHead
, &sought
);
260 IOWorkLoopCounter
*wlc
;
261 IOUserClientProcessEntry
*uce
;
263 /* Disconnect workloop counters; cleanup takes place in unregisterWorkLoop() */
264 while ((wlc
= SLIST_FIRST(&found
->workLoopList
))) {
265 SLIST_REMOVE_HEAD(&found
->workLoopList
, link
);
266 wlc
->parentKext
= NULL
;
269 /* Free up the user client list */
270 while ((uce
= TAILQ_FIRST(&found
->userClientCallList
))) {
271 TAILQ_REMOVE(&found
->userClientCallList
, uce
, link
);
272 kfree(uce
, sizeof(IOUserClientProcessEntry
));
275 /* Remove from kext trees */
276 RB_REMOVE(KextTree
, &kextHead
, found
);
277 RB_REMOVE(KextAddressTree
, &kextAddressHead
, found
);
280 * Clear a matching kextHint to avoid use after free in
281 * onClassAdded() for a class add after a KEXT unload.
283 if (found
== kextHint
) {
287 /* Finally, free the class node */
288 kfree(found
, sizeof(KextNode
));
294 panic("IOStatistics::onKextUnload: cannot find kext: %s", kext
->getIdentifierCString());
297 IORWLockUnlock(lock
);
300 void IOStatistics::onClassAdded(OSKext
*parentKext
, OSMetaClass
*metaClass
)
303 KextNode soughtKext
, *foundKext
= NULL
;
305 assert(parentKext
&& metaClass
);
311 LOG(1, "IOStatistics::onClassAdded: %s\n", metaClass
->getClassName());
313 ce
= (ClassNode
*)kalloc(sizeof(ClassNode
));
318 memset(ce
, 0, sizeof(ClassNode
));
323 if (kextHint
&& kextHint
->kext
== parentKext
) {
324 foundKext
= kextHint
;
327 soughtKext
.kext
= parentKext
;
328 foundKext
= RB_FIND(KextTree
, &kextHead
, &soughtKext
);
332 ClassNode soughtClass
, *foundClass
= NULL
;
333 const OSMetaClass
*superClass
;
335 ce
->metaClass
= metaClass
;
336 ce
->classID
= lastClassIndex
++;
337 ce
->parentKext
= foundKext
;
339 /* Has superclass? */
340 superClass
= ce
->metaClass
->getSuperClass();
342 soughtClass
.metaClass
= superClass
;
343 foundClass
= RB_FIND(ClassTree
, &classHead
, &soughtClass
);
345 ce
->superClassID
= foundClass
? foundClass
->classID
: (uint32_t)(-1);
347 SLIST_INIT(&ce
->counterList
);
348 SLIST_INIT(&ce
->userClientList
);
350 RB_INSERT(ClassTree
, &classHead
, ce
);
351 SLIST_INSERT_HEAD(&foundKext
->classList
, ce
, lLink
);
353 foundKext
->classes
++;
355 kextHint
= foundKext
;
361 panic("IOStatistics::onClassAdded: cannot find parent kext: %s", parentKext
->getIdentifierCString());
364 IORWLockUnlock(lock
);
367 void IOStatistics::onClassRemoved(OSKext
*parentKext
, OSMetaClass
*metaClass
)
369 ClassNode sought
, *found
;
371 assert(parentKext
&& metaClass
);
377 LOG(1, "IOStatistics::onClassRemoved: %s\n", metaClass
->getClassName());
381 sought
.metaClass
= metaClass
;
382 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
384 IOEventSourceCounter
*esc
;
385 IOUserClientCounter
*ucc
;
387 /* Free up the list of counters */
388 while ((esc
= SLIST_FIRST(&found
->counterList
))) {
389 SLIST_REMOVE_HEAD(&found
->counterList
, link
);
390 kfree(esc
, sizeof(IOEventSourceCounter
));
393 /* Free up the user client list */
394 while ((ucc
= SLIST_FIRST(&found
->userClientList
))) {
395 SLIST_REMOVE_HEAD(&found
->userClientList
, link
);
396 kfree(ucc
, sizeof(IOUserClientCounter
));
399 /* Remove from class tree */
400 RB_REMOVE(ClassTree
, &classHead
, found
);
402 /* Remove from parent */
403 SLIST_REMOVE(&found
->parentKext
->classList
, found
, ClassNode
, lLink
);
405 /* Finally, free the class node */
406 kfree(found
, sizeof(ClassNode
));
412 panic("IOStatistics::onClassRemoved: cannot find class: %s", metaClass
->getClassName());
415 IORWLockUnlock(lock
);
418 IOEventSourceCounter
*IOStatistics::registerEventSource(OSObject
*inOwner
)
420 IOEventSourceCounter
*counter
= NULL
;
421 ClassNode sought
, *found
= NULL
;
422 boolean_t createDummyCounter
= FALSE
;
430 counter
= (IOEventSourceCounter
*)kalloc(sizeof(IOEventSourceCounter
));
435 memset(counter
, 0, sizeof(IOEventSourceCounter
));
439 /* Workaround for <rdar://problem/7158117> - create a dummy counter when inOwner is bad.
440 * We use retainCount here as our best indication that the pointer is awry.
442 if (inOwner
->retainCount
> 0xFFFFFF) {
443 kprintf("IOStatistics::registerEventSource - bad metaclass %p\n", inOwner
);
444 createDummyCounter
= TRUE
;
447 sought
.metaClass
= inOwner
->getMetaClass();
448 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
452 counter
->parentClass
= found
;
453 SLIST_INSERT_HEAD(&found
->counterList
, counter
, link
);
454 registeredCounters
++;
457 if (!(createDummyCounter
|| found
)) {
458 panic("IOStatistics::registerEventSource: cannot find parent class: %s", inOwner
->getMetaClass()->getClassName());
461 IORWLockUnlock(lock
);
466 void IOStatistics::unregisterEventSource(IOEventSourceCounter
*counter
)
474 if (counter
->parentClass
) {
475 SLIST_REMOVE(&counter
->parentClass
->counterList
, counter
, IOEventSourceCounter
, link
);
476 registeredCounters
--;
478 kfree(counter
, sizeof(IOEventSourceCounter
));
480 IORWLockUnlock(lock
);
483 IOWorkLoopCounter
* IOStatistics::registerWorkLoop(IOWorkLoop
*workLoop
)
485 IOWorkLoopCounter
*counter
= NULL
;
494 counter
= (IOWorkLoopCounter
*)kalloc(sizeof(IOWorkLoopCounter
));
499 memset(counter
, 0, sizeof(IOWorkLoopCounter
));
501 found
= getKextNodeFromBacktrace(TRUE
);
503 panic("IOStatistics::registerWorkLoop: cannot find parent kext");
506 counter
->parentKext
= found
;
507 counter
->workLoop
= workLoop
;
508 RB_INIT(&counter
->dependencyHead
);
509 SLIST_INSERT_HEAD(&found
->workLoopList
, counter
, link
);
510 registeredWorkloops
++;
512 releaseKextNode(found
);
517 void IOStatistics::unregisterWorkLoop(IOWorkLoopCounter
*counter
)
524 if (counter
->parentKext
) {
525 SLIST_REMOVE(&counter
->parentKext
->workLoopList
, counter
, IOWorkLoopCounter
, link
);
527 kfree(counter
, sizeof(IOWorkLoopCounter
));
528 registeredWorkloops
--;
530 IORWLockUnlock(lock
);
533 IOUserClientCounter
*IOStatistics::registerUserClient(IOUserClient
*userClient
)
535 ClassNode sought
, *found
;
536 IOUserClientCounter
*counter
= NULL
;
544 counter
= (IOUserClientCounter
*)kalloc(sizeof(IOUserClientCounter
));
549 memset(counter
, 0, sizeof(IOUserClientCounter
));
553 sought
.metaClass
= userClient
->getMetaClass();
555 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
557 counter
->parentClass
= found
;
558 SLIST_INSERT_HEAD(&found
->userClientList
, counter
, link
);
561 panic("IOStatistics::registerUserClient: cannot find parent class: %s", sought
.metaClass
->getClassName());
564 IORWLockUnlock(lock
);
569 void IOStatistics::unregisterUserClient(IOUserClientCounter
*counter
)
577 SLIST_REMOVE(&counter
->parentClass
->userClientList
, counter
, IOUserClientCounter
, link
);
578 kfree(counter
, sizeof(IOUserClientCounter
));
580 IORWLockUnlock(lock
);
583 void IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
591 if (!nextWorkLoopDependency
) {
595 attachedEventSources
++;
596 wlc
->attachedEventSources
++;
598 /* Track the kext dependency */
599 nextWorkLoopDependency
->loadTag
= esc
->parentClass
->parentKext
->loadTag
;
600 if (NULL
== RB_INSERT(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, nextWorkLoopDependency
)) {
601 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
604 IORWLockUnlock(lock
);
607 void IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
609 IOWorkLoopDependency sought
, *found
;
617 attachedEventSources
--;
618 wlc
->attachedEventSources
--;
620 sought
.loadTag
= esc
->parentClass
->parentKext
->loadTag
;
622 found
= RB_FIND(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, &sought
);
624 RB_REMOVE(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, found
);
625 kfree(found
, sizeof(IOWorkLoopDependency
));
628 IORWLockUnlock(lock
);
631 int IOStatistics::getStatistics(sysctl_req
*req
)
634 uint32_t calculatedSize
, size
;
636 IOStatisticsHeader
*header
;
638 assert(IOStatistics::enabled
&& req
);
640 IORWLockRead(IOStatistics::lock
);
642 /* Work out how much we need to allocate. IOStatisticsKext is of variable size. */
643 calculatedSize
= sizeof(IOStatisticsHeader
) +
644 sizeof(IOStatisticsGlobal
) +
645 (sizeof(IOStatisticsKext
) * loadedKexts
) + (sizeof(uint32_t) * registeredClasses
) +
646 (sizeof(IOStatisticsMemory
) * loadedKexts
) +
647 (sizeof(IOStatisticsClass
) * registeredClasses
) +
648 (sizeof(IOStatisticsCounter
) * registeredClasses
) +
649 (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
) +
650 (sizeof(IOStatisticsClassName
) * registeredClasses
);
653 if (req
->oldptr
== USER_ADDR_NULL
) {
654 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
659 if (req
->newptr
!= USER_ADDR_NULL
) {
664 buffer
= (char*)kalloc(calculatedSize
);
670 memset(buffer
, 0, calculatedSize
);
674 header
= (IOStatisticsHeader
*)((void*)ptr
);
676 header
->sig
= IOSTATISTICS_SIG
;
677 header
->ver
= IOSTATISTICS_VER
;
679 header
->seq
= sequenceID
;
681 ptr
+= sizeof(IOStatisticsHeader
);
683 /* Global data - seq, timers, interrupts, etc) */
684 header
->globalStatsOffset
= sizeof(IOStatisticsHeader
);
685 size
= copyGlobalStatistics((IOStatisticsGlobal
*)((void*)ptr
));
688 /* Kext statistics */
689 header
->kextStatsOffset
= header
->globalStatsOffset
+ size
;
690 size
= copyKextStatistics((IOStatisticsKext
*)((void*)ptr
));
693 /* Memory allocation info */
694 header
->memoryStatsOffset
= header
->kextStatsOffset
+ size
;
695 size
= copyMemoryStatistics((IOStatisticsMemory
*)((void*)ptr
));
698 /* Class statistics */
699 header
->classStatsOffset
= header
->memoryStatsOffset
+ size
;
700 size
= copyClassStatistics((IOStatisticsClass
*)((void*)ptr
));
703 /* Dynamic class counter data */
704 header
->counterStatsOffset
= header
->classStatsOffset
+ size
;
705 size
= copyCounterStatistics((IOStatisticsCounter
*)((void*)ptr
));
708 /* Kext identifiers */
709 header
->kextIdentifiersOffset
= header
->counterStatsOffset
+ size
;
710 size
= copyKextIdentifiers((IOStatisticsKextIdentifier
*)((void*)ptr
));
714 header
->classNamesOffset
= header
->kextIdentifiersOffset
+ size
;
715 size
= copyClassNames((IOStatisticsClassName
*)ptr
);
718 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
719 calculatedSize
, loadedKexts
, registeredClasses
);
721 assert( (uint32_t)(ptr
- buffer
) == calculatedSize
);
723 error
= SYSCTL_OUT(req
, buffer
, calculatedSize
);
725 kfree(buffer
, calculatedSize
);
728 IORWLockUnlock(IOStatistics::lock
);
732 int IOStatistics::getWorkLoopStatistics(sysctl_req
*req
)
735 uint32_t calculatedSize
, size
;
737 IOStatisticsWorkLoopHeader
*header
;
739 assert(IOStatistics::enabled
&& req
);
741 IORWLockRead(IOStatistics::lock
);
743 /* Approximate how much we need to allocate (worse case estimate) */
744 calculatedSize
= sizeof(IOStatisticsWorkLoop
) * registeredWorkloops
+
745 sizeof(uint32_t) * attachedEventSources
;
748 if (req
->oldptr
== USER_ADDR_NULL
) {
749 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
754 if (req
->newptr
!= USER_ADDR_NULL
) {
759 buffer
= (char*)kalloc(calculatedSize
);
765 header
= (IOStatisticsWorkLoopHeader
*)((void*)buffer
);
767 header
->sig
= IOSTATISTICS_SIG_WORKLOOP
;
768 header
->ver
= IOSTATISTICS_VER
;
770 header
->seq
= sequenceID
;
772 header
->workloopCount
= registeredWorkloops
;
774 size
= copyWorkLoopStatistics(&header
->workLoopStats
);
776 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize
, size
);
778 assert( size
<= calculatedSize
);
780 error
= SYSCTL_OUT(req
, buffer
, size
);
782 kfree(buffer
, calculatedSize
);
785 IORWLockUnlock(IOStatistics::lock
);
789 int IOStatistics::getUserClientStatistics(sysctl_req
*req
)
792 uint32_t calculatedSize
, size
;
794 uint32_t requestedLoadTag
= 0;
795 IOStatisticsUserClientHeader
*header
;
797 assert(IOStatistics::enabled
&& req
);
799 IORWLockRead(IOStatistics::lock
);
801 /* Work out how much we need to allocate */
802 calculatedSize
= sizeof(IOStatisticsUserClientHeader
) +
803 sizeof(IOStatisticsUserClientCall
) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
* loadedKexts
;
806 if (req
->oldptr
== USER_ADDR_NULL
) {
807 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
811 /* Kext request (potentially) valid? */
812 if (!req
->newptr
|| req
->newlen
< sizeof(requestedLoadTag
)) {
817 SYSCTL_IN(req
, &requestedLoadTag
, sizeof(requestedLoadTag
));
819 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag
);
821 buffer
= (char*)kalloc(calculatedSize
);
827 header
= (IOStatisticsUserClientHeader
*)((void*)buffer
);
829 header
->sig
= IOSTATISTICS_SIG_USERCLIENT
;
830 header
->ver
= IOSTATISTICS_VER
;
832 header
->seq
= sequenceID
;
834 header
->processes
= 0;
836 size
= copyUserClientStatistics(header
, requestedLoadTag
);
838 assert((sizeof(IOStatisticsUserClientHeader
) + size
) <= calculatedSize
);
841 error
= SYSCTL_OUT(req
, buffer
, sizeof(IOStatisticsUserClientHeader
) + size
);
847 kfree(buffer
, calculatedSize
);
850 IORWLockUnlock(IOStatistics::lock
);
854 uint32_t IOStatistics::copyGlobalStatistics(IOStatisticsGlobal
*stats
)
856 stats
->kextCount
= loadedKexts
;
857 stats
->classCount
= registeredClasses
;
858 stats
->workloops
= registeredWorkloops
;
860 return sizeof(IOStatisticsGlobal
);
863 uint32_t IOStatistics::copyKextStatistics(IOStatisticsKext
*stats
)
869 RB_FOREACH(ke
, KextTree
, &kextHead
) {
870 stats
->loadTag
= ke
->loadTag
;
871 ke
->kext
->getSizeInfo(&stats
->loadSize
, &stats
->wiredSize
);
873 stats
->classes
= ke
->classes
;
875 /* Append indices of owned classes */
876 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
877 stats
->classIndexes
[index
++] = ce
->classID
;
880 stats
= (IOStatisticsKext
*)((void*)((char*)stats
+ sizeof(IOStatisticsKext
) + (ke
->classes
* sizeof(uint32_t))));
883 return (sizeof(IOStatisticsKext
) * loadedKexts
+ sizeof(uint32_t) * registeredClasses
);
886 uint32_t IOStatistics::copyMemoryStatistics(IOStatisticsMemory
*stats
)
890 RB_FOREACH(ke
, KextTree
, &kextHead
) {
891 stats
->allocatedSize
= ke
->memoryCounters
[kIOStatisticsMalloc
];
892 stats
->freedSize
= ke
->memoryCounters
[kIOStatisticsFree
];
893 stats
->allocatedAlignedSize
= ke
->memoryCounters
[kIOStatisticsMallocAligned
];
894 stats
->freedAlignedSize
= ke
->memoryCounters
[kIOStatisticsFreeAligned
];
895 stats
->allocatedContiguousSize
= ke
->memoryCounters
[kIOStatisticsMallocContiguous
];
896 stats
->freedContiguousSize
= ke
->memoryCounters
[kIOStatisticsFreeContiguous
];
897 stats
->allocatedPageableSize
= ke
->memoryCounters
[kIOStatisticsMallocPageable
];
898 stats
->freedPageableSize
= ke
->memoryCounters
[kIOStatisticsFreePageable
];
902 return (sizeof(IOStatisticsMemory
) * loadedKexts
);
905 uint32_t IOStatistics::copyClassStatistics(IOStatisticsClass
*stats
)
910 RB_FOREACH(ke
, KextTree
, &kextHead
) {
911 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
912 stats
->classID
= ce
->classID
;
913 stats
->superClassID
= ce
->superClassID
;
914 stats
->classSize
= ce
->metaClass
->getClassSize();
920 return sizeof(IOStatisticsClass
) * registeredClasses
;
923 uint32_t IOStatistics::copyCounterStatistics(IOStatisticsCounter
*stats
)
928 RB_FOREACH(ke
, KextTree
, &kextHead
) {
929 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
930 IOUserClientCounter
*userClientCounter
;
931 IOEventSourceCounter
*counter
;
933 stats
->classID
= ce
->classID
;
934 stats
->classInstanceCount
= ce
->metaClass
->getInstanceCount();
936 IOStatisticsUserClients
*uc
= &stats
->userClientStatistics
;
938 /* User client counters */
939 SLIST_FOREACH(userClientCounter
, &ce
->userClientList
, link
) {
940 uc
->clientCalls
+= userClientCounter
->clientCalls
;
944 IOStatisticsInterruptEventSources
*iec
= &stats
->interruptEventSourceStatistics
;
945 IOStatisticsInterruptEventSources
*fiec
= &stats
->filterInterruptEventSourceStatistics
;
946 IOStatisticsTimerEventSources
*tec
= &stats
->timerEventSourceStatistics
;
947 IOStatisticsCommandGates
*cgc
= &stats
->commandGateStatistics
;
948 IOStatisticsCommandQueues
*cqc
= &stats
->commandQueueStatistics
;
949 IOStatisticsDerivedEventSources
*dec
= &stats
->derivedEventSourceStatistics
;
951 /* Event source counters */
952 SLIST_FOREACH(counter
, &ce
->counterList
, link
) {
953 switch (counter
->type
) {
954 case kIOStatisticsInterruptEventSourceCounter
:
956 iec
->produced
+= counter
->u
.interrupt
.produced
;
957 iec
->checksForWork
+= counter
->u
.interrupt
.checksForWork
;
959 case kIOStatisticsFilterInterruptEventSourceCounter
:
961 fiec
->produced
+= counter
->u
.filter
.produced
;
962 fiec
->checksForWork
+= counter
->u
.filter
.checksForWork
;
964 case kIOStatisticsTimerEventSourceCounter
:
966 tec
->timeouts
+= counter
->u
.timer
.timeouts
;
967 tec
->checksForWork
+= counter
->u
.timer
.checksForWork
;
968 tec
->timeOnGate
+= counter
->timeOnGate
;
969 tec
->closeGateCalls
+= counter
->closeGateCalls
;
970 tec
->openGateCalls
+= counter
->openGateCalls
;
972 case kIOStatisticsCommandGateCounter
:
974 cgc
->timeOnGate
+= counter
->timeOnGate
;
975 cgc
->actionCalls
+= counter
->u
.commandGate
.actionCalls
;
977 case kIOStatisticsCommandQueueCounter
:
979 cqc
->actionCalls
+= counter
->u
.commandQueue
.actionCalls
;
981 case kIOStatisticsDerivedEventSourceCounter
:
983 dec
->timeOnGate
+= counter
->timeOnGate
;
984 dec
->closeGateCalls
+= counter
->closeGateCalls
;
985 dec
->openGateCalls
+= counter
->openGateCalls
;
996 return sizeof(IOStatisticsCounter
) * registeredClasses
;
999 uint32_t IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier
*kextIDs
)
1003 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1004 strncpy(kextIDs
->identifier
, ke
->kext
->getIdentifierCString(), kIOStatisticsDriverNameLength
);
1008 return (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
);
1011 uint32_t IOStatistics::copyClassNames(IOStatisticsClassName
*classNames
)
1016 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1017 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
1018 strncpy(classNames
->name
, ce
->metaClass
->getClassName(), kIOStatisticsClassNameLength
);
1023 return (sizeof(IOStatisticsClassName
) * registeredClasses
);
1026 uint32_t IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop
*stats
)
1029 IOWorkLoopCounter
*wlc
;
1030 IOWorkLoopDependency
*dependentNode
;
1031 uint32_t size
, accumulatedSize
= 0;
1033 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1034 SLIST_FOREACH(wlc
, &ke
->workLoopList
, link
) {
1035 stats
->kextLoadTag
= ke
->loadTag
;
1036 stats
->attachedEventSources
= wlc
->attachedEventSources
;
1037 stats
->timeOnGate
= wlc
->timeOnGate
;
1038 stats
->dependentKexts
= 0;
1039 RB_FOREACH(dependentNode
, IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
) {
1040 stats
->dependentKextLoadTags
[stats
->dependentKexts
] = dependentNode
->loadTag
;
1041 stats
->dependentKexts
++;
1044 size
= sizeof(IOStatisticsWorkLoop
) + (sizeof(uint32_t) * stats
->dependentKexts
);
1046 accumulatedSize
+= size
;
1047 stats
= (IOStatisticsWorkLoop
*)((void*)((char*)stats
+ size
));
1051 return accumulatedSize
;
1054 uint32_t IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader
*stats
, uint32_t loadTag
)
1056 KextNode
*sought
, *found
= NULL
;
1058 IOUserClientProcessEntry
*processEntry
;
1060 RB_FOREACH(sought
, KextTree
, &kextHead
) {
1061 if (sought
->loadTag
== loadTag
) {
1071 TAILQ_FOREACH(processEntry
, &found
->userClientCallList
, link
) {
1072 strncpy(stats
->userClientCalls
[procs
].processName
, processEntry
->processName
, kIOStatisticsProcessNameLength
);
1073 stats
->userClientCalls
[procs
].pid
= processEntry
->pid
;
1074 stats
->userClientCalls
[procs
].calls
= processEntry
->calls
;
1079 return sizeof(IOStatisticsUserClientCall
) * stats
->processes
;
1082 void IOStatistics::storeUserClientCallInfo(IOUserClient
*userClient
, IOUserClientCounter
*counter
)
1084 OSString
*ossUserClientCreator
= NULL
;
1086 KextNode
*parentKext
;
1087 IOUserClientProcessEntry
*entry
, *nextEntry
, *prevEntry
= NULL
;
1089 const char *ptr
= NULL
;
1092 /* TODO: see if this can be more efficient */
1093 obj
= userClient
->copyProperty("IOUserClientCreator",
1095 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
1100 ossUserClientCreator
= OSDynamicCast(OSString
, obj
);
1102 if (ossUserClientCreator
) {
1103 uint32_t len
, lenIter
= 0;
1105 ptr
= ossUserClientCreator
->getCStringNoCopy();
1106 len
= ossUserClientCreator
->getLength();
1108 while ((*ptr
!= ' ') && (lenIter
< len
)) {
1113 if (lenIter
< len
) {
1114 ptr
++; // Skip the space
1117 while ( (*ptr
!= ',') && (lenIter
< len
)) {
1118 pid
= pid
*10 + (*ptr
- '0');
1123 if(lenIter
== len
) {
1134 IORWLockWrite(lock
);
1136 parentKext
= counter
->parentClass
->parentKext
;
1138 TAILQ_FOREACH(entry
, &parentKext
->userClientCallList
, link
) {
1139 if (entry
->pid
== pid
) {
1140 /* Found, so increment count and move to the head */
1143 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1147 /* At the head already, so increment and return */
1156 if (count
== IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
) {
1157 /* Max elements hit, so reuse the last */
1158 entry
= TAILQ_LAST(&parentKext
->userClientCallList
, ProcessEntryList
);
1159 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1162 /* Otherwise, allocate a new entry */
1163 entry
= (IOUserClientProcessEntry
*)kalloc(sizeof(IOUserClientProcessEntry
));
1165 IORWLockUnlock(lock
);
1170 strncpy(entry
->processName
, ptr
, kIOStatisticsProcessNameLength
);
1175 TAILQ_FOREACH(nextEntry
, &parentKext
->userClientCallList
, link
) {
1176 if (nextEntry
->calls
<= entry
->calls
)
1179 prevEntry
= nextEntry
;
1183 TAILQ_INSERT_HEAD(&parentKext
->userClientCallList
, entry
, link
);
1185 TAILQ_INSERT_AFTER(&parentKext
->userClientCallList
, prevEntry
, entry
, link
);
1188 IORWLockUnlock(lock
);
1195 void IOStatistics::countUserClientCall(IOUserClient
*client
) {
1196 IOUserClient::ExpansionData
*data
;
1197 IOUserClientCounter
*counter
;
1199 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1200 if (!(data
= client
->reserved
)) {
1204 if ((counter
= data
->counter
)) {
1205 storeUserClientCallInfo(client
, counter
);
1206 OSIncrementAtomic(&counter
->clientCalls
);
1210 KextNode
*IOStatistics::getKextNodeFromBacktrace(boolean_t write
) {
1211 const uint32_t btMin
= 3;
1214 unsigned btCount
= sizeof(bt
) / sizeof(bt
[0]);
1215 vm_offset_t
*scanAddr
= NULL
;
1217 KextNode
*found
= NULL
, *ke
= NULL
;
1220 * Gathering the backtrace is a significant source of
1221 * overhead. OSBacktrace does many safety checks that
1222 * are not needed in this situation.
1224 btCount
= fastbacktrace((uintptr_t*)bt
, btCount
);
1227 IORWLockWrite(lock
);
1232 /* Ignore first levels */
1233 scanAddr
= (vm_offset_t
*)&bt
[btMin
- 1];
1235 for (i
= btMin
- 1; i
< btCount
; i
++, scanAddr
++) {
1236 ke
= RB_ROOT(&kextAddressHead
);
1238 if (*scanAddr
< ke
->address
) {
1239 ke
= RB_LEFT(ke
, addressLink
);
1242 if ((*scanAddr
< ke
->address_end
) && (*scanAddr
>= ke
->address
)) {
1243 if (!ke
->kext
->isKernelComponent()) {
1249 ke
= RB_RIGHT(ke
, addressLink
);
1255 IORWLockUnlock(lock
);
1261 void IOStatistics::releaseKextNode(KextNode
*node
) {
1262 #pragma unused(node)
1263 IORWLockUnlock(lock
);
1266 /* IOLib allocations */
1267 void IOStatistics::countAlloc(uint32_t index
, vm_size_t size
) {
1274 ke
= getKextNodeFromBacktrace(FALSE
);
1276 OSAddAtomic(size
, &ke
->memoryCounters
[index
]);
1277 releaseKextNode(ke
);
1281 #endif /* IOKITSTATS */