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
);
80 int IOStatistics::kextNodeCompare(KextNode
*e1
, KextNode
*e2
)
82 if (e1
->kext
< e2
->kext
)
84 else if (e1
->kext
> e2
->kext
)
90 RB_GENERATE(IOStatistics::KextTree
, KextNode
, link
, kextNodeCompare
);
92 /* Kext tree ordered by address */
94 IOStatistics::KextAddressTreeHead
IOStatistics::kextAddressHead
= RB_INITIALIZER(&IOStatistics::kextAddressHead
);
96 int IOStatistics::kextAddressNodeCompare(KextNode
*e1
, KextNode
*e2
)
98 if (e1
->address
< e2
->address
)
100 else if (e1
->address
> e2
->address
)
106 RB_GENERATE(IOStatistics::KextAddressTree
, KextNode
, addressLink
, kextAddressNodeCompare
);
110 IOStatistics::ClassTreeHead
IOStatistics::classHead
= RB_INITIALIZER(&IOStatistics::classHead
);
112 int IOStatistics::classNodeCompare(ClassNode
*e1
, ClassNode
*e2
) {
113 if (e1
->metaClass
< e2
->metaClass
)
115 else if (e1
->metaClass
> e2
->metaClass
)
121 RB_GENERATE(IOStatistics::ClassTree
, ClassNode
, tLink
, classNodeCompare
);
123 /* Workloop dependencies */
125 int IOWorkLoopCounter::loadTagCompare(IOWorkLoopDependency
*e1
, IOWorkLoopDependency
*e2
) {
126 if (e1
->loadTag
< e2
->loadTag
)
128 else if (e1
->loadTag
> e2
->loadTag
)
134 RB_GENERATE(IOWorkLoopCounter::DependencyTree
, IOWorkLoopDependency
, link
, IOWorkLoopCounter::loadTagCompare
);
139 oid_sysctl(__unused
struct sysctl_oid
*oidp
, __unused
void *arg1
, int arg2
, struct sysctl_req
*req
)
142 uint32_t request
= arg2
;
146 case kIOStatisticsGeneral
:
147 error
= IOStatistics::getStatistics(req
);
149 case kIOStatisticsWorkLoop
:
150 error
= IOStatistics::getWorkLoopStatistics(req
);
152 case kIOStatisticsUserClient
:
153 error
= IOStatistics::getUserClientStatistics(req
);
162 SYSCTL_NODE(_debug
, OID_AUTO
, iokit_statistics
, CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, "IOStatistics");
164 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, general
,
165 CTLTYPE_STRUCT
| CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
166 0, kIOStatisticsGeneral
, oid_sysctl
, "S", "");
168 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, workloop
,
169 CTLTYPE_STRUCT
| CTLFLAG_RD
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
170 0, kIOStatisticsWorkLoop
, oid_sysctl
, "S", "");
172 static SYSCTL_PROC(_debug_iokit_statistics
, OID_AUTO
, userclient
,
173 CTLTYPE_STRUCT
| CTLFLAG_RW
| CTLFLAG_NOAUTO
| CTLFLAG_KERN
| CTLFLAG_LOCKED
,
174 0, kIOStatisticsUserClient
, oid_sysctl
, "S", "");
176 void IOStatistics::initialize()
182 /* Only enabled if the boot argument is set. */
183 if (!(kIOStatistics
& gIOKitDebug
)) {
187 sysctl_register_oid(&sysctl__debug_iokit_statistics_general
);
188 sysctl_register_oid(&sysctl__debug_iokit_statistics_workloop
);
189 sysctl_register_oid(&sysctl__debug_iokit_statistics_userclient
);
191 lock
= IORWLockAlloc();
196 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
197 if (!nextWorkLoopDependency
) {
204 void IOStatistics::onKextLoad(OSKext
*kext
, kmod_info_t
*kmod_info
)
208 assert(kext
&& kmod_info
);
214 LOG(1, "IOStatistics::onKextLoad: %s, tag %d, address 0x%llx, address end 0x%llx\n",
215 kext
->getIdentifierCString(), kmod_info
->id
, (uint64_t)kmod_info
->address
, (uint64_t)(kmod_info
->address
+ kmod_info
->size
));
217 ke
= (KextNode
*)kalloc(sizeof(KextNode
));
222 memset(ke
, 0, sizeof(KextNode
));
225 ke
->loadTag
= kmod_info
->id
;
226 ke
->address
= kmod_info
->address
;
227 ke
->address_end
= kmod_info
->address
+ kmod_info
->size
;
229 SLIST_INIT(&ke
->classList
);
230 TAILQ_INIT(&ke
->userClientCallList
);
234 RB_INSERT(KextTree
, &kextHead
, ke
);
235 RB_INSERT(KextAddressTree
, &kextAddressHead
, ke
);
241 IORWLockUnlock(lock
);
244 void IOStatistics::onKextUnload(OSKext
*kext
)
246 KextNode sought
, *found
;
254 LOG(1, "IOStatistics::onKextUnload: %s\n", kext
->getIdentifierCString());
259 found
= RB_FIND(KextTree
, &kextHead
, &sought
);
261 IOWorkLoopCounter
*wlc
;
262 IOUserClientProcessEntry
*uce
;
264 /* Disconnect workloop counters; cleanup takes place in unregisterWorkLoop() */
265 while ((wlc
= SLIST_FIRST(&found
->workLoopList
))) {
266 SLIST_REMOVE_HEAD(&found
->workLoopList
, link
);
267 wlc
->parentKext
= NULL
;
270 /* Free up the user client list */
271 while ((uce
= TAILQ_FIRST(&found
->userClientCallList
))) {
272 TAILQ_REMOVE(&found
->userClientCallList
, uce
, link
);
273 kfree(uce
, sizeof(IOUserClientProcessEntry
));
276 /* Remove from kext trees */
277 RB_REMOVE(KextTree
, &kextHead
, found
);
278 RB_REMOVE(KextAddressTree
, &kextAddressHead
, found
);
281 * Clear a matching kextHint to avoid use after free in
282 * onClassAdded() for a class add after a KEXT unload.
284 if (found
== kextHint
) {
288 /* Finally, free the class node */
289 kfree(found
, sizeof(KextNode
));
295 panic("IOStatistics::onKextUnload: cannot find kext: %s", kext
->getIdentifierCString());
298 IORWLockUnlock(lock
);
301 void IOStatistics::onClassAdded(OSKext
*parentKext
, OSMetaClass
*metaClass
)
304 KextNode soughtKext
, *foundKext
= NULL
;
306 assert(parentKext
&& metaClass
);
312 LOG(1, "IOStatistics::onClassAdded: %s\n", metaClass
->getClassName());
314 ce
= (ClassNode
*)kalloc(sizeof(ClassNode
));
319 memset(ce
, 0, sizeof(ClassNode
));
324 if (kextHint
&& kextHint
->kext
== parentKext
) {
325 foundKext
= kextHint
;
328 soughtKext
.kext
= parentKext
;
329 foundKext
= RB_FIND(KextTree
, &kextHead
, &soughtKext
);
333 ClassNode soughtClass
, *foundClass
= NULL
;
334 const OSMetaClass
*superClass
;
336 ce
->metaClass
= metaClass
;
337 ce
->classID
= lastClassIndex
++;
338 ce
->parentKext
= foundKext
;
340 /* Has superclass? */
341 superClass
= ce
->metaClass
->getSuperClass();
343 soughtClass
.metaClass
= superClass
;
344 foundClass
= RB_FIND(ClassTree
, &classHead
, &soughtClass
);
346 ce
->superClassID
= foundClass
? foundClass
->classID
: (uint32_t)(-1);
348 SLIST_INIT(&ce
->counterList
);
349 SLIST_INIT(&ce
->userClientList
);
351 RB_INSERT(ClassTree
, &classHead
, ce
);
352 SLIST_INSERT_HEAD(&foundKext
->classList
, ce
, lLink
);
354 foundKext
->classes
++;
356 kextHint
= foundKext
;
362 panic("IOStatistics::onClassAdded: cannot find parent kext: %s", parentKext
->getIdentifierCString());
365 IORWLockUnlock(lock
);
368 void IOStatistics::onClassRemoved(OSKext
*parentKext
, OSMetaClass
*metaClass
)
370 ClassNode sought
, *found
;
372 assert(parentKext
&& metaClass
);
378 LOG(1, "IOStatistics::onClassRemoved: %s\n", metaClass
->getClassName());
382 sought
.metaClass
= metaClass
;
383 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
385 IOEventSourceCounter
*esc
;
386 IOUserClientCounter
*ucc
;
388 /* Free up the list of counters */
389 while ((esc
= SLIST_FIRST(&found
->counterList
))) {
390 SLIST_REMOVE_HEAD(&found
->counterList
, link
);
391 kfree(esc
, sizeof(IOEventSourceCounter
));
394 /* Free up the user client list */
395 while ((ucc
= SLIST_FIRST(&found
->userClientList
))) {
396 SLIST_REMOVE_HEAD(&found
->userClientList
, link
);
397 kfree(ucc
, sizeof(IOUserClientCounter
));
400 /* Remove from class tree */
401 RB_REMOVE(ClassTree
, &classHead
, found
);
403 /* Remove from parent */
404 SLIST_REMOVE(&found
->parentKext
->classList
, found
, ClassNode
, lLink
);
406 /* Finally, free the class node */
407 kfree(found
, sizeof(ClassNode
));
413 panic("IOStatistics::onClassRemoved: cannot find class: %s", metaClass
->getClassName());
416 IORWLockUnlock(lock
);
419 IOEventSourceCounter
*IOStatistics::registerEventSource(OSObject
*inOwner
)
421 IOEventSourceCounter
*counter
= NULL
;
422 ClassNode sought
, *found
= NULL
;
423 boolean_t createDummyCounter
= FALSE
;
431 counter
= (IOEventSourceCounter
*)kalloc(sizeof(IOEventSourceCounter
));
436 memset(counter
, 0, sizeof(IOEventSourceCounter
));
440 /* Workaround for <rdar://problem/7158117> - create a dummy counter when inOwner is bad.
441 * We use retainCount here as our best indication that the pointer is awry.
443 if (inOwner
->retainCount
> 0xFFFFFF) {
444 kprintf("IOStatistics::registerEventSource - bad metaclass %p\n", inOwner
);
445 createDummyCounter
= TRUE
;
448 sought
.metaClass
= inOwner
->getMetaClass();
449 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
453 counter
->parentClass
= found
;
454 SLIST_INSERT_HEAD(&found
->counterList
, counter
, link
);
455 registeredCounters
++;
458 if (!(createDummyCounter
|| found
)) {
459 panic("IOStatistics::registerEventSource: cannot find parent class: %s", inOwner
->getMetaClass()->getClassName());
462 IORWLockUnlock(lock
);
467 void IOStatistics::unregisterEventSource(IOEventSourceCounter
*counter
)
475 if (counter
->parentClass
) {
476 SLIST_REMOVE(&counter
->parentClass
->counterList
, counter
, IOEventSourceCounter
, link
);
477 registeredCounters
--;
479 kfree(counter
, sizeof(IOEventSourceCounter
));
481 IORWLockUnlock(lock
);
484 IOWorkLoopCounter
* IOStatistics::registerWorkLoop(IOWorkLoop
*workLoop
)
486 IOWorkLoopCounter
*counter
= NULL
;
495 counter
= (IOWorkLoopCounter
*)kalloc(sizeof(IOWorkLoopCounter
));
500 memset(counter
, 0, sizeof(IOWorkLoopCounter
));
502 found
= getKextNodeFromBacktrace(TRUE
);
504 panic("IOStatistics::registerWorkLoop: cannot find parent kext");
507 counter
->parentKext
= found
;
508 counter
->workLoop
= workLoop
;
509 RB_INIT(&counter
->dependencyHead
);
510 SLIST_INSERT_HEAD(&found
->workLoopList
, counter
, link
);
511 registeredWorkloops
++;
513 releaseKextNode(found
);
518 void IOStatistics::unregisterWorkLoop(IOWorkLoopCounter
*counter
)
525 if (counter
->parentKext
) {
526 SLIST_REMOVE(&counter
->parentKext
->workLoopList
, counter
, IOWorkLoopCounter
, link
);
528 kfree(counter
, sizeof(IOWorkLoopCounter
));
529 registeredWorkloops
--;
531 IORWLockUnlock(lock
);
534 IOUserClientCounter
*IOStatistics::registerUserClient(IOUserClient
*userClient
)
536 ClassNode sought
, *found
;
537 IOUserClientCounter
*counter
= NULL
;
545 counter
= (IOUserClientCounter
*)kalloc(sizeof(IOUserClientCounter
));
550 memset(counter
, 0, sizeof(IOUserClientCounter
));
554 sought
.metaClass
= userClient
->getMetaClass();
556 found
= RB_FIND(ClassTree
, &classHead
, &sought
);
558 counter
->parentClass
= found
;
559 SLIST_INSERT_HEAD(&found
->userClientList
, counter
, link
);
562 panic("IOStatistics::registerUserClient: cannot find parent class: %s", sought
.metaClass
->getClassName());
565 IORWLockUnlock(lock
);
570 void IOStatistics::unregisterUserClient(IOUserClientCounter
*counter
)
578 SLIST_REMOVE(&counter
->parentClass
->userClientList
, counter
, IOUserClientCounter
, link
);
579 kfree(counter
, sizeof(IOUserClientCounter
));
581 IORWLockUnlock(lock
);
584 void IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
592 if (!nextWorkLoopDependency
) {
596 attachedEventSources
++;
597 wlc
->attachedEventSources
++;
599 /* Track the kext dependency */
600 nextWorkLoopDependency
->loadTag
= esc
->parentClass
->parentKext
->loadTag
;
601 if (NULL
== RB_INSERT(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, nextWorkLoopDependency
)) {
602 nextWorkLoopDependency
= (IOWorkLoopDependency
*)kalloc(sizeof(IOWorkLoopDependency
));
605 IORWLockUnlock(lock
);
608 void IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter
*wlc
, IOEventSourceCounter
*esc
)
610 IOWorkLoopDependency sought
, *found
;
618 attachedEventSources
--;
619 wlc
->attachedEventSources
--;
621 sought
.loadTag
= esc
->parentClass
->parentKext
->loadTag
;
623 found
= RB_FIND(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, &sought
);
625 RB_REMOVE(IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
, found
);
626 kfree(found
, sizeof(IOWorkLoopDependency
));
629 IORWLockUnlock(lock
);
632 int IOStatistics::getStatistics(sysctl_req
*req
)
635 uint32_t calculatedSize
, size
;
637 IOStatisticsHeader
*header
;
639 assert(IOStatistics::enabled
&& req
);
641 IORWLockRead(IOStatistics::lock
);
643 /* Work out how much we need to allocate. IOStatisticsKext is of variable size. */
644 calculatedSize
= sizeof(IOStatisticsHeader
) +
645 sizeof(IOStatisticsGlobal
) +
646 (sizeof(IOStatisticsKext
) * loadedKexts
) + (sizeof(uint32_t) * registeredClasses
) +
647 (sizeof(IOStatisticsMemory
) * loadedKexts
) +
648 (sizeof(IOStatisticsClass
) * registeredClasses
) +
649 (sizeof(IOStatisticsCounter
) * registeredClasses
) +
650 (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
) +
651 (sizeof(IOStatisticsClassName
) * registeredClasses
);
654 if (req
->oldptr
== USER_ADDR_NULL
) {
655 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
660 if (req
->newptr
!= USER_ADDR_NULL
) {
665 buffer
= (char*)kalloc(calculatedSize
);
671 memset(buffer
, 0, calculatedSize
);
675 header
= (IOStatisticsHeader
*)((void*)ptr
);
677 header
->sig
= IOSTATISTICS_SIG
;
678 header
->ver
= IOSTATISTICS_VER
;
680 header
->seq
= sequenceID
;
682 ptr
+= sizeof(IOStatisticsHeader
);
684 /* Global data - seq, timers, interrupts, etc) */
685 header
->globalStatsOffset
= sizeof(IOStatisticsHeader
);
686 size
= copyGlobalStatistics((IOStatisticsGlobal
*)((void*)ptr
));
689 /* Kext statistics */
690 header
->kextStatsOffset
= header
->globalStatsOffset
+ size
;
691 size
= copyKextStatistics((IOStatisticsKext
*)((void*)ptr
));
694 /* Memory allocation info */
695 header
->memoryStatsOffset
= header
->kextStatsOffset
+ size
;
696 size
= copyMemoryStatistics((IOStatisticsMemory
*)((void*)ptr
));
699 /* Class statistics */
700 header
->classStatsOffset
= header
->memoryStatsOffset
+ size
;
701 size
= copyClassStatistics((IOStatisticsClass
*)((void*)ptr
));
704 /* Dynamic class counter data */
705 header
->counterStatsOffset
= header
->classStatsOffset
+ size
;
706 size
= copyCounterStatistics((IOStatisticsCounter
*)((void*)ptr
));
709 /* Kext identifiers */
710 header
->kextIdentifiersOffset
= header
->counterStatsOffset
+ size
;
711 size
= copyKextIdentifiers((IOStatisticsKextIdentifier
*)((void*)ptr
));
715 header
->classNamesOffset
= header
->kextIdentifiersOffset
+ size
;
716 size
= copyClassNames((IOStatisticsClassName
*)ptr
);
719 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
720 calculatedSize
, loadedKexts
, registeredClasses
);
722 assert( (uint32_t)(ptr
- buffer
) == calculatedSize
);
724 error
= SYSCTL_OUT(req
, buffer
, calculatedSize
);
726 kfree(buffer
, calculatedSize
);
729 IORWLockUnlock(IOStatistics::lock
);
733 int IOStatistics::getWorkLoopStatistics(sysctl_req
*req
)
736 uint32_t calculatedSize
, size
;
738 IOStatisticsWorkLoopHeader
*header
;
740 assert(IOStatistics::enabled
&& req
);
742 IORWLockRead(IOStatistics::lock
);
744 /* Approximate how much we need to allocate (worse case estimate) */
745 calculatedSize
= sizeof(IOStatisticsWorkLoop
) * registeredWorkloops
+
746 sizeof(uint32_t) * attachedEventSources
;
749 if (req
->oldptr
== USER_ADDR_NULL
) {
750 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
755 if (req
->newptr
!= USER_ADDR_NULL
) {
760 buffer
= (char*)kalloc(calculatedSize
);
765 memset(buffer
, 0, calculatedSize
);
766 header
= (IOStatisticsWorkLoopHeader
*)((void*)buffer
);
768 header
->sig
= IOSTATISTICS_SIG_WORKLOOP
;
769 header
->ver
= IOSTATISTICS_VER
;
771 header
->seq
= sequenceID
;
773 header
->workloopCount
= registeredWorkloops
;
775 size
= copyWorkLoopStatistics(&header
->workLoopStats
);
777 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize
, size
);
779 assert( size
<= calculatedSize
);
781 error
= SYSCTL_OUT(req
, buffer
, size
);
783 kfree(buffer
, calculatedSize
);
786 IORWLockUnlock(IOStatistics::lock
);
790 int IOStatistics::getUserClientStatistics(sysctl_req
*req
)
793 uint32_t calculatedSize
, size
;
795 uint32_t requestedLoadTag
= 0;
796 IOStatisticsUserClientHeader
*header
;
798 assert(IOStatistics::enabled
&& req
);
800 IORWLockRead(IOStatistics::lock
);
802 /* Work out how much we need to allocate */
803 calculatedSize
= sizeof(IOStatisticsUserClientHeader
) +
804 sizeof(IOStatisticsUserClientCall
) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
* loadedKexts
;
807 if (req
->oldptr
== USER_ADDR_NULL
) {
808 error
= SYSCTL_OUT(req
, NULL
, calculatedSize
);
812 /* Kext request (potentially) valid? */
813 if (!req
->newptr
|| req
->newlen
< sizeof(requestedLoadTag
)) {
818 error
= SYSCTL_IN(req
, &requestedLoadTag
, sizeof(requestedLoadTag
));
823 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag
);
825 buffer
= (char*)kalloc(calculatedSize
);
830 memset(buffer
, 0, calculatedSize
);
831 header
= (IOStatisticsUserClientHeader
*)((void*)buffer
);
833 header
->sig
= IOSTATISTICS_SIG_USERCLIENT
;
834 header
->ver
= IOSTATISTICS_VER
;
836 header
->seq
= sequenceID
;
838 header
->processes
= 0;
840 size
= copyUserClientStatistics(header
, requestedLoadTag
);
842 assert((sizeof(IOStatisticsUserClientHeader
) + size
) <= calculatedSize
);
845 error
= SYSCTL_OUT(req
, buffer
, sizeof(IOStatisticsUserClientHeader
) + size
);
851 kfree(buffer
, calculatedSize
);
854 IORWLockUnlock(IOStatistics::lock
);
858 uint32_t IOStatistics::copyGlobalStatistics(IOStatisticsGlobal
*stats
)
860 stats
->kextCount
= loadedKexts
;
861 stats
->classCount
= registeredClasses
;
862 stats
->workloops
= registeredWorkloops
;
864 return sizeof(IOStatisticsGlobal
);
867 uint32_t IOStatistics::copyKextStatistics(IOStatisticsKext
*stats
)
873 RB_FOREACH(ke
, KextTree
, &kextHead
) {
874 stats
->loadTag
= ke
->loadTag
;
875 ke
->kext
->getSizeInfo(&stats
->loadSize
, &stats
->wiredSize
);
877 stats
->classes
= ke
->classes
;
879 /* Append indices of owned classes */
880 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
881 stats
->classIndexes
[index
++] = ce
->classID
;
884 stats
= (IOStatisticsKext
*)((void*)((char*)stats
+ sizeof(IOStatisticsKext
) + (ke
->classes
* sizeof(uint32_t))));
887 return (sizeof(IOStatisticsKext
) * loadedKexts
+ sizeof(uint32_t) * registeredClasses
);
890 uint32_t IOStatistics::copyMemoryStatistics(IOStatisticsMemory
*stats
)
894 RB_FOREACH(ke
, KextTree
, &kextHead
) {
895 stats
->allocatedSize
= ke
->memoryCounters
[kIOStatisticsMalloc
];
896 stats
->freedSize
= ke
->memoryCounters
[kIOStatisticsFree
];
897 stats
->allocatedAlignedSize
= ke
->memoryCounters
[kIOStatisticsMallocAligned
];
898 stats
->freedAlignedSize
= ke
->memoryCounters
[kIOStatisticsFreeAligned
];
899 stats
->allocatedContiguousSize
= ke
->memoryCounters
[kIOStatisticsMallocContiguous
];
900 stats
->freedContiguousSize
= ke
->memoryCounters
[kIOStatisticsFreeContiguous
];
901 stats
->allocatedPageableSize
= ke
->memoryCounters
[kIOStatisticsMallocPageable
];
902 stats
->freedPageableSize
= ke
->memoryCounters
[kIOStatisticsFreePageable
];
906 return (sizeof(IOStatisticsMemory
) * loadedKexts
);
909 uint32_t IOStatistics::copyClassStatistics(IOStatisticsClass
*stats
)
914 RB_FOREACH(ke
, KextTree
, &kextHead
) {
915 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
916 stats
->classID
= ce
->classID
;
917 stats
->superClassID
= ce
->superClassID
;
918 stats
->classSize
= ce
->metaClass
->getClassSize();
924 return sizeof(IOStatisticsClass
) * registeredClasses
;
927 uint32_t IOStatistics::copyCounterStatistics(IOStatisticsCounter
*stats
)
932 RB_FOREACH(ke
, KextTree
, &kextHead
) {
933 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
934 IOUserClientCounter
*userClientCounter
;
935 IOEventSourceCounter
*counter
;
937 stats
->classID
= ce
->classID
;
938 stats
->classInstanceCount
= ce
->metaClass
->getInstanceCount();
940 IOStatisticsUserClients
*uc
= &stats
->userClientStatistics
;
942 /* User client counters */
943 SLIST_FOREACH(userClientCounter
, &ce
->userClientList
, link
) {
944 uc
->clientCalls
+= userClientCounter
->clientCalls
;
948 IOStatisticsInterruptEventSources
*iec
= &stats
->interruptEventSourceStatistics
;
949 IOStatisticsInterruptEventSources
*fiec
= &stats
->filterInterruptEventSourceStatistics
;
950 IOStatisticsTimerEventSources
*tec
= &stats
->timerEventSourceStatistics
;
951 IOStatisticsCommandGates
*cgc
= &stats
->commandGateStatistics
;
952 IOStatisticsCommandQueues
*cqc
= &stats
->commandQueueStatistics
;
953 IOStatisticsDerivedEventSources
*dec
= &stats
->derivedEventSourceStatistics
;
955 /* Event source counters */
956 SLIST_FOREACH(counter
, &ce
->counterList
, link
) {
957 switch (counter
->type
) {
958 case kIOStatisticsInterruptEventSourceCounter
:
960 iec
->produced
+= counter
->u
.interrupt
.produced
;
961 iec
->checksForWork
+= counter
->u
.interrupt
.checksForWork
;
963 case kIOStatisticsFilterInterruptEventSourceCounter
:
965 fiec
->produced
+= counter
->u
.filter
.produced
;
966 fiec
->checksForWork
+= counter
->u
.filter
.checksForWork
;
968 case kIOStatisticsTimerEventSourceCounter
:
970 tec
->timeouts
+= counter
->u
.timer
.timeouts
;
971 tec
->checksForWork
+= counter
->u
.timer
.checksForWork
;
972 tec
->timeOnGate
+= counter
->timeOnGate
;
973 tec
->closeGateCalls
+= counter
->closeGateCalls
;
974 tec
->openGateCalls
+= counter
->openGateCalls
;
976 case kIOStatisticsCommandGateCounter
:
978 cgc
->timeOnGate
+= counter
->timeOnGate
;
979 cgc
->actionCalls
+= counter
->u
.commandGate
.actionCalls
;
981 case kIOStatisticsCommandQueueCounter
:
983 cqc
->actionCalls
+= counter
->u
.commandQueue
.actionCalls
;
985 case kIOStatisticsDerivedEventSourceCounter
:
987 dec
->timeOnGate
+= counter
->timeOnGate
;
988 dec
->closeGateCalls
+= counter
->closeGateCalls
;
989 dec
->openGateCalls
+= counter
->openGateCalls
;
1000 return sizeof(IOStatisticsCounter
) * registeredClasses
;
1003 uint32_t IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier
*kextIDs
)
1007 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1008 strncpy(kextIDs
->identifier
, ke
->kext
->getIdentifierCString(), kIOStatisticsDriverNameLength
);
1012 return (sizeof(IOStatisticsKextIdentifier
) * loadedKexts
);
1015 uint32_t IOStatistics::copyClassNames(IOStatisticsClassName
*classNames
)
1020 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1021 SLIST_FOREACH(ce
, &ke
->classList
, lLink
) {
1022 strncpy(classNames
->name
, ce
->metaClass
->getClassName(), kIOStatisticsClassNameLength
);
1027 return (sizeof(IOStatisticsClassName
) * registeredClasses
);
1030 uint32_t IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop
*stats
)
1033 IOWorkLoopCounter
*wlc
;
1034 IOWorkLoopDependency
*dependentNode
;
1035 uint32_t size
, accumulatedSize
= 0;
1037 RB_FOREACH(ke
, KextTree
, &kextHead
) {
1038 SLIST_FOREACH(wlc
, &ke
->workLoopList
, link
) {
1039 stats
->kextLoadTag
= ke
->loadTag
;
1040 stats
->attachedEventSources
= wlc
->attachedEventSources
;
1041 stats
->timeOnGate
= wlc
->timeOnGate
;
1042 stats
->dependentKexts
= 0;
1043 RB_FOREACH(dependentNode
, IOWorkLoopCounter::DependencyTree
, &wlc
->dependencyHead
) {
1044 stats
->dependentKextLoadTags
[stats
->dependentKexts
] = dependentNode
->loadTag
;
1045 stats
->dependentKexts
++;
1048 size
= sizeof(IOStatisticsWorkLoop
) + (sizeof(uint32_t) * stats
->dependentKexts
);
1050 accumulatedSize
+= size
;
1051 stats
= (IOStatisticsWorkLoop
*)((void*)((char*)stats
+ size
));
1055 return accumulatedSize
;
1058 uint32_t IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader
*stats
, uint32_t loadTag
)
1060 KextNode
*sought
, *found
= NULL
;
1062 IOUserClientProcessEntry
*processEntry
;
1064 RB_FOREACH(sought
, KextTree
, &kextHead
) {
1065 if (sought
->loadTag
== loadTag
) {
1075 TAILQ_FOREACH(processEntry
, &found
->userClientCallList
, link
) {
1076 strncpy(stats
->userClientCalls
[procs
].processName
, processEntry
->processName
, kIOStatisticsProcessNameLength
);
1077 stats
->userClientCalls
[procs
].pid
= processEntry
->pid
;
1078 stats
->userClientCalls
[procs
].calls
= processEntry
->calls
;
1083 return sizeof(IOStatisticsUserClientCall
) * stats
->processes
;
1086 void IOStatistics::storeUserClientCallInfo(IOUserClient
*userClient
, IOUserClientCounter
*counter
)
1088 OSString
*ossUserClientCreator
= NULL
;
1090 KextNode
*parentKext
;
1091 IOUserClientProcessEntry
*entry
, *nextEntry
, *prevEntry
= NULL
;
1093 const char *ptr
= NULL
;
1096 /* TODO: see if this can be more efficient */
1097 obj
= userClient
->copyProperty("IOUserClientCreator",
1099 kIORegistryIterateRecursively
| kIORegistryIterateParents
);
1104 ossUserClientCreator
= OSDynamicCast(OSString
, obj
);
1106 if (ossUserClientCreator
) {
1107 uint32_t len
, lenIter
= 0;
1109 ptr
= ossUserClientCreator
->getCStringNoCopy();
1110 len
= ossUserClientCreator
->getLength();
1112 while ((*ptr
!= ' ') && (lenIter
< len
)) {
1117 if (lenIter
< len
) {
1118 ptr
++; // Skip the space
1121 while ( (*ptr
!= ',') && (lenIter
< len
)) {
1122 pid
= pid
*10 + (*ptr
- '0');
1127 if(lenIter
== len
) {
1138 IORWLockWrite(lock
);
1140 parentKext
= counter
->parentClass
->parentKext
;
1142 TAILQ_FOREACH(entry
, &parentKext
->userClientCallList
, link
) {
1143 if (entry
->pid
== pid
) {
1144 /* Found, so increment count and move to the head */
1147 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1151 /* At the head already, so increment and return */
1160 if (count
== IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS
) {
1161 /* Max elements hit, so reuse the last */
1162 entry
= TAILQ_LAST(&parentKext
->userClientCallList
, ProcessEntryList
);
1163 TAILQ_REMOVE(&parentKext
->userClientCallList
, entry
, link
);
1166 /* Otherwise, allocate a new entry */
1167 entry
= (IOUserClientProcessEntry
*)kalloc(sizeof(IOUserClientProcessEntry
));
1169 IORWLockUnlock(lock
);
1174 strncpy(entry
->processName
, ptr
, kIOStatisticsProcessNameLength
);
1179 TAILQ_FOREACH(nextEntry
, &parentKext
->userClientCallList
, link
) {
1180 if (nextEntry
->calls
<= entry
->calls
)
1183 prevEntry
= nextEntry
;
1187 TAILQ_INSERT_HEAD(&parentKext
->userClientCallList
, entry
, link
);
1189 TAILQ_INSERT_AFTER(&parentKext
->userClientCallList
, prevEntry
, entry
, link
);
1192 IORWLockUnlock(lock
);
1199 void IOStatistics::countUserClientCall(IOUserClient
*client
) {
1200 IOUserClient::ExpansionData
*data
;
1201 IOUserClientCounter
*counter
;
1203 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1204 if (!(data
= client
->reserved
)) {
1208 if ((counter
= data
->counter
)) {
1209 storeUserClientCallInfo(client
, counter
);
1210 OSIncrementAtomic(&counter
->clientCalls
);
1214 KextNode
*IOStatistics::getKextNodeFromBacktrace(boolean_t write
) {
1215 const uint32_t btMin
= 3;
1218 unsigned btCount
= sizeof(bt
) / sizeof(bt
[0]);
1219 vm_offset_t
*scanAddr
= NULL
;
1221 KextNode
*found
= NULL
, *ke
= NULL
;
1224 * Gathering the backtrace is a significant source of
1225 * overhead. OSBacktrace does many safety checks that
1226 * are not needed in this situation.
1228 btCount
= backtrace((uintptr_t*)bt
, btCount
);
1231 IORWLockWrite(lock
);
1236 /* Ignore first levels */
1237 scanAddr
= (vm_offset_t
*)&bt
[btMin
- 1];
1239 for (i
= btMin
- 1; i
< btCount
; i
++, scanAddr
++) {
1240 ke
= RB_ROOT(&kextAddressHead
);
1242 if (*scanAddr
< ke
->address
) {
1243 ke
= RB_LEFT(ke
, addressLink
);
1246 if ((*scanAddr
< ke
->address_end
) && (*scanAddr
>= ke
->address
)) {
1247 if (!ke
->kext
->isKernelComponent()) {
1253 ke
= RB_RIGHT(ke
, addressLink
);
1259 IORWLockUnlock(lock
);
1265 void IOStatistics::releaseKextNode(KextNode
*node
) {
1266 #pragma unused(node)
1267 IORWLockUnlock(lock
);
1270 /* IOLib allocations */
1271 void IOStatistics::countAlloc(uint32_t index
, vm_size_t size
) {
1278 ke
= getKextNodeFromBacktrace(FALSE
);
1280 OSAddAtomic(size
, &ke
->memoryCounters
[index
]);
1281 releaseKextNode(ke
);
1285 #endif /* IOKITSTATS */