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