If you've got user client class instances when no user client app is running, then you're definitely retaining the user client instances more often than you're releasing them. For example, you might be keeping a retained reference to client instances in the main driver class. In your user client class's stop()
method, make sure to remove that client instance from the driver.
Another thing to watch out for: make sure you call superclass implementations from your overridden versions of the built-in IOService methods such as stop()
, free()
etc. Not doing so will usually put the IO Kit into an inconsistent state.
Finally, a useful technique for debugging retain leaks in I/O Kit drivers, is to actually log the retains and releases by overriding the methods with logging versions:
void MyClass::taggedRetain(const void* tag) const
{
OSReportWithBacktrace(
"MyClass" CLASS_OBJECT_FORMAT_STRING "::taggedRetain(tag=%p)\n", CLASS_OBJECT_FORMAT(this), tag);
IOService::taggedRetain(tag);
}
void MyClass::taggedRelease(const void * tag) const
{
OSReportWithBacktrace(
"MyClass" CLASS_OBJECT_FORMAT_STRING "::taggedRelease(tag=%p)\n", CLASS_OBJECT_FORMAT(this), tag);
int count = getRetainCount();
IOService::taggedRelease(tag);
if (count == 1)
printf(
"MyClass::taggedRelease(tag=%p) final done\n", tag);
else
printf(
"MyClass" CLASS_OBJECT_FORMAT_STRING "::taggedRelease(tag=%p) done\n", CLASS_OBJECT_FORMAT(this), tag);
}
The macros in this code are defined in a header as follows:
#define CLASS_OBJECT_FORMAT_STRING "[%s@%p:%dx]"
#define CLASS_OBJECT_FORMAT(obj) myClassName(obj), obj, myRefCount(obj)
inline int myRefCount(const OSObject* obj)
{
return obj ? obj->getRetainCount() : 0;
}
inline const char* myClassName(const OSObject* obj)
{
if (!obj) return "(null)";
return obj->getMetaClass()->getClassName();
}
#endif
I should explain that taggedRetain()
and taggedRelease()
are the actual underlying implementation of retain()
and release()
- if you override the latter, you won't see any retains and releases coming from OSCollections, as they use the tagged versions (with a non-null tag).
The backtrace generated by OSReportWithBacktrace()
is unfortunately just a bunch of hex pointers, but you can look those up using gdb.
In any case, by logging retains and releases for your objects, you can go through all retains and make sure they are matched by a release in the right place. Watch out for cycles!
free()
afterdetach()
. I get a call to free if I plug the device in and remove it again without the userspace application open, but if the user client ever gets opened, it won't get a call to free in the end. What could cause that, a retained reference to the driver? – Mesozoic