Created
December 8, 2012 07:56
-
-
Save gnachman/4239174 to your computer and use it in GitHub Desktop.
Swizzle retain/release/autorelease to debug busted reference counting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Use with https://github.com/rentzsch/jrswizzle | |
// MIT license | |
// After your program dies because with a zombie, examine [theBigLog objectForKey:@"0x12345"], subbing the address of the thing that died. You can get stack traces referred to in the logs by looking up the "stack-12345" numbers in the same dict. To track a particular object, add [NSValue valueWithPointer:myobject] to debugList in a @synchronized block. | |
@interface ClassToDebug : NSColor | |
@end | |
@interface ClassToDebug (DebugRefs) | |
@end | |
static NSMutableSet *debugList; | |
NSMutableDictionary *theBigLog; | |
@implementation ClassToDebug (DebugRefs) | |
- (bool)shouldDebugRefs { | |
@synchronized([NSObject class]) { | |
return [debugList containsObject:[NSValue valueWithPointer:self]]; | |
} | |
} | |
- (void)logDebugRefs:(NSString *)msg { | |
@synchronized([NSObject class]) { | |
NSString *key = [NSString stringWithFormat:@"%p", self]; | |
NSMutableString *s = [theBigLog objectForKey:key]; | |
if (!s) { | |
s = [[NSMutableString alloc] init]; | |
[theBigLog setObject:s forKey:key]; | |
} | |
[s appendString:msg]; | |
} | |
} | |
- (NSString *)debugCallStack { | |
NSString *syms = [[NSThread callStackSymbols] description]; | |
NSUInteger hash = [syms hash]; | |
NSString *key = [NSString stringWithFormat:@"stack-%llu", hash]; | |
@synchronized([NSObject class]) { | |
if (![theBigLog objectForKey:key]) { | |
[theBigLog setObject:syms forKey:key]; | |
} | |
} | |
return key; | |
} | |
- (id)debugRefsRetain { | |
id rv = [self debugRefsRetain]; | |
if ([self shouldDebugRefs]) { | |
[self logDebugRefs:[NSString stringWithFormat:@"Retain %@ from %@ sending rc to %d\n", self, [self debugCallStack], (int)[self retainCount]]]; | |
} | |
return rv; | |
} | |
- (oneway void)debugRefsRelease { | |
if ([self shouldDebugRefs]) { | |
[self logDebugRefs:[NSString stringWithFormat:@"Release %@ from %@ sending rc to %d\n", self, [self debugCallStack], (int)[self retainCount]-1]]; | |
if ([self retainCount] == 1) { | |
@synchronized([NSObject class]) { | |
[debugList removeObject:[NSValue valueWithPointer:self]]; | |
} | |
} | |
} | |
[self debugRefsRelease]; | |
} | |
- (id)debugRefsAutorelease { | |
id rv = [self debugRefsAutorelease]; | |
if ([self shouldDebugRefs]) { | |
[self logDebugRefs:[NSString stringWithFormat:@"Autorelease %@ from %@ sending rc to %d\n", self, [self debugCallStack], (int)[self retainCount]]]; | |
} | |
return rv; | |
} | |
@end | |
void DoSwizzle() { | |
debugList = [[NSMutableSet alloc] init]; | |
theBigLog = [[NSMutableDictionary alloc] init]; | |
NSError *e = nil; | |
[ClassToDebug jr_swizzleMethod:@selector(retain) withMethod:@selector(debugRefsRetain) error:&e]; | |
assert(!e); | |
[ClassToDebug jr_swizzleMethod:@selector(release) withMethod:@selector(debugRefsRelease) error:&e]; | |
assert(!e); | |
[ClassToDebug jr_swizzleMethod:@selector(autorelease) withMethod:@selector(debugRefsAutorelease) error:&e]; | |
assert(!e); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment