xref: /xnu-12377.61.12/iokit/Tests/TestServices/TestIOServiceUserNotification.cpp (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 #if DEVELOPMENT || DEBUG
2 #include "TestIOServiceUserNotification.h"
3 #include <IOKit/IOService.h>
4 #include <IOKit/IOUserClient.h>
5 #include <IOKit/IOKitServer.h>
6 #include <kern/ipc_kobject.h>
7 #include "../../Kernel/IOServicePrivate.h"
8 
9 OSDefineMetaClassAndStructors(TestIOServiceUserNotification, IOService);
10 
11 OSDefineMetaClassAndStructors(TestIOServiceUserNotificationUserClient, IOUserClient);
12 
13 bool
start(IOService * provider)14 TestIOServiceUserNotification::start(IOService * provider)
15 {
16 	OSString * str = OSString::withCStringNoCopy("TestIOServiceUserNotificationUserClient");
17 	bool ret = IOService::start(provider);
18 	if (ret && str != NULL) {
19 		setProperty(gIOUserClientClassKey, str);
20 		fUserNotifications = OSArray::withCapacity(1);
21 		fLock = IOLockAlloc();
22 		registerService();
23 	}
24 	OSSafeReleaseNULL(str);
25 	return ret;
26 }
27 
28 void
free()29 TestIOServiceUserNotification::free()
30 {
31 	if (fLock) {
32 		IOLockFree(fLock);
33 		fLock = NULL;
34 	}
35 	OSSafeReleaseNULL(fUserNotifications);
36 	IOService::free();
37 }
38 
39 void
registerUserNotification(OSObject * notification)40 TestIOServiceUserNotification::registerUserNotification(OSObject * notification)
41 {
42 	IOLockLock(fLock);
43 	// Proactively trim the list to avoid holding too many objects
44 	trimUserNotificationsLocked();
45 	assert(fUserNotifications->getNextIndexOfObject(notification, 0) == -1);
46 	fUserNotifications->setObject(notification);
47 	IOLockUnlock(fLock);
48 }
49 
50 void
trimUserNotificationsLocked()51 TestIOServiceUserNotification::trimUserNotificationsLocked()
52 {
53 	OSArray * remaining = OSArray::withCapacity(1);
54 	if (!remaining) {
55 		return;
56 	}
57 	fUserNotifications->iterateObjects(^(OSObject * obj) {
58 		if (obj->getRetainCount() != 1) {
59 		        remaining->setObject(obj);
60 		}
61 		return false;
62 	});
63 	fUserNotifications->release();
64 	fUserNotifications = remaining;
65 }
66 
67 size_t
getUserNotificationLeakCount()68 TestIOServiceUserNotification::getUserNotificationLeakCount()
69 {
70 	size_t count = 0;
71 	IOLockLock(fLock);
72 	trimUserNotificationsLocked();
73 	count = fUserNotifications->getCount();
74 	IOLockUnlock(fLock);
75 	return count;
76 }
77 
78 bool
start(IOService * provider)79 TestIOServiceUserNotificationUserClient::start(IOService * provider)
80 {
81 	if (!IOUserClient::start(provider)) {
82 		return false;
83 	}
84 	fProvider = OSDynamicCast(TestIOServiceUserNotification, provider);
85 	assert(fProvider);
86 	return true;
87 }
88 
89 IONotifier *
registerInterest(const OSSymbol * typeOfInterest,IOServiceInterestHandler handler,void * target,void * ref)90 TestIOServiceUserNotificationUserClient::registerInterest(const OSSymbol * typeOfInterest,
91     IOServiceInterestHandler handler,
92     void * target,
93     void * ref)
94 {
95 	IONotifier * notify = IOService::registerInterest(typeOfInterest, handler, target, ref);
96 
97 	// No straightforward way to make sure registerInterest is called from the test app
98 	// Could check if handler is _ZN32IOServiceMessageUserNotification8_handlerEPvS0_jP9IOServiceS0_m
99 	// But still cannot rule out other user process regisering interest
100 	OSObject * obj = (OSObject *)target;
101 	// Just panic the system if target isn't OSObject
102 	fProvider->registerUserNotification(obj);
103 
104 	return notify;
105 }
106 
107 IOReturn
clientClose()108 TestIOServiceUserNotificationUserClient::clientClose()
109 {
110 	if (!isInactive()) {
111 		terminate();
112 	}
113 	return kIOReturnSuccess;
114 }
115 
116 IOReturn
externalMethod(uint32_t selector,IOExternalMethodArguments * args,IOExternalMethodDispatch * dispatch,OSObject * target,void * reference)117 TestIOServiceUserNotificationUserClient::externalMethod(uint32_t selector, IOExternalMethodArguments * args,
118     IOExternalMethodDispatch * dispatch, OSObject * target, void * reference)
119 {
120 	if (selector == 0) {
121 		registerService();
122 	} else if (selector == 1 && args->scalarOutputCount >= 1) {
123 		args->scalarOutput[0] = fProvider->getUserNotificationLeakCount();
124 	}
125 	return kIOReturnSuccess;
126 }
127 
128 #endif /* DEVELOPMENT || DEBUG */
129