views:

1288

answers:

3

Hi all,

I'm working on tracking down some difficult to find memory leaks in my iPhone program. I'm running a quick test on an app which leaks an NSString object with the following intentionally -incorrect- code:

-(void)applicationDidFinishLaunching:(NSNotification *)notification;
{
    NSMutableString *test = [[NSMutableString alloc] init];
    [test appendString:@"Testing 1"];
    [test appendString:@"\nTesting 2"];
    NSLog(@"%@", test);

    // Uncomment the following line to release the
    // string and clean up your leak.
    // [test release], test = nil;
}

After running leaks on the process ID of the application, I come up with the following:

sf$ leaks 3951
Process 3951: 9988 nodes malloced for 1260 KB
Process 3951: 3 leaks for 128 total leaked bytes.
Leak: 0x163b50  size=64 instance of 'NSCFDictionary', type ObjC, implemented in Foundation 
 0xa07e0720 0x01001080 0x0000000a 0x00000010   .~.............
 0x0000000a 0x0000000c 0x0000000b 0x00000000  ................
 0x00000000 0x00000015 0xa1b1c1d3 0x00163b90  .............;..
 0x00163bd0 0x00000000 0x00000000 0x00000000  .;..............
Leak: 0x178190  size=32 string 'Testing 1
Testing 2'
Leak: 0x178210  size=32 instance of 'NSCFString', type ObjC, implemented in CoreFoundation 
 0xa02e24a0 0x010007ad 0x00178190 0x00000013  .$..............
 0x00000020 0x00000200 0x00000000 0x00000000   ...............

Now, we all know where the leak is. That's not the point of this exercise, for me at least. I'm trying to recognize how to make sense of this output. I'm being told that there are 3 leaks.

They're objects which are located at the memory addresses 0x163b50, 0x178190, 0x178210. Their implementation is in Apple frameworks, not my code -- according to 'leaks'. In a trivial example such as the following, finding the leak isn't difficult. However, in an application with 500K lines of code, I find the output from leaks here useless.

What am I doing wrong and how can I make sense of this output to help me find the culprit in the code I've written to clean up my memory leaks?

Please note that this thread shouldn't advocate the usage of Instruments or Clang Static Analyzer. I've cleaned up all memory leaks which Clang Static Analyzer has reported to me. Instruments is bloated and non-informative to me. I'm getting a ton of reports for leaks, none of which the stack traces are showing go back to my own code -- although I'm certain the leak(s) are indeed in my code. I'd like to figure out how to use the cmd line leaks tool here.

Thanks all.

EDIT: Even after uncommenting the lines to clean up the leaks, the 'leaks' utility complains even more than it did when there was leaks. Is Foundation/Cocoa leaking that much from such a trivial example? This occurs after uncommenting the release for the test string above:

sf$ leaks 4383
Process 4383: 9890 nodes malloced for 1255 KB
Process 4383: 7 leaks for 560 total leaked bytes.
Leak: 0x163920  size=176 instance of 'NSPathStore2', type ObjC, implemented in Foundation 
 0xa07e2ae0 0x04f00000 0x0055002f 0x00650073  .*~...../.U.s.e.
 0x00730072 0x0073002f 0x002f0066 0x0069004c  r.s./.s.f./.L.i.
 0x00720062 0x00720061 0x002f0079 0x00700041  b.r.a.r.y./.A.p.
 0x006c0070 0x00630069 0x00740061 0x006f0069  p.l.i.c.a.t.i.o.
 0x0020006e 0x00750053 0x00700070 0x0072006f  n. .S.u.p.p.o.r.
 0x002f0074 0x00490053 0x0042004d 0x002f004c  t./.S.I.M.B.L./.
 0x006c0050 0x00670075 0x006e0069 0x002f0073  P.l.u.g.i.n.s./.
 0x00650054 0x006d0072 0x006e0069 0x006c0061  T.e.r.m.i.n.a.l.
 ...
Leak: 0x163350  size=160 instance of 'NSPathStore2', type ObjC, implemented in Foundation 
 0xa07e2ae0 0x04a00000 0x0055002f 0x00650073  .*~...../.U.s.e.
 0x00730072 0x0073002f 0x002f0066 0x0069004c  r.s./.s.f./.L.i.
 0x00720062 0x00720061 0x002f0079 0x00700041  b.r.a.r.y./.A.p.
 0x006c0070 0x00630069 0x00740061 0x006f0069  p.l.i.c.a.t.i.o.
 0x0020006e 0x00750053 0x00700070 0x0072006f  n. .S.u.p.p.o.r.
 0x002f0074 0x00490053 0x0042004d 0x002f004c  t./.S.I.M.B.L./.
 0x006c0050 0x00670075 0x006e0069 0x002f0073  P.l.u.g.i.n.s./.
 0x00650044 0x0069006c 0x00690063 0x0075006f  D.e.l.i.c.i.o.u.
 ...
Leak: 0x1635a0  size=64 instance of 'NSCFDictionary', type ObjC, implemented in Foundation 
 0xa07e0720 0x01001080 0x0000000a 0x00000010   .~.............
 0x0000000a 0x0000000c 0x0000000b 0x00000000  ................
 0x00000000 0x00000015 0xa1b1c1d3 0x001635e0  .............5..
 0x00163620 0x00000000 0x00000000 0x00000000   6..............
Leak: 0x163620  size=64 
 0xa02ed360 0x00160ee0 0x00163700 0xa02efc00  `........7......
 0x00000000 0x00000000 0x00163680 0x00000000  .........6......
 0x00000000 0x00000000 0x00163660 0xa02ed440  ........`6..@...
 0xa02ec1a0 0xa02f0420 0x00000000 0x00163660  .... ./.....`6..
Leak: 0x163680  size=48 instance of 'NSCFString', type ObjC, implemented in CoreFoundation 
 0xa02e24a0 0x0100078c 0x6d6f6323 0x6c65642e  .$......#com.del
 0x6f696369 0x61737375 0x69726166 0x6c65442e  icioussafari.Del
 0x6f696369 0x61537375 0x69726166 0x00000000  iciousSafari....
Leak: 0x163660  size=32 instance of 'NSCFString', type ObjC, implemented in CoreFoundation 
 0xa02e24a0 0x0200078c 0x6c65440f 0x6f696369  .$.......Delicio
 0x61537375 0x69726166 0x00000000 0x00000000  usSafari........
Leak: 0x160ee0  size=16 instance of 'NSCFString', type ObjC, implemented in CoreFoundation 
 0xa02e24a0 0x0100078c 0x362e3103 0x00000000  .$.......1.6....
A: 

For this example, leaks reports 3 leaks for a total of 128 bytes. The interpretation should be as follows:

1) you are leaking a NSMutableString which appears to be implemented internally as NSCFDictionary for a total of 64 bytes

2) you are leaking Testing 1 for a total of 32 bytes

3) you are leaking \nTesting 2 for a total of 32 bytes

This should be because if you do not release your NSMutableString, none of the objects (strings in this case) belonging to the NSCFDictionary data structure will be released: they are retained each time you use the appendString method. When you release your NSMutableString, all of the object inside NSCFDictionary are automatically released, along with the NSCFDictionary itself.

From the Apple documentation (http://developer.apple.com/iPhone/library/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html):

If a Cocoa object is autoreleased without an autorelease pool in place, Xcode sends an a message to the console warning you that the object is just leaking. Even if you are not writing a Cocoa application, it is possible to see this same type of console warning. The implementation of many Cocoa classes is based on Core Foundation types. If your application uses Core Foundation, it is possible that the leaks are occurring as a result of calls to that framework.

To find memory leaks of this type, use the debugger to put a breakpoint on the _NSAutoreleaseNoPool function. This function is declared in NSDebug.h in the Foundation framework. When the debugger reaches that function, you should be able to look at the stack crawl and see what piece of code caused the leak.

unforgiven
+1  A: 

I think the reason you are seeing this in leaks is because appendString is using autorelease internally. The autorelease pool isn't being discarded until the next 'event'. Basically, I think the sample is too simple to accurately show what's going on.

I could be way off base here, but I would try wrapping your code in an autorelease pool to see what changes.

-(void)applicationDidFinishLaunching:(NSNotification *)notification;
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSMutableString *test = [[NSMutableString alloc] init];
    [test appendString:@"Testing 1"];
    [test appendString:@"\nTesting 2"];
    NSLog(@"%@", test);
    [test release], test = nil;
    [pool release] // this should drain the autorelease pool
}
Jab
+2  A: 

There is no stack trace in the output you showed. The addresses you see are the objects' own addresses, not function pointers, and the hex numbers next to the punctuation characters are simply the hex dump of the data.

To find out where the object was allocated from, set MallocStackLogging in leaks's environment:

% MallocStackLogging=1 leaks …

You may also want to use the -nocontent option, which will suppress the hex dump. Don't use this all the time, however: Sometimes the hex dump contains a valuable clue.

Also, leaks is not necessarily telling you that you have three leaks; to be precise, it's telling you that you have three leaked objects. The deliberate leak you showed produces only one leaked object, but a different leak (such as in a loop or frequently-called method) may leak many objects.

Edit: BTW, some of those leaks are from SIMBL or one or more of your SIMBL plug-ins. Turn off SIMBL and any other input manager hacks before leak-hunting. Remember, that code runs in your process; moreover, leaks doesn't care whose code allocated or leaked the memory, only that it's leaked, so it will show the leaked object regardless of who allocated or leaked it.

Peter Hosey
Thanks Peter. I have no idea with SIMBL is, but I'll look it up.