How do I debug an autorelease crash?

  • I got all my code wired together yesterday, which means my problem
    could be anywhere.  Is there an easy way to track down the culprit?  I
    crash with a signal 11 here:

    0    objc_msgSend
    1    NSPopAutoreleasePool
    2    -[NSApplication run]
    3    NSApplicationMain
    4    main

    What exactly causes this crash anyway?  Have I manually released an
    autoreleased object?  It seems like it would be more useful to get a
    signal raised at the time of the error rather than at the end of the
    run loop.

    Dave
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • > What exactly causes this crash anyway?  Have I manually released an
    > autoreleased object?  It seems like it would be more useful to get a
    > signal raised at the time of the error rather than at the end of the
    > run loop.
    >
    > Dave

    My guess is you're not retaining it at one point or another, so it gets
    released. Later on in your code you try to access it, but it's already
    been released, so you're trying to access a nonexistent variable, and
    so it gets mad. To make sure this is the problem, one thing you could
    do is retain it a bunch at one point after being autoreleased, and see
    if your application still crashes. If it doesn't, you should remove the
    "bunch of retains" and then track down where you should be retaining it
    to avoid it being released until it's no longer needed. If it still
    crashes, well... I guess I was wrong. Sorry :-(

    - - -

    Dave Keck
    If I were smart I'd own a company to tell you the the name of.
    "Contrariwise," continued Tweedledee, "if it was so, it might  be; and
    if it were so, it would be; but as it isn't, it ain't. That's logic."
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • Your theory about manually releasing and autoreleased object is almost
    certainly correct. Since it is very difficult to find out what the
    intended target of the objc_msgSend was intended to be, you may have to
    resort to a more painstaking approach.

    One thing I have done, and if there are better methods I would love to
    hear about them, is to log each release and retain. You could also have
    the log entry include the retainCount of each object in your data model.

    Or here is something I just thought of that you could try - implement
    release in your subclass, and if the retain count of the object is one,
    look in the autorelease pool before calling [super release]. If it is
    in the autorelease pool, raise an exception. You would probably want to
    turn this off in a shipping project for performance reasons.

    That technique will only work with your classes, not the cocoa library
    classes - unless you want to start using categories, which may be
    risky. Theoretically, you could put a category on NSObject that would
    do the check for every object in your app, but you would have to
    reproduce the default release code. I suppose you could look in GNUStep
    for a start. Probably not worth it, but it would be educational.

    Maybe it would be easier to subclass or add a category to the
    autorelease pool.

    The other thing to do is sift through your code, checking every place
    where the word "release" occurs, and making sure it is balanced by an
    alloc or a retain. Chances are you are using a constructor for an
    object and assuming that it is retained when in fact it has been put in
    the autorelease pool. Methods like NSString's stringWithFormat: put the
    result in the autorelease pool.

    The autorelease pool is emptied at the end of each event loop, so
    chances are that whatever you are releasing incorrectly was created as
    a result of the most recent event.

    Sorry if this is rambling; AFIK there is no silver bullet to solve this
    type of problem. I look forward to hearing if others have tried any of
    above techniques or have come up with better ones.

    Jerry

    On Saturday, September 13, 2003, at 08:07  AM, Dave Riggle wrote:

    > I got all my code wired together yesterday, which means my problem
    > could be anywhere.  Is there an easy way to track down the culprit?  I
    > crash with a signal 11 here:
    >
    > 0    objc_msgSend
    > 1    NSPopAutoreleasePool
    > 2    -[NSApplication run]
    > 3    NSApplicationMain
    > 4    main
    >
    > What exactly causes this crash anyway?  Have I manually released an
    > autoreleased object?  It seems like it would be more useful to get a
    > signal raised at the time of the error rather than at the end of the
    > run loop.
    >
    > Dave
    > _______________________________________________
    > cocoa-dev mailing list | <cocoa-dev...>
    > Help/Unsubscribe/Archives:
    > http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    > Do not post admin requests to the list. They will be ignored.
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
  • On Saturday, September 13, 2003, at 11:07 AM, Dave Riggle wrote:

    > I got all my code wired together yesterday, which means my problem
    > could be anywhere.  Is there an easy way to track down the culprit?  I
    > crash with a signal 11 here:
    >
    > 0    objc_msgSend
    > 1    NSPopAutoreleasePool
    > 2    -[NSApplication run]
    > 3    NSApplicationMain
    > 4    main
    >
    > What exactly causes this crash anyway?  Have I manually released an
    > autoreleased object?  It seems like it would be more useful to get a
    > signal raised at the time of the error rather than at the end of the
    > run loop.
    >
    >

    Your theory is most probably correct.  The first way to avoid these
    problems is to follow Cocoa's very simple conventions for memory
    management.  See copious threads on the subject in this forum by
    searching with mamasam: http://cocoa.mamasam.com/.  See
    http://www.stepwise.com/Articles/Technical/2001-03-11.01.html and many
    similar articles at stepwise
    http://www.stepwise.com/StartingPoint/Cocoa.html.

    After you have made a mistake, it is actually not that hard to figure
    out what went wrong.  See NSDebug.h

    Here are some handy environment variables that you can set or specify
    on the command line:

    NAME OF ENV. VARIABLE              DEFAULT    SET TO...
    NSDebugEnabled                  NO            "YES"

    NSZombieEnabled                  NO            "YES"
          // Enable object zombies. When an object is deallocated, its isa
    // pointer is modified to be that of a "zombie" class (whether or
    // not the storage is then freed can be controlled by the
    // NSDeallocateZombies variable). Messages sent to the zombie
    // object cause logged messages and can be broken on in a debugger.
    // The default is NO.

    NSDeallocateZombies              NO           "YES"
    // Determines whether the storage of objects that have been
    // "zombified" is then freed or not. The default value (NO)
    // is most suitable for debugging messages sent to zombie
    // objects. And since the memory is never freed, storage
    // allocated to an object will never be reused, either (which
    // is sometimes useful otherwise).

    NSHangOnUncaughtException          NO              "YES"

    NSEnableAutoreleasePool             YES                "NO"
    NSAutoreleaseFreedObjectCheckEnabled      NO          "YES"

    As you might guess from these variables, running your application with
    the following arguments will enable you to zero in on the problem:

    -NSDebugEnabled YES -NSZombieEnabled YES
    -NSAutoreleaseFreedObjectCheckEnabled YES

    Then, when an attempt is made to release and already deallocated object
    (such as when you leave pointers to deallocated objects in an
    autorelease pool), an exception is raised.  The exception identifies
    the object that was over released.  That object will be an NSZombie
    that you can interrogate to determine what class the original object
    was.  You can also configure the autorelease pools to never release the
    objects they contain and you can configure objects so that they are
    never deallocated.  When a release message is sent that would have been
    sent to a deallocated object if the object had been permitted to be
    deallocated, an exception is raised and you can see in the debugger
    exactly which -release was one -release too many.

    Some handy methods and functions of the NSAutoreleasePool class:

    // Functions used as interesting breakpoints in a debugger
    FOUNDATION_EXPORT void _NSAutoreleaseNoPool(void *object);
    // Called to log the "Object X of class Y autoreleased with no
    // pool in place - just leaking" message.

    FOUNDATION_EXPORT void _NSAutoreleaseFreedObject(void *freedObject);
    // Called when a previously freed object would be released
    // by an autorelease pool. See +enableFreedObjectCheck: below.

    FOUNDATION_EXPORT void _NSAutoreleaseHighWaterLog(unsigned int count);
    // Called whenever a high water mark is reached by a pool.
    // See +setPoolCountHighWaterMark: below.

    @interface NSAutoreleasePool (NSAutoreleasePoolDebugging)

    + (void)enableRelease:(BOOL)enable;
    // Enables or disables autorelease pools; that is, whether or
    // not the autorelease pools send the -release message to their
    // objects when each pool is released. This message affects only
    // the pools of the autorelease pool stack of the current thread
    // (and any future pools in that thread). The "default default"
    // value can be set in the initial environment when a program
    // is launched with the NSEnableAutoreleasePool environment
    // variable (see notes at the top of this file) -- as thread
    // pool-stacks are created, they take their initial enabled
    // state from that environment variable.

    + (void)showPools;
    // Displays to stderr the state of the current thread's
    // autorelease pool stack.

    + (void)resetTotalAutoreleasedObjects;
    + (unsigned)totalAutoreleasedObjects;
    // Returns the number of objects autoreleased (in ALL threads,
    // currently) since the counter was last reset to zero with
    // +resetTotalAutoreleasedObjects.

    + (void)enableFreedObjectCheck:(BOOL)enable;
    // Enables or disables freed-object checking for the pool stack
    // of the current thread (and any future pools in that thread).
    // When enabled, an autorelease pool will call the function
    // _NSAutoreleaseFreedObject() when it is about to attempt to
    // release an object that the runtime has marked as freed (and
    // then it doesn't attempt to send -release to the freed storage).
    // The pointer to the freed storage is passed to that function.
    // The "default default" value can be set in the initial
    // environment when a program is launched with the
    // NSAutoreleaseFreedObjectCheckEnabled environment variable
    // (see notes at the top of this file) -- as thread pool-stacks
    // are created, they take their initial freed-object-check state
    // from that environment variable.

    Some more interesting stuff in NSDebug.h:

    FOUNDATION_EXPORT BOOL NSHangOnMallocError;
    // MACH only: Cause the process to hang after printing out the
    // "Malloc-related error detected with code N" message to stderr.
    // A backtrace can be gotten from the process with the 'sample'
    // utility, or the process can be attached to with a debugger.
    // The default is NO. Has no effect on non-MACH platforms.

    FOUNDATION_EXPORT BOOL NSHangOnUncaughtException;
    // If set to YES, causes the process to hang after logging the
    // "*** Uncaught exception:" message. A backtrace can be gotten
    // from the process with the 'sample' utility, or the process can
    // be attached to with a debugger. The default is NO.

    FOUNDATION_EXPORT BOOL NSIsFreedObject(id anObject);
    // Returns YES if the value passed as the parameter is a pointer
    // to a freed object. Note that memory allocation packages will
    // eventually reuse freed memory blocks to satisfy a request.
    // NSZombieEnabled and NSDeallocateZombies can be used to prevent
    // reuse of allocated objects.

    /****************    Stack processing    ****************/

    FOUNDATION_EXPORT void *NSFrameAddress(unsigned frame);
    FOUNDATION_EXPORT void *NSReturnAddress(unsigned frame);
    // Returns the value of the frame pointer or return address,
    // respectively, of the specified frame. Frames are numbered
    // sequentially, with the "current" frame being zero, the
    // previous frame being 1, etc. The current frame is the
    // frame in which either of these functions is called. For
    // example, NSReturnAddress(0) returns an address near where
    // this function was called, NSReturnAddress(1) returns the
    // address to which control will return when current frame
    // exits, etc. If the requested frame does not exist, then
    // NULL is returned. The behavior of these functions is
    // undefined in the presence of code which has been compiled
    // without frame pointers.

    FOUNDATION_EXPORT unsigned NSCountFrames(void);
    // Returns the number of call frames on the stack. The behavior
    // of this functions is undefined in the presence of code which
    // has been compiled without frame pointers.
    _______________________________________________
    cocoa-dev mailing list | <cocoa-dev...>
    Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/cocoa-dev
    Do not post admin requests to the list. They will be ignored.
previous month september 2003 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          
Go to today