How can NSLogs cause code to not crash? - ios

I had a rather interesting exc_bad_access crash today. After a lot of digging, I came up with the following information (running in simulator):
If I just ran the code, the app would randomly crash at a random point while loading data into my managed object. From what I could tell, it was always crashing when I loaded data into the managed object -- not on the sections that converted from my JSON dict to data to the object actually used (from strings and NSNulls to ints/floats and nils)
Random crashes are, of course, evil, so I tried to step through the process in the debugger, but that didn't prove practical -- I was processing a LOT of objects, so stepping through them one-by-one just didn't work. So I decided to add some NSLogs to track the process and try to spot a pattern that way.
Instantly solved the crash.
Just one NSLog, anywhere in the process, prevented the crash.
I eventually tracked my way up the stack trace and found the actual issue: I was accessing the managed object in a threaded environment, but NOT from within the associated MOC's performBlockAndWait: method. At that point, the crash was incredibly obvious to me -- I'm shocked I didn't have more issues earlier. I'm willing to bet that between having a 'small' test data set of 2-3 objects and having debug code in there with NSLogs, the error was pretty effectively masked earlier... but the question remains:
Why does an NSLog prevent the app from crashing? How on earth could a piece of code without side effects change the execution of the rest of the app? This makes no sense!

Amazingly enough, this is a fairly common situation: I have seen it more than once when enabling logging in a seemingly unrelated place would instantly solve a timing issue somewhere else.
The reason for this is that NSLog, just like many other output functions, has internal synchronization. There is a mutex somewhere that protects access to the internal buffers of NSLog, either in the NSLog itself or in one of the I/O libraries that it uses. This synchronization enables callers to use NSLog from multiple threads. It is this synchronization that changes the timing of your program, affecting a race condition and ultimately solving a crash.

Why does an NSLog prevent the app from crashing? How on earth could a
piece of code without side effects change the execution of the rest of
the app? This makes no sense!
Indeed this makes a sense. Really.
A single NSLog forces to print something to your console, it takes some fraction of seconds and in between your processing on somethread gets finished and the crash (might be due to un-availability of input is) no-more.
Your error may be due to async call. Your next process starts before finishing previous one. And your next process need data from previos process. NSLog consumes some time.

Related

Is there a way to take action, thus execute code, when a iOS application crashes ? Is this possible?

Is there a way to take action, thus execute code, when an iOS application crashes? Specifically, I would like to save the core data storage. Is this possible? I would say that this is possible since, for example, Firebase has to send information online for making crashlytics work. How can this be achieved? Thanks
Yes, but it is very difficult, and "save core data storage" would be far too much (and very dangerous, to boot).
Most crashes result from a signal (often SIGSEGV, but also SIGABRT, SIGILL or others), and you can install a signal handler to run code in that case. However, that code must be very, very carefully written because you will be in a special execution state. There are a small number of C functions you are permitted to use (see the man page for sigaction for the list). Most notably, you can't allocate memory. Allocating memory in a signal catching function can deadlock the program in a tight spinlock (done that myself when I tried to write my own crash handler in my more naive days; it's really bad).
The way that crash handlers like Crashlytics do it is that they do as little as possible during the signal handler, mostly just writing the stack trace to storage (using pre-allocated buffers). When you restart, they see that there's an unhandled stack trace from a previous run, and then they do all the complicated stuff like uploading it to a server, or displaying UI, or whatever.
But even if you could write to Core Data in the middle of a signal handler, you would never want to do that. During a signal handler, the system is in an undefined state. Various invariants may not currently hold (such as whether the object graph is consistent). The fact that you're crashing this way indicates that something illegal has happened. The last thing you should do in that state is take data that is highly untrustworthy and overwrite the good data on disk.

Avoiding fatal crashes

I have been debugging an app for a while and am ready to upload it to app store. However, I still get occasional crashes that are hard to duplicate or debug--for example when pushing buttons in weird sequences. If I write these sequences down, I can debug but inevitably it happens when I haven't written it down and these can be difficult to replicate.
I know I need to just call it a day and leave future bug fixes for next release. I have removed all the abort() statements I had in testing. However, occasionally rather than getting a crash that lets you just close the app and reopen, I get one that makes it impossible to open the app without reinstalling. For example, this just happened, with a
'NSGenericException', reason: '*** Collection <__NSCFSet: 0x174a41b90> was mutated while being enumerated.'
This resulted from switching VCs during a background sync to the cloud.
I can live with crashes where you just need to reopen but not with ones that render the app unusable. Are there any guidelines for types of crashes to help you focus on the really fatal ones?
Or is there anything you can do to keep crashes from bricking app?
You should just fix this problem. Since you have this crashlog with that error message, you know which method can raise the problem, so you've got a good head start on fixing it, even if the problem manifests itself inconsistently and/or in a variety of ways.
But the occasional crash may not seem like too much of an inconvenience to you, but it is the quickest way to 1-star reviews and unhappy customers. I suspect you will quickly regret distributing an app with known, easily reproduced crashes.
But, a few observations:
It sounds like your background update process is mutating your model objects used by the main thread.
If possible, I'd suggest being careful to simply do not change any of your model objects in the background thread, but rather populate a local variable and when you're ready to update the UI accordingly, dispatch both the model update and UI refresh to the main thread.
If you cannot do this for some reason, you have to synchronize all interaction of model updates with some mechanism such as locks, GCD serial queue, reader-writer model, etc. This is slightly more complicated approach, but can be done.
I would advise temporarily editing your target's "scheme" and turn on the thread sanitizer:
It may possibly help you identify and more easily reproduce these sorts of problems. And the more easily you can reproduce the problem, the more easily you will be able to fix the issue.
You say:
Or is there anything you can do to keep crashes from bricking app?
It sounds like the "save" operation is somehow leaving the results in persistent storage in an internally inconsistent manner. Any one of the following would help, though I'd suggest you do all three if possible):
At the risk of repeating myself, fix the crash (it's always better to eliminate the source of the problem than try to program around manifestations of the problem);
Depending upon how you're saving your results, you may be able to employ an atomic save operation, so that if it fails half way, it won't leave it in an inconsistent state; we can't advise how you should do that without seeing code snippet illustrating how you're saving the results, but it's often an option;
Make sure that, if the "load" process that reads the persistent storage can fail, that it does so gracefully, rather than crashing; see if you can get it in this state where the app is failing during start-up, and then carefully debug what's going on in the start-up process that is causing the app to fail with this particular set of data in persistent storage. In the "devices" section, there is an option to download the data associated with an app, so you can carefully diagnose what's going on.

2 different NSManagedObjectContexts save causes infinite memory allocation

I'm getting a weird issue. I have the following set-up:
Model.xcdatamodeld
ModelBackup.xcdatamodeld
Each of these have their own NSManagedObjectContext NSPersistantStoreCoordinator and NSMananagedObjectModel.
I can read and write to each of these successfully separately. However, if I try to read/write to Model at the same time I'm read/writing to ModelBackup, I get infinite memory allocation. In the simulator, the CPU will spike to 200% and the memory will increase around 80-100 MB/second. This will eventually crash when memory hits 2.0+GB. This happens when I do a context save on both these NSManagedObjectContexts. I can read/access them fine.
Anyone know why I'm unable to write to both of these?
I have Model with ConcurrencyType:NSMainQueueConcurrencyType and ModelBackup with ConcurrencyType:NSPrivateQueueConcurrencyType.
The idea behind having two different xcdatamodeld is a workaround/alternative the parent-child core-data pattern. Our app is doing massive updates to the datamodel so I want to have a background datamodel perform those updates then when the app launches the next time, switch to the new updated sqlite.
First, you can do massive updates with parent/child as long as you pay attention to how much you are writing to disk at any one save.
Second, you can have two NSPersistentStoreCoordinator instances pointed at the same sqlite file and avoid using two files.
Third, What does your code look like for the creation of these contexts? Are you using the second context only within -performBlock: calls?
Fourth, what happens when you stop in the middle of that memory allocation? What does your stack look like?
For your immediate problem, you have an infinite loop. If I had to guess I would guess that you are using NSManagedObjectContextDidSaveNotification, probably with a nil object and each save is kicking off a save to the other context causing a loop.
Seeing the code would help solve your immediate issue. However I suspect there is an easier solution to your problem.

Core Data crash within a try catch block

I have the following crash log and code to go along with it:
https://gist.github.com/emilevictor/7422ac293eb27b415fb8
I'm a bit confused, as I have wrapped this Core Data code (which creates a new instance in the database) with a try catch block but it is still crashing out occasionally in on release compiled code.
This is on a device which has had its local data wiped and installed from scratch, by the way.
I'm not sure what else to do, I assume that this code might have an issue.
First the try/catch problem. #try/#catch only trap NSExceptions which have been #thrown or -raised. You don't have this kind of exception, you have a segfault. These happen at a much lower level and cannot be trapped in a #try/#catch.
The real problem here is what's going wrong to cause the segfault. Usually this is caused be objects which have been prematurely -dealloced or by notifications being sent to -dealloced observers. I can see from your call stack that the process is in the middle of sending a notification, so my guess it the second type.
Somewhere, you have registered an observer and that observer has gone out of scope (-dealloced) without unregistering itself. I would start by profiling the app for zombies.

Can invalid thread handles in C# be a sign of unmanaged memory corruption?

We have a C# service that has started failing in very odd ways around thread handles. Specifically calls to EventWaitHandle.Reset, ReaderWriterLock calls, and other similar threading calls are randomly blowing up with Invalid Handle errors deep in the stack. These are calls that are definately supposed to work. Could this be a sign of memory corruption? If so we're a bit confused how this is the only symptom and not crashing. We have COM objects with C++ code where we do HeapFree's, and also have some managed code calling into FreeHGlobal so are suspicious. Has anyone experienced anything like this with problems manifesting themselves seemingly localized to thread primitives?
I've not seen this particular symptom. But I think it's conceivable that the cause is as you suspect - there's no rule that says that memory corruption must cause crashes.
Imagine that you had a data structure like this
buffer[100];
threadHandle;
And you had a simple out by one error in the code filling the buffer. Now your thread handle is defective and presumably you'd get the symptom you see.
I don't know your environment well (I'm Linux/Java these days) but could another cause be using a threadHandle after the thread has terminated? So the handle is indeed no longer valid? Some sort of race condition in your termination code?
Turns out this was a PInvoke issue. We were incorrectly PInvoking a Thread Handle to the C# SafeFileHandle wrapper, which under the write conditions would invalidate the the thread handle which was later re-allocated by other threads and a giant mess ensued. THus the moral of the story, be very very careful using .net Handle wrapper classes in P-Invoke.

Resources