]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOStatistics.cpp
xnu-1699.22.81.tar.gz
[apple/xnu.git] / iokit / Kernel / IOStatistics.cpp
CommitLineData
6d2010ae
A
1/*
2 * Copyright (c) 2010 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/sysctl.h>
30#include <kern/host.h>
31
32#include <IOKit/system.h>
33#include <libkern/c++/OSKext.h>
34#include <libkern/OSAtomic.h>
35
36#include <IOKit/IOStatisticsPrivate.h>
37#include <IOKit/IOUserClient.h>
38#include <IOKit/IOEventSource.h>
39#include <IOKit/IOKitDebug.h>
40
41#if IOKITSTATS
42
43bool IOStatistics::enabled = false;
44
45uint32_t IOStatistics::sequenceID = 0;
46
47uint32_t IOStatistics::lastClassIndex = 0;
48uint32_t IOStatistics::lastKextIndex = 0;
49
50uint32_t IOStatistics::loadedKexts = 0;
51uint32_t IOStatistics::registeredClasses = 0;
52uint32_t IOStatistics::registeredCounters = 0;
53uint32_t IOStatistics::registeredWorkloops = 0;
54
55uint32_t IOStatistics::attachedEventSources = 0;
56
57IOWorkLoopDependency *IOStatistics::nextWorkLoopDependency = NULL;
58
59/* Logging */
60
61#define LOG_LEVEL 0
62
63#define LOG(level, format, ...) \
64do { \
65 if (level <= LOG_LEVEL) \
66 printf(format, ##__VA_ARGS__); \
67} while (0)
68
69/* Locks */
70
71IORWLock *IOStatistics::lock = NULL;
72
73/* Kext tree */
74
75KextNode *IOStatistics::kextHint = NULL;
76
77IOStatistics::KextTreeHead IOStatistics::kextHead = RB_INITIALIZER(&IOStatistics::kextHead);
78
79int IOStatistics::kextNodeCompare(KextNode *e1, KextNode *e2)
80{
81 if (e1->kext < e2->kext)
82 return -1;
83 else if (e1->kext > e2->kext)
84 return 1;
85 else
86 return 0;
87}
88
89RB_GENERATE(IOStatistics::KextTree, KextNode, link, kextNodeCompare);
90
91/* Kext tree ordered by address */
92
93IOStatistics::KextAddressTreeHead IOStatistics::kextAddressHead = RB_INITIALIZER(&IOStatistics::kextAddressHead);
94
95int IOStatistics::kextAddressNodeCompare(KextNode *e1, KextNode *e2)
96{
97 if (e1->address < e2->address)
98 return -1;
99 else if (e1->address > e2->address)
100 return 1;
101 else
102 return 0;
103}
104
105RB_GENERATE(IOStatistics::KextAddressTree, KextNode, addressLink, kextAddressNodeCompare);
106
107/* Class tree */
108
109IOStatistics::ClassTreeHead IOStatistics::classHead = RB_INITIALIZER(&IOStatistics::classHead);
110
111int IOStatistics::classNodeCompare(ClassNode *e1, ClassNode *e2) {
112 if (e1->metaClass < e2->metaClass)
113 return -1;
114 else if (e1->metaClass > e2->metaClass)
115 return 1;
116 else
117 return 0;
118}
119
120RB_GENERATE(IOStatistics::ClassTree, ClassNode, tLink, classNodeCompare);
121
122/* Workloop dependencies */
123
124int IOWorkLoopCounter::loadTagCompare(IOWorkLoopDependency *e1, IOWorkLoopDependency *e2) {
125 if (e1->loadTag < e2->loadTag)
126 return -1;
127 else if (e1->loadTag > e2->loadTag)
128 return 1;
129 else
130 return 0;
131}
132
133RB_GENERATE(IOWorkLoopCounter::DependencyTree, IOWorkLoopDependency, link, IOWorkLoopCounter::loadTagCompare);
134
135/* sysctl stuff */
136
137static int
138oid_sysctl(__unused struct sysctl_oid *oidp, __unused void *arg1, int arg2, struct sysctl_req *req)
139{
140 int error = EINVAL;
141 uint32_t request = arg2;
142
143 switch (request)
144 {
145 case kIOStatisticsGeneral:
146 error = IOStatistics::getStatistics(req);
147 break;
148 case kIOStatisticsWorkLoop:
149 error = IOStatistics::getWorkLoopStatistics(req);
150 break;
151 case kIOStatisticsUserClient:
152 error = IOStatistics::getUserClientStatistics(req);
153 break;
154 default:
155 break;
156 }
157
158 return error;
159}
160
161SYSCTL_NODE(_debug, OID_AUTO, iokit_statistics, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "IOStatistics");
162
163static 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", "");
166
167static 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", "");
170
171static 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", "");
174
175void IOStatistics::initialize()
176{
177 if (enabled) {
178 return;
179 }
180
181#if DEVELOPMENT || DEBUG
182 /* Always enabled in development and debug builds. */
183#else
184 /* Only enabled in release builds if the boot argument is set. */
185 if (!(kIOStatistics & gIOKitDebug)) {
186 return;
187 }
188#endif
189
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);
193
194 lock = IORWLockAlloc();
195 if (!lock) {
196 return;
197 }
198
199 nextWorkLoopDependency = (IOWorkLoopDependency*)kalloc(sizeof(IOWorkLoopDependency));
200 if (!nextWorkLoopDependency) {
201 return;
202 }
203
204 enabled = true;
205}
206
207void IOStatistics::onKextLoad(OSKext *kext, kmod_info_t *kmod_info)
208{
209 KextNode *ke;
210
211 assert(kext && kmod_info);
212
213 if (!enabled) {
214 return;
215 }
216
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));
219
220 ke = (KextNode *)kalloc(sizeof(KextNode));
221 if (!ke) {
222 return;
223 }
224
225 memset(ke, 0, sizeof(KextNode));
226
227 ke->kext = kext;
228 ke->loadTag = kmod_info->id;
229 ke->address = kmod_info->address;
230 ke->address_end = kmod_info->address + kmod_info->size;
231
232 SLIST_INIT(&ke->classList);
233 TAILQ_INIT(&ke->userClientCallList);
234
235 IORWLockWrite(lock);
236
237 RB_INSERT(KextTree, &kextHead, ke);
238 RB_INSERT(KextAddressTree, &kextAddressHead, ke);
239
240 sequenceID++;
241 loadedKexts++;
242 lastKextIndex++;
243
244 IORWLockUnlock(lock);
245}
246
247void IOStatistics::onKextUnload(OSKext *kext)
248{
249 KextNode sought, *found;
250
251 assert(kext);
252
253 if (!enabled) {
254 return;
255 }
256
257 LOG(1, "IOStatistics::onKextUnload: %s\n", kext->getIdentifierCString());
258
259 IORWLockWrite(lock);
260
261 sought.kext = kext;
262 found = RB_FIND(KextTree, &kextHead, &sought);
263 if (found) {
264 IOWorkLoopCounter *wlc;
265 IOUserClientProcessEntry *uce;
266
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));
271 }
272
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));
277 }
278
279 /* Remove from kext trees */
280 RB_REMOVE(KextTree, &kextHead, found);
281 RB_REMOVE(KextAddressTree, &kextAddressHead, found);
282
283 /*
284 * Clear a matching kextHint to avoid use after free in
285 * onClassAdded() for a class add after a KEXT unload.
286 */
287 if (found == kextHint) {
288 kextHint = NULL;
289 }
290
291 /* Finally, free the class node */
292 kfree(found, sizeof(KextNode));
293
294 sequenceID++;
295 loadedKexts--;
296 }
297 else {
298 panic("IOStatistics::onKextUnload: cannot find kext: %s", kext->getIdentifierCString());
299 }
300
301 IORWLockUnlock(lock);
302}
303
304void IOStatistics::onClassAdded(OSKext *parentKext, OSMetaClass *metaClass)
305{
306 ClassNode *ce;
307 KextNode soughtKext, *foundKext = NULL;
308
309 assert(parentKext && metaClass);
310
311 if (!enabled) {
312 return;
313 }
314
315 LOG(1, "IOStatistics::onClassAdded: %s\n", metaClass->getClassName());
316
317 ce = (ClassNode *)kalloc(sizeof(ClassNode));
318 if (!ce) {
319 return;
320 }
321
322 memset(ce, 0, sizeof(ClassNode));
323
324 IORWLockWrite(lock);
325
326 /* Hinted? */
327 if (kextHint && kextHint->kext == parentKext) {
328 foundKext = kextHint;
329 }
330 else {
331 soughtKext.kext = parentKext;
332 foundKext = RB_FIND(KextTree, &kextHead, &soughtKext);
333 }
334
335 if (foundKext) {
336 ClassNode soughtClass, *foundClass = NULL;
337 const OSMetaClass *superClass;
338
339 ce->metaClass = metaClass;
340 ce->classID = lastClassIndex++;
341 ce->parentKext = foundKext;
342
343 /* Has superclass? */
344 superClass = ce->metaClass->getSuperClass();
345 if (superClass) {
346 soughtClass.metaClass = superClass;
347 foundClass = RB_FIND(ClassTree, &classHead, &soughtClass);
348 }
349 ce->superClassID = foundClass ? foundClass->classID : (uint32_t)(-1);
350
351 SLIST_INIT(&ce->counterList);
352 SLIST_INIT(&ce->userClientList);
353
354 RB_INSERT(ClassTree, &classHead, ce);
355 SLIST_INSERT_HEAD(&foundKext->classList, ce, lLink);
356
357 foundKext->classes++;
358
359 kextHint = foundKext;
360
361 sequenceID++;
362 registeredClasses++;
363 }
364 else {
365 panic("IOStatistics::onClassAdded: cannot find parent kext: %s", parentKext->getIdentifierCString());
366 }
367
368 IORWLockUnlock(lock);
369}
370
371void IOStatistics::onClassRemoved(OSKext *parentKext, OSMetaClass *metaClass)
372{
373 ClassNode sought, *found;
374
375 assert(parentKext && metaClass);
376
377 if (!enabled) {
378 return;
379 }
380
381 LOG(1, "IOStatistics::onClassRemoved: %s\n", metaClass->getClassName());
382
383 IORWLockWrite(lock);
384
385 sought.metaClass = metaClass;
386 found = RB_FIND(ClassTree, &classHead, &sought);
387 if (found) {
388 IOEventSourceCounter *esc;
389 IOUserClientCounter *ucc;
390
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));
395 }
396
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));
401 }
402
403 /* Remove from class tree */
404 RB_REMOVE(ClassTree, &classHead, found);
405
406 /* Remove from parent */
407 SLIST_REMOVE(&found->parentKext->classList, found, ClassNode, lLink);
408
409 /* Finally, free the class node */
410 kfree(found, sizeof(ClassNode));
411
412 sequenceID++;
413 registeredClasses--;
414 }
415 else {
416 panic("IOStatistics::onClassRemoved: cannot find class: %s", metaClass->getClassName());
417 }
418
419 IORWLockUnlock(lock);
420}
421
422IOEventSourceCounter *IOStatistics::registerEventSource(OSObject *inOwner)
423{
424 IOEventSourceCounter *counter = NULL;
425 ClassNode sought, *found = NULL;
426 boolean_t createDummyCounter = FALSE;
427
428 assert(inOwner);
429
430 if (!enabled) {
431 return NULL;
432 }
433
434 counter = (IOEventSourceCounter*)kalloc(sizeof(IOEventSourceCounter));
435 if (!counter) {
436 return NULL;
437 }
438
439 memset(counter, 0, sizeof(IOEventSourceCounter));
440
441 IORWLockWrite(lock);
442
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.
445 */
446 if (inOwner->retainCount > 0xFFFFFF) {
447 kprintf("IOStatistics::registerEventSource - bad metaclass %p\n", inOwner);
448 createDummyCounter = TRUE;
449 }
450 else {
451 sought.metaClass = inOwner->getMetaClass();
452 found = RB_FIND(ClassTree, &classHead, &sought);
453 }
454
455 if (found) {
456 counter->parentClass = found;
457 SLIST_INSERT_HEAD(&found->counterList, counter, link);
458 registeredCounters++;
459 }
460
461 if (!(createDummyCounter || found)) {
462 panic("IOStatistics::registerEventSource: cannot find parent class: %s", inOwner->getMetaClass()->getClassName());
463 }
464
465 IORWLockUnlock(lock);
466
467 return counter;
468}
469
470void IOStatistics::unregisterEventSource(IOEventSourceCounter *counter)
471{
472 if (!counter) {
473 return;
474 }
475
476 IORWLockWrite(lock);
477
478 if (counter->parentClass) {
479 SLIST_REMOVE(&counter->parentClass->counterList, counter, IOEventSourceCounter, link);
480 registeredCounters--;
481 }
482 kfree(counter, sizeof(IOEventSourceCounter));
483
484 IORWLockUnlock(lock);
485}
486
487IOWorkLoopCounter* IOStatistics::registerWorkLoop(IOWorkLoop *workLoop)
488{
489 IOWorkLoopCounter *counter = NULL;
490 KextNode *found;
491
492 assert(workLoop);
493
494 if (!enabled) {
495 return NULL;
496 }
497
498 counter = (IOWorkLoopCounter*)kalloc(sizeof(IOWorkLoopCounter));
499 if (!counter) {
500 return NULL;
501 }
502
503 memset(counter, 0, sizeof(IOWorkLoopCounter));
504
505 found = getKextNodeFromBacktrace(TRUE);
506 if (!found) {
507 panic("IOStatistics::registerWorkLoop: cannot find parent kext");
508 }
509
510 counter->parentKext = found;
511 counter->workLoop = workLoop;
512 RB_INIT(&counter->dependencyHead);
513 SLIST_INSERT_HEAD(&found->workLoopList, counter, link);
514 registeredWorkloops++;
515
516 releaseKextNode(found);
517
518 return counter;
519}
520
521void IOStatistics::unregisterWorkLoop(IOWorkLoopCounter *counter)
522{
523 if (!counter) {
524 return;
525 }
526
527 IORWLockWrite(lock);
528
529 SLIST_REMOVE(&counter->parentKext->workLoopList, counter, IOWorkLoopCounter, link);
530 kfree(counter, sizeof(IOWorkLoopCounter));
531 registeredWorkloops--;
532
533 IORWLockUnlock(lock);
534}
535
536IOUserClientCounter *IOStatistics::registerUserClient(IOUserClient *userClient)
537{
538 ClassNode sought, *found;
539 IOUserClientCounter *counter = NULL;
540
541 assert(userClient);
542
543 if (!enabled) {
544 return NULL;
545 }
546
547 counter = (IOUserClientCounter*)kalloc(sizeof(IOUserClientCounter));
548 if (!counter) {
549 return NULL;
550 }
551
552 memset(counter, 0, sizeof(IOUserClientCounter));
553
554 IORWLockWrite(lock);
555
556 sought.metaClass = userClient->getMetaClass();
557
558 found = RB_FIND(ClassTree, &classHead, &sought);
559 if (found) {
560 counter->parentClass = found;
561 SLIST_INSERT_HEAD(&found->userClientList, counter, link);
562 }
563 else {
564 panic("IOStatistics::registerUserClient: cannot find parent class: %s", sought.metaClass->getClassName());
565 }
566
567 IORWLockUnlock(lock);
568
569 return counter;
570}
571
572void IOStatistics::unregisterUserClient(IOUserClientCounter *counter)
573{
574 if (!counter) {
575 return;
576 }
577
578 IORWLockWrite(lock);
579
580 SLIST_REMOVE(&counter->parentClass->userClientList, counter, IOUserClientCounter, link);
581 kfree(counter, sizeof(IOUserClientCounter));
582
583 IORWLockUnlock(lock);
584}
585
586void IOStatistics::attachWorkLoopEventSource(IOWorkLoopCounter *wlc, IOEventSourceCounter *esc)
587{
588 if (!wlc) {
589 return;
590 }
591
592 IORWLockWrite(lock);
593
594 if (!nextWorkLoopDependency) {
595 return;
596 }
597
598 attachedEventSources++;
599 wlc->attachedEventSources++;
600
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));
605 }
606
607 IORWLockUnlock(lock);
608}
609
610void IOStatistics::detachWorkLoopEventSource(IOWorkLoopCounter *wlc, IOEventSourceCounter *esc)
611{
612 IOWorkLoopDependency sought, *found;
613
614 if (!wlc) {
615 return;
616 }
617
618 IORWLockWrite(lock);
619
620 attachedEventSources--;
621 wlc->attachedEventSources--;
622
623 sought.loadTag = esc->parentClass->parentKext->loadTag;
624
625 found = RB_FIND(IOWorkLoopCounter::DependencyTree, &wlc->dependencyHead, &sought);
626 if (found) {
627 RB_REMOVE(IOWorkLoopCounter::DependencyTree, &wlc->dependencyHead, found);
628 kfree(found, sizeof(IOWorkLoopDependency));
629 }
630
631 IORWLockUnlock(lock);
632}
633
634int IOStatistics::getStatistics(sysctl_req *req)
635{
636 int error;
637 uint32_t calculatedSize, size;
638 char *buffer, *ptr;
639 IOStatisticsHeader *header;
640
641 assert(IOStatistics::enabled && req);
642
643 IORWLockRead(IOStatistics::lock);
644
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);
654
655 /* Size request? */
656 if (req->oldptr == USER_ADDR_NULL) {
657 error = SYSCTL_OUT(req, NULL, calculatedSize);
658 goto exit;
659 }
660
661 /* Read only */
662 if (req->newptr != USER_ADDR_NULL) {
663 error = EPERM;
664 goto exit;
665 }
666
667 buffer = (char*)kalloc(calculatedSize);
668 if (!buffer) {
669 error = ENOMEM;
670 goto exit;
671 }
672
673 memset(buffer, 0, calculatedSize);
674
675 ptr = buffer;
676
677 header = (IOStatisticsHeader*)((void*)ptr);
678
679 header->sig = IOSTATISTICS_SIG;
680 header->ver = IOSTATISTICS_VER;
681
682 header->seq = sequenceID;
683
684 ptr += sizeof(IOStatisticsHeader);
685
686 /* Global data - seq, timers, interrupts, etc) */
687 header->globalStatsOffset = sizeof(IOStatisticsHeader);
688 size = copyGlobalStatistics((IOStatisticsGlobal*)((void*)ptr));
689 ptr += size;
690
691 /* Kext statistics */
692 header->kextStatsOffset = header->globalStatsOffset + size;
693 size = copyKextStatistics((IOStatisticsKext*)((void*)ptr));
694 ptr += size;
695
696 /* Memory allocation info */
697 header->memoryStatsOffset = header->kextStatsOffset + size;
698 size = copyMemoryStatistics((IOStatisticsMemory*)((void*)ptr));
699 ptr += size;
700
701 /* Class statistics */
702 header->classStatsOffset = header->memoryStatsOffset + size;
703 size = copyClassStatistics((IOStatisticsClass*)((void*)ptr));
704 ptr += size;
705
706 /* Dynamic class counter data */
707 header->counterStatsOffset = header->classStatsOffset + size;
708 size = copyCounterStatistics((IOStatisticsCounter*)((void*)ptr));
709 ptr += size;
710
711 /* Kext identifiers */
712 header->kextIdentifiersOffset = header->counterStatsOffset + size;
713 size = copyKextIdentifiers((IOStatisticsKextIdentifier*)((void*)ptr));
714 ptr += size;
715
716 /* Class names */
717 header->classNamesOffset = header->kextIdentifiersOffset + size;
718 size = copyClassNames((IOStatisticsClassName*)ptr);
719 ptr += size;
720
721 LOG(2, "IOStatistics::getStatistics - calculatedSize 0x%x, kexts 0x%x, classes 0x%x.\n",
722 calculatedSize, loadedKexts, registeredClasses);
723
724 assert( (uint32_t)(ptr - buffer) == calculatedSize );
725
726 error = SYSCTL_OUT(req, buffer, calculatedSize);
727
728 kfree(buffer, calculatedSize);
729
730exit:
731 IORWLockUnlock(IOStatistics::lock);
732 return error;
733}
734
735int IOStatistics::getWorkLoopStatistics(sysctl_req *req)
736{
737 int error;
738 uint32_t calculatedSize, size;
739 char *buffer;
740 IOStatisticsWorkLoopHeader *header;
741
742 assert(IOStatistics::enabled && req);
743
744 IORWLockRead(IOStatistics::lock);
745
746 /* Approximate how much we need to allocate (worse case estimate) */
747 calculatedSize = sizeof(IOStatisticsWorkLoop) * registeredWorkloops +
748 sizeof(uint32_t) * attachedEventSources;
749
750 /* Size request? */
751 if (req->oldptr == USER_ADDR_NULL) {
752 error = SYSCTL_OUT(req, NULL, calculatedSize);
753 goto exit;
754 }
755
756 /* Read only */
757 if (req->newptr != USER_ADDR_NULL) {
758 error = EPERM;
759 goto exit;
760 }
761
762 buffer = (char*)kalloc(calculatedSize);
763 if (!buffer) {
764 error = ENOMEM;
765 goto exit;
766 }
767
768 header = (IOStatisticsWorkLoopHeader*)((void*)buffer);
769
770 header->sig = IOSTATISTICS_SIG_WORKLOOP;
771 header->ver = IOSTATISTICS_VER;
772
773 header->seq = sequenceID;
774
775 header->workloopCount = registeredWorkloops;
776
777 size = copyWorkLoopStatistics(&header->workLoopStats);
778
779 LOG(2, "IOStatistics::getWorkLoopStatistics: calculatedSize %d, size %d\n", calculatedSize, size);
780
781 assert( size <= calculatedSize );
782
783 error = SYSCTL_OUT(req, buffer, size);
784
785 kfree(buffer, calculatedSize);
786
787exit:
788 IORWLockUnlock(IOStatistics::lock);
789 return error;
790}
791
792int IOStatistics::getUserClientStatistics(sysctl_req *req)
793{
794 int error;
795 uint32_t calculatedSize, size;
796 char *buffer;
797 uint32_t requestedLoadTag = 0;
798 IOStatisticsUserClientHeader *header;
799
800 assert(IOStatistics::enabled && req);
801
802 IORWLockRead(IOStatistics::lock);
803
804 /* Work out how much we need to allocate */
805 calculatedSize = sizeof(IOStatisticsUserClientHeader) +
806 sizeof(IOStatisticsUserClientCall) * IOKIT_STATISTICS_RECORDED_USERCLIENT_PROCS * loadedKexts;
807
808 /* Size request? */
809 if (req->oldptr == USER_ADDR_NULL) {
810 error = SYSCTL_OUT(req, NULL, calculatedSize);
811 goto exit;
812 }
813
814 /* Kext request (potentially) valid? */
815 if (!req->newptr || req->newlen < sizeof(requestedLoadTag)) {
816 error = EINVAL;
817 goto exit;
818 }
819
820 SYSCTL_IN(req, &requestedLoadTag, sizeof(requestedLoadTag));
821
822 LOG(2, "IOStatistics::getUserClientStatistics - requesting kext w/load tag: %d\n", requestedLoadTag);
823
824 buffer = (char*)kalloc(calculatedSize);
825 if (!buffer) {
826 error = ENOMEM;
827 goto exit;
828 }
829
830 header = (IOStatisticsUserClientHeader*)((void*)buffer);
831
832 header->sig = IOSTATISTICS_SIG_USERCLIENT;
833 header->ver = IOSTATISTICS_VER;
834
835 header->seq = sequenceID;
836
837 header->processes = 0;
838
839 size = copyUserClientStatistics(header, requestedLoadTag);
840
841 assert((sizeof(IOStatisticsUserClientHeader) + size) <= calculatedSize);
842
843 if (size) {
844 error = SYSCTL_OUT(req, buffer, sizeof(IOStatisticsUserClientHeader) + size);
845 }
846 else {
847 error = EINVAL;
848 }
849
850 kfree(buffer, calculatedSize);
851
852exit:
853 IORWLockUnlock(IOStatistics::lock);
854 return error;
855}
856
857uint32_t IOStatistics::copyGlobalStatistics(IOStatisticsGlobal *stats)
858{
859 stats->kextCount = loadedKexts;
860 stats->classCount = registeredClasses;
861 stats->workloops = registeredWorkloops;
862
863 return sizeof(IOStatisticsGlobal);
864}
865
866uint32_t IOStatistics::copyKextStatistics(IOStatisticsKext *stats)
867{
868 KextNode *ke;
869 ClassNode *ce;
870 uint32_t index = 0;
871
872 RB_FOREACH(ke, KextTree, &kextHead) {
873 stats->loadTag = ke->loadTag;
874 ke->kext->getSizeInfo(&stats->loadSize, &stats->wiredSize);
875
876 stats->classes = ke->classes;
877
878 /* Append indices of owned classes */
879 SLIST_FOREACH(ce, &ke->classList, lLink) {
880 stats->classIndexes[index++] = ce->classID;
881 }
882
883 stats = (IOStatisticsKext *)((void*)((char*)stats + sizeof(IOStatisticsKext) + (ke->classes * sizeof(uint32_t))));
884 }
885
886 return (sizeof(IOStatisticsKext) * loadedKexts + sizeof(uint32_t) * registeredClasses);
887}
888
889uint32_t IOStatistics::copyMemoryStatistics(IOStatisticsMemory *stats)
890{
891 KextNode *ke;
892
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];
902 stats++;
903 }
904
905 return (sizeof(IOStatisticsMemory) * loadedKexts);
906}
907
908uint32_t IOStatistics::copyClassStatistics(IOStatisticsClass *stats)
909{
910 KextNode *ke;
911 ClassNode *ce;
912
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();
918
919 stats++;
920 }
921 }
922
923 return sizeof(IOStatisticsClass) * registeredClasses;
924}
925
926uint32_t IOStatistics::copyCounterStatistics(IOStatisticsCounter *stats)
927{
928 KextNode *ke;
929 ClassNode *ce;
930
931 RB_FOREACH(ke, KextTree, &kextHead) {
932 SLIST_FOREACH(ce, &ke->classList, lLink) {
933 IOUserClientCounter *userClientCounter;
934 IOEventSourceCounter *counter;
935
936 stats->classID = ce->classID;
937 stats->classInstanceCount = ce->metaClass->getInstanceCount();
938
939 IOStatisticsUserClients *uc = &stats->userClientStatistics;
940
941 /* User client counters */
942 SLIST_FOREACH(userClientCounter, &ce->userClientList, link) {
943 uc->clientCalls += userClientCounter->clientCalls;
944 uc->created++;
945 }
946
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;
953
954 /* Event source counters */
955 SLIST_FOREACH(counter, &ce->counterList, link) {
956 switch (counter->type) {
957 case kIOStatisticsInterruptEventSourceCounter:
958 iec->created++;
959 iec->produced += counter->u.interrupt.produced;
960 iec->checksForWork += counter->u.interrupt.checksForWork;
961 break;
962 case kIOStatisticsFilterInterruptEventSourceCounter:
963 fiec->created++;
964 fiec->produced += counter->u.filter.produced;
965 fiec->checksForWork += counter->u.filter.checksForWork;
966 break;
967 case kIOStatisticsTimerEventSourceCounter:
968 tec->created++;
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;
974 break;
975 case kIOStatisticsCommandGateCounter:
976 cgc->created++;
977 cgc->timeOnGate += counter->timeOnGate;
978 cgc->actionCalls += counter->u.commandGate.actionCalls;
979 break;
980 case kIOStatisticsCommandQueueCounter:
981 cqc->created++;
982 cqc->actionCalls += counter->u.commandQueue.actionCalls;
983 break;
984 case kIOStatisticsDerivedEventSourceCounter:
985 dec->created++;
986 dec->timeOnGate += counter->timeOnGate;
987 dec->closeGateCalls += counter->closeGateCalls;
988 dec->openGateCalls += counter->openGateCalls;
989 break;
990 default:
991 break;
992 }
993 }
994
995 stats++;
996 }
997 }
998
999 return sizeof(IOStatisticsCounter) * registeredClasses;
1000}
1001
1002uint32_t IOStatistics::copyKextIdentifiers(IOStatisticsKextIdentifier *kextIDs)
1003{
1004 KextNode *ke;
1005
1006 RB_FOREACH(ke, KextTree, &kextHead) {
1007 strncpy(kextIDs->identifier, ke->kext->getIdentifierCString(), kIOStatisticsDriverNameLength);
1008 kextIDs++;
1009 }
1010
1011 return (sizeof(IOStatisticsKextIdentifier) * loadedKexts);
1012}
1013
1014uint32_t IOStatistics::copyClassNames(IOStatisticsClassName *classNames)
1015{
1016 KextNode *ke;
1017 ClassNode *ce;
1018
1019 RB_FOREACH(ke, KextTree, &kextHead) {
1020 SLIST_FOREACH(ce, &ke->classList, lLink) {
1021 strncpy(classNames->name, ce->metaClass->getClassName(), kIOStatisticsClassNameLength);
1022 classNames++;
1023 }
1024 }
1025
1026 return (sizeof(IOStatisticsClassName) * registeredClasses);
1027}
1028
1029uint32_t IOStatistics::copyWorkLoopStatistics(IOStatisticsWorkLoop *stats)
1030{
1031 KextNode *ke;
1032 IOWorkLoopCounter *wlc;
1033 IOWorkLoopDependency *dependentNode;
1034 uint32_t size, accumulatedSize = 0;
1035
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++;
1045 }
1046
1047 size = sizeof(IOStatisticsWorkLoop) + (sizeof(uint32_t) * stats->dependentKexts);
1048
1049 accumulatedSize += size;
1050 stats = (IOStatisticsWorkLoop*)((void*)((char*)stats + size));
1051 }
1052 }
1053
1054 return accumulatedSize;
1055}
1056
1057uint32_t IOStatistics::copyUserClientStatistics(IOStatisticsUserClientHeader *stats, uint32_t loadTag)
1058{
1059 KextNode *sought, *found = NULL;
1060 uint32_t procs = 0;
1061 IOUserClientProcessEntry *processEntry;
1062
1063 RB_FOREACH(sought, KextTree, &kextHead) {
1064 if (sought->loadTag == loadTag) {
1065 found = sought;
1066 break;
1067 }
1068 }
1069
1070 if (!found) {
1071 return 0;
1072 }
1073
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;
1078 stats->processes++;
1079 procs++;
1080 }
1081
1082 return sizeof(IOStatisticsUserClientCall) * stats->processes;
1083}
1084
1085void IOStatistics::storeUserClientCallInfo(IOUserClient *userClient, IOUserClientCounter *counter)
1086{
1087 OSString *ossUserClientCreator = NULL;
1088 int32_t pid = -1;
1089 KextNode *parentKext;
1090 IOUserClientProcessEntry *entry, *nextEntry, *prevEntry = NULL;
1091 uint32_t count = 0;
1092 const char *ptr = NULL;
1093 OSObject *obj;
1094
1095 /* TODO: see if this can be more efficient */
1096 obj = userClient->copyProperty("IOUserClientCreator",
1097 gIOServicePlane,
1098 kIORegistryIterateRecursively | kIORegistryIterateParents);
1099
1100 if (!obj)
1101 goto err_nounlock;
1102
1103 ossUserClientCreator = OSDynamicCast(OSString, obj);
1104
1105 if (ossUserClientCreator) {
1106 uint32_t len, lenIter = 0;
1107
1108 ptr = ossUserClientCreator->getCStringNoCopy();
1109 len = ossUserClientCreator->getLength();
1110
1111 while ((*ptr != ' ') && (lenIter < len)) {
1112 ptr++;
1113 lenIter++;
1114 }
1115
1116 if (lenIter < len) {
1117 ptr++; // Skip the space
1118 lenIter++;
1119 pid = 0;
1120 while ( (*ptr != ',') && (lenIter < len)) {
1121 pid = pid*10 + (*ptr - '0');
1122 ptr++;
1123 lenIter++;
1124 }
1125
1126 if(lenIter == len) {
1127 pid = -1;
1128 } else {
1129 ptr += 2;
1130 }
1131 }
1132 }
1133
1134 if (-1 == pid)
1135 goto err_nounlock;
1136
1137 IORWLockWrite(lock);
1138
1139 parentKext = counter->parentClass->parentKext;
1140
1141 TAILQ_FOREACH(entry, &parentKext->userClientCallList, link) {
1142 if (entry->pid == pid) {
1143 /* Found, so increment count and move to the head */
1144 entry->calls++;
1145 if (count) {
1146 TAILQ_REMOVE(&parentKext->userClientCallList, entry, link);
1147 break;
1148 }
1149 else {
1150 /* At the head already, so increment and return */
1151 goto err_unlock;
1152 }
1153 }
1154
1155 count++;
1156 }
1157
1158 if (!entry) {
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);
1163 }
1164 else {
1165 /* Otherwise, allocate a new entry */
1166 entry = (IOUserClientProcessEntry*)kalloc(sizeof(IOUserClientProcessEntry));
1167 if (!entry) {
1168 IORWLockUnlock(lock);
1169 return;
1170 }
1171 }
1172
1173 strncpy(entry->processName, ptr, kIOStatisticsProcessNameLength);
1174 entry->pid = pid;
1175 entry->calls = 1;
1176 }
1177
1178 TAILQ_FOREACH(nextEntry, &parentKext->userClientCallList, link) {
1179 if (nextEntry->calls <= entry->calls)
1180 break;
1181
1182 prevEntry = nextEntry;
1183 }
1184
1185 if (!prevEntry)
1186 TAILQ_INSERT_HEAD(&parentKext->userClientCallList, entry, link);
1187 else
1188 TAILQ_INSERT_AFTER(&parentKext->userClientCallList, prevEntry, entry, link);
1189
1190err_unlock:
1191 IORWLockUnlock(lock);
1192
1193err_nounlock:
1194 if (obj)
1195 obj->release();
1196}
1197
1198void IOStatistics::countUserClientCall(IOUserClient *client) {
1199 IOUserClient::ExpansionData *data;
1200 IOUserClientCounter *counter;
1201
1202 /* Guard against an uninitialized client object - <rdar://problem/8577946> */
1203 if (!(data = client->reserved)) {
1204 return;
1205 }
1206
1207 if ((counter = data->counter)) {
1208 storeUserClientCallInfo(client, counter);
1209 OSIncrementAtomic(&counter->clientCalls);
1210 }
1211}
1212
1213KextNode *IOStatistics::getKextNodeFromBacktrace(boolean_t write) {
1214 const uint32_t btMin = 3;
1215
1216 void *bt[16];
1217 unsigned btCount = sizeof(bt) / sizeof(bt[0]);
1218 vm_offset_t *scanAddr = NULL;
1219 uint32_t i;
1220 KextNode *found = NULL, *ke = NULL;
1221
1222 btCount = OSBacktrace(bt, btCount);
1223
1224 if (write) {
1225 IORWLockWrite(lock);
1226 } else {
1227 IORWLockRead(lock);
1228 }
1229
1230 /* Ignore first levels */
1231 scanAddr = (vm_offset_t *)&bt[btMin - 1];
1232
1233 for (i = 0; i < btCount; i++, scanAddr++) {
1234 ke = RB_ROOT(&kextAddressHead);
1235 while (ke) {
1236 if (*scanAddr < ke->address) {
1237 ke = RB_LEFT(ke, addressLink);
1238 }
1239 else {
1240 if ((*scanAddr < ke->address_end) && (*scanAddr >= ke->address)) {
1241 if (!ke->kext->isKernelComponent()) {
1242 return ke;
1243 } else {
1244 found = ke;
1245 }
1246 }
1247 ke = RB_RIGHT(ke, addressLink);
1248 }
1249 }
1250 }
1251
1252 if (!found) {
1253 IORWLockUnlock(lock);
1254 }
1255
1256 return found;
1257}
1258
1259void IOStatistics::releaseKextNode(KextNode *node) {
1260#pragma unused(node)
1261 IORWLockUnlock(lock);
1262}
1263
1264/* IOLib allocations */
1265void IOStatistics::countAlloc(uint32_t index, vm_size_t size) {
1266 KextNode *ke;
1267
1268 if (!enabled) {
1269 return;
1270 }
1271
1272 ke = getKextNodeFromBacktrace(FALSE);
1273 if (ke) {
1274 OSAddAtomic(size, &ke->memoryCounters[index]);
1275 releaseKextNode(ke);
1276 }
1277}
1278
1279#endif /* IOKITSTATS */