xref: /xnu-11215/iokit/Kernel/IOStatistics.cpp (revision 855239e5)
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 
43 bool IOStatistics::enabled = false;
44 
45 uint32_t IOStatistics::sequenceID = 0;
46 
47 uint32_t IOStatistics::lastClassIndex = 0;
48 uint32_t IOStatistics::lastKextIndex = 0;
49 
50 uint32_t IOStatistics::loadedKexts = 0;
51 uint32_t IOStatistics::registeredClasses = 0;
52 uint32_t IOStatistics::registeredCounters = 0;
53 uint32_t IOStatistics::registeredWorkloops = 0;
54 
55 uint32_t IOStatistics::attachedEventSources = 0;
56 
57 IOWorkLoopDependency *IOStatistics::nextWorkLoopDependency = NULL;
58 
59 /* Logging */
60 
61 #define LOG_LEVEL 0
62 
63 #define LOG(level, format, ...) \
64 do { \
65 	if (level <= LOG_LEVEL) \
66 		printf(format, ##__VA_ARGS__); \
67 } while (0)
68 
69 /* Locks */
70 
71 IORWLock *IOStatistics::lock = NULL;
72 
73 /* Kext tree */
74 
75 KextNode *IOStatistics::kextHint = NULL;
76 
77 IOStatistics::KextTreeHead IOStatistics::kextHead = RB_INITIALIZER(&IOStatistics::kextHead);
78 
79 int 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 
89 RB_GENERATE(IOStatistics::KextTree, KextNode, link, kextNodeCompare);
90 
91 /* Kext tree ordered by address */
92 
93 IOStatistics::KextAddressTreeHead IOStatistics::kextAddressHead = RB_INITIALIZER(&IOStatistics::kextAddressHead);
94 
95 int 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 
105 RB_GENERATE(IOStatistics::KextAddressTree, KextNode, addressLink, kextAddressNodeCompare);
106 
107 /* Class tree */
108 
109 IOStatistics::ClassTreeHead IOStatistics::classHead = RB_INITIALIZER(&IOStatistics::classHead);
110 
111 int 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 
120 RB_GENERATE(IOStatistics::ClassTree, ClassNode, tLink, classNodeCompare);
121 
122 /* Workloop dependencies */
123 
124 int 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 
133 RB_GENERATE(IOWorkLoopCounter::DependencyTree, IOWorkLoopDependency, link, IOWorkLoopCounter::loadTagCompare);
134 
135 /* sysctl stuff */
136 
137 static int
138 oid_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 
161 SYSCTL_NODE(_debug, OID_AUTO, iokit_statistics, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "IOStatistics");
162 
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", "");
166 
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", "");
170 
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", "");
174 
175 void 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 
207 void 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 
247 void 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 
304 void 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 
371 void 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 
422 IOEventSourceCounter *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 
470 void 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 
487 IOWorkLoopCounter* 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 
521 void 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 
536 IOUserClientCounter *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 
572 void 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 
586 void 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 
610 void 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 
634 int 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 
730 exit:
731 	IORWLockUnlock(IOStatistics::lock);
732 	return error;
733 }
734 
735 int 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 
787 exit:
788 	IORWLockUnlock(IOStatistics::lock);
789 	return error;
790 }
791 
792 int 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 
852 exit:
853 	IORWLockUnlock(IOStatistics::lock);
854 	return error;
855 }
856 
857 uint32_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 
866 uint32_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 
889 uint32_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 
908 uint32_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 
926 uint32_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 
1002 uint32_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 
1014 uint32_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 
1029 uint32_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 
1057 uint32_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 
1085 void 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 
1190 err_unlock:
1191 	IORWLockUnlock(lock);
1192 
1193 err_nounlock:
1194 	if (obj)
1195 		obj->release();
1196 }
1197 
1198 void 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 
1213 KextNode *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 
1259 void IOStatistics::releaseKextNode(KextNode *node) {
1260 #pragma unused(node)
1261 	IORWLockUnlock(lock);
1262 }
1263 
1264 /* IOLib allocations */
1265 void 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 */
1280