How to reproduce a rare "_CFAutoReleasePoolPop" crash?

196 views Asked by At

I am trying to reproduce such crash(es): enter image description here

There's Manual Reference Counting in my project. Also, there's a lot of multi-threading. Some of the properties are not thread-safe. :(
I have just one assumption about the cause of this crash: some object is overreleased (?).

I've added automated UI tests (Appium), but they haven't helped yet.
Also, I've profiled for Zombies - everything seems ok.
Also, I've tried Xcode's Static Analyzer ( Product -> Analyze ), there're a lot of warnings, but none of them seems to be a cause of such crash (I've looked at the warning Incorrect decrement of reference count not owned at this point).

I created a test project with MRC, and added such code:

- (void)testAssumptions {
    //@autoreleasepool
    {
        [self overReleaseNilValue];
        [self overReleaseNotNilValue];
    }
}

- (void)overReleaseNilValue {
    NSIndexPath* path = [[NSIndexPath alloc] initWithIndex:42];
    [path release];
    [path release];
}

- (void)overReleaseNotNilValue {
    NSIndexPath* path = nil;
    [path release];
    [path release];
}

Releasing an object twice doesn't crash neither with autorelease pool enabled nor without a pool.

So my questions are:
1. What can be another reason of such crash except of releasing already released object?
2. Are there ways to increase a probability of reproducing such crash? E.g. some env. variable which reduces some autorelease pool tolerance to unsafe code? Or some additional autorelease pool?
3. Why doesn't my test project code crash?

Any comments are highly appreciated.

1

There are 1 answers

2
Rob Napier On BEST ANSWER

Incorrect decrement of reference count not owned at this point

This would definitely the be warning to explore. It's almost certainly pointing you to at least one error.

Your test project is not actually testing anything (they're also named backwards I believe). There is no promise that over-releasing a value will cause a crash. overReleaseNotNilValue is well-defined behavior, and is absolutely will not crash (sending a message to nil does nothing). overReleaseNilValue is undefined behavior. I haven't dug into it, but I would expect NSIndexPath to be implemented with tagged pointers, which will not crash if you over-release them.

Undefined is undefined. It doesn't mean crash. Over-releasing a value can do anything. If you're lucky, it'll crash....

Also, there's a lot of multi-threading. Some of the properties are not thread-safe.

I would expect this to be at the heart of your problem if it's intermittent. I've worked on such projects. The solution is to fix the problems. You will not know which specific problem is the cause of your crashes. You may never know. You still must fix them all. It will take some time, but you have to make the code thread-safe, or its behavior is undefined.

As to debugging, you will want to do the following, in order:

  • Turn on the Runtime Sanitization options (under the Scheme editor, Run, Diagnostics). You especially will want the Thread Sanitizer for this.

  • Clear all Static Analyzer warnings. If any of them say your memory management is wrong, you have to clear those. The system is literally telling you where the problems are. Don't ignore it.

  • Clear all warnings. There should be zero warnings in your project. If there are lots of "false" warnings then you will never see the real warnings telling you exactly where your problems are. Eliminate all warnings.

I spent 8 months eliminating rare over-release crashes in one a well-written project written by expert developers and almost no threading. It can take a lot of time. You have to clear every problem. Just one incorrect release is enough to crash the program randomly.