Crash reporting in iOS

Introduction

We all work hard to prevent any crashes in our programs, but there’s always some condition where the unexpected happens. Crashes in production might be a nightmare but they do happen. So for any application out there, whose developers are committed to resolving all imperfections in their product, crash reporting is critical. Crash reports can be seen and managed from Xcode’s Organizer or in iTunes-Connect, but what if you wanted to receive then from anybody using the application? What if you wanted the application to automatically gather stack traces? We are going to do just that in this article.

What is a crash log?

A crash log is nothing more than a piece of text that indicates the contents of the program’s stack (all functions being called by the application at a particular time) when the execution had to be stopped by the system. Many iOS developers think that these log can only be accessed by the system and they can only view them in Xcode’s Organizer. However, the application has access to the data written in the program stack, provided that you know where to look at.

Why does an application crash

Many people don’t exactly know what happens when an application crashes, so I decided to explain briefly. Most often, an application crashes because it violates a restriction imposed by the operating system. Let’s say you try to access a memory that has already been released/deleted. The system realizes that you are trying to read or write data at an address that is no longer at the program’s disposal (The OS knows which part of the memory it reserved for every running application so it knows when a program is trying to access parts of it that are outside it’s authority). Since that operation is either a mistake in the application’s logic, or malicious behavior, the operating system has no choice but to terminate the execution of the program.

Exit codes

When an application terminates, it returns a number that indicates how the process ended. As a convention, if the program exited successfully, it returns code zero. Every non-zero code means that the program failed at some point of it’s execution. iOS is no exception – every time your application crashes, Xcode indicates the code it exited with. You have, no doubt seen them, though you might not have known they were exit codes. I’m talking about SIG_ABRT, BAD_ACCESS, SIG_KILL and maybe SIG_PIPE. These are all values that are trying to tell you what happened with your program. SIG_ABRT is most commonly associated with exceptions being thrown and not caught. SIG_KILL you can try by killing your application from the multitasking screen while attached to the debugger – it mean that your application was forcefully quit by another proces – normally it is not something you should be concerned with. BAD_ACCESS happens when you access memory that’s already been deallocated. SIG_PIPE is kind of rare, but it means that you were trying to write into a pipe (a connection to a file or a server for instance) that has been closed by the remote party.

Signals

Signals are a method of inter process communication (IPC) where different programs running on the same system exchange information by sending each other number codes. Nowadays, it is almost exclusively used by the operating system in order to manage application programs (here, “application” refers to a program that is not part of the operating system – the kernel itself). The standard values are specified in the signal.h file in the iOS framework (of any UNIX framework). Signals can be intercepted by the program and handled (more on that below).

Accessing crash logs from an iOS application

How can an application access it’s crash logs when the system terminates it? Well, this has something to do with those signals I was writing about above. Thing is…I lied a little. The operating system does not just terminate your application – it send a signal to it. Termination is just a side effect – the default action when receiving most signals, is for an application to crash (exit with non-zero code). You might already see where I’m going with this – we are going to replace the signal’s default action with a custom one that extracts the crash log…

Let me step back for a little bit. As I mentioned, signals can be handled by programs. The way that works is, every signal is associated with a piece of code (a function) that is invoked to handle it. When the application starts, the default handlers are set in place. Most of them just make the application crash. However, at runtime, programs have the ability to replace these handler with a new one. So let’s go ahead and do just that.

Setting signal handlers

Finally, let’s write some code. Since signals are deep in the C libraries, fiddling around with them requires some C/C++ magic.

To use the following examples, you will need to import some headers:

#import <signal.h>
#import <execinfo.h>

First of all, let’s create our signal handling function:

void crashSignalHandler(int signal)
{
    // do magic here...
    exit(signal);
}

As you can clearly see, it is nothing special – it’s just a standard void C function with one parameter of type int where it receives the signal code it has to handle. Any function that fulfills these requirements can be a signal handler. I should emphasis that it cannot be a method of a class. What’s interesting here is the last line in the function – exit(signal);. It is actually kind of important – it is going to terminate our application once we handle the signal and before we exit the function. WHAT? Why would I want to crash my own application since I have a chance to save it. Well, I thought the same thing, but once you receive a signal like BAD_ACCESS, even if you don’t call the exit function, the application will freeze. It is better to just let go sometimes…and let your creation crash (and burn). Please note that because we replace the default handler (that makes the application crash) with our own, it no longer exits automatically and that’s why it will not crash unless we call the exit function.

Replace the default signal handler:

We wrote a function to handle our signal, but it doesn’t get called. We have to instruct the system to remove the default handler and start using ours. Here’s how we do that:

signal(SIGABRT, crashSignalHandler);

This will set a new handler for the SIGABRT signal. However, not all crashes are produced by SIGABRT. In fact, these are relatively easy to fix. What we need is to intercept BAD_ACCESS.

signal(SIGABRT, crashSignalHandler);
signal(SIGSEGV, crashSignalHandler);
signal(SIGBUS, crashSignalHandler);

I found that most crashes are caught with these three signals.

Revert to the default signal handler:

You might want to disable your custom crash reporting in some occasions. To achieve this, you have to remove your handler and put the default one in place.

signal(SIGABRT, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);

This one was easy. SIG_DFL is just a #define for the default function.

Retrieving the stack trace

So now, the only thing left to do is to save the stack trace in our handler. Since the application is going to terminate right after the handler returns, we are going to save the log in the file system so we can access it next time the application is run. To read the stack trace we will use another C magic:

void* callstack[128];
int frames = backtrace(callstack, 128);

This will copy the first 128 entries in the program’s stack into the “callback” array. To desymbolicate it and save it into a file, we can use the following:

backtrace_symbols_fd(callstack, frames, crashLogFileDescriptor);

The “backtrace_symbols_fd” function will take the array we received above and write it to a file. It takes the file descriptor of the target file, so you will have to open it yourself and get the file descriptor. If you don’r already know, a file descriptor is an integer that identifies an open file within the file system. You can acquire it by opening the file in the following way:

const char* fileNameCString = [crashLogFilePath cStringUsingEncoding:NSUTF8StringEncoding];
FILE* crashFile = fopen(fileNameCString, "w");
crashLogFileDescriptor = crashFile->_file;

The resulting file will contain something like this:

0   AppName                             0x00206077 crashSignalHandler + 306
1   libsystem_platform.dylib            0x3aa7005b _sigtramp + 34
2   libsystem_pthread.dylib             0x3aa75a33 pthread_kill + 58
3   libsystem_c.dylib                   0x3a9bcffd abort + 76
4   libc++abi.dylib                     0x39cebcd7 <redacted> + 74
5   libc++abi.dylib                     0x39d046e5 <redacted> + 252
6   libobjc.A.dylib                     0x3a44d921 <redacted> + 192
7   libc++abi.dylib                     0x39d021c7 <redacted> + 78
8   libc++abi.dylib                     0x39d01d2d __cxa_increment_exception_refcount + 0
9   libobjc.A.dylib                     0x3a44d7f7 objc_exception_rethrow + 42
10  CoreFoundation                      0x2ff40c9d CFRunLoopRunSpecific + 640
11  CoreFoundation                      0x2ff40a0b CFRunLoopRunInMode + 106
12  GraphicsServices                    0x34c67283 GSEventRunModal + 138
13  UIKit                               0x327e4049 UIApplicationMain + 1136
14  AppName                             0x000cd07f main + 154
15  AppName                             0x00008c28 start + 40

System method show as “redacted”, but you should be able to see functions that you wrote for your application.

Here’s the whole handler function:

void crashSignalHandler(int signal)
{
    NSlog(@"Application received signal: %d", signal);
    void* callstack[128];
    int frames = backtrace(callstack, 128);
    backtrace_symbols_fd(callstack, frames, crashLogFileDescriptor);
    exit(signal);
}

Note: You can read the manual page for all functions mentioned above type typing “man <functionName>” in the Terminal application:

man backtrace_symbols_fd

Conclusion

So there you go…crash reporting in iOS applications. Now you wont wonder how they did it next time you see and alert asking you if you wanted to send a crash log to the developers of an application you use. As you can see, it’s nothing hard, you just need to know where to look. Let me know in the comment section – Will you be implementing crash reporting for your app, and if you have, was it similar to the way I showed you?

Once again, thanks for reading!

Using Key Value Coding validation

Key value coding in a powerful tool in Objective-C’s Cocoa framework that allows you to change an NSObject subclasses’s data without using properties and setters. It uses a mechanism, similar to the one used for adding data to an NSMutableDictionary. For instance, if I wanted to change a property named “brand” in a “Car” class, using properties, I would write something like:

car.brand = @"Honda";

Using Key Value Coding, or KVC,  I can achieve the same thing typing:

[car setValue:@"Honda" forKey:@"brand"];

At first glance, the latter might seam like it doesn’t make much sense. First of all, it introduces some hardcoded strings, which is never a good thing. Not only that, hardcoding a property’s name can be very dangerous if we later decide to change that property’s name, or remove it altogether. If this happens, the application will crash with an exception, saying the Car in not key value compliant for key “brand”.

Another disadvantage of using KVC is that it might seam like it decreases dependency in the code because you would only need to use NSObject (KVC is built into NSObject). In the example above, we don’t need to expose the Car class, we are only setting a property in an object of a class, we don’t need to know about. However, the fact that the header file for Car is not imported, doesn’t mean that there is no dependency. We still have to be sure that the object is an instance of the Car class, or at the very least, make sure it has a property named “brand”.

Despite this, Key value coding can be a powerful weapon if used appropriately.  It adds a new level of abstraction when dealing with different types of data classes. Normally, it would be hard to generalize some code to work for a wide range of data classes. Let’s say you want to be able to dynamically map values to a data model by using plists. It would be inconvenient to put method names in it (it’s possible, but it doesn’t seam right – to me anyway). Using KVC, you will be able to put property names in that plist in order to do the mapping. And since you made the effort to implement reading those plists, you will most likely want to be able to use not only one type of data class, but a wide range of them. And since KVC is built into NSObject, you will have no problem doing so – all you need to know is the name of the property you need to change.

Another major advantage in using KVC is the ability to integrate validation and error handling right into your data model. The problem with error handing is that it’s too boring and tedious and programmers tend to avoid it like the plague until the last stage of development. This means that often it is not considered during the design phase and often it becomes too hard to implement properly. The harsh reality is that software has to be designed with error handing in mind. You have to carefully think about, how errors are generated and passed along from the business logic to the UI. This is not as easy as it might seam sometimes. Key value coding validation allows you to integrate error checking right into your data model using its validate methods. The idea behind validation methods is pretty straight-forward. For every property, a method called validate<PropertyName>:forKey:error: contains code that checks if the new value supplied is valid for that property. It returns YES or NO depending on whether or not the value is acceptable and if not, an error can be returned in the last parameter.  Additionally, the value, provided to the method via the first parameter can be modified during the function’s executing. The idea is that, if validation fails, a different value might be supplied that will not fail the checks but is semantically identical to the one provided. For example – let’s say a numeric field is being validated, but the user has specified a string (@”15 km”) instead of a number. The validation method realizes that it received an object of the wrong type, but instead of returning NO and passing an NSError object, it first tries to convert the string to a number, just in case it contains same valid information. In this case, it would find the number 15 in the string and decide to use that. It might even realize that the user has specified kilometers and it needs a distance in meters and multiply 15 by 1000 to get 15000 as a final result. This can, no doubt, be useful in many applications. Unfortunately, Key value coding validation will not automatically invoke the validate method every time a property setter is called. You have to explicitly validate the value before calling the setter.

NSError* validationError = nil;

[accident validateValue:&valueToValidate forKey:propertyKey error:&validationError];

if (validationError == nil)

{

[accident setValue:valueToValidate forKey:propertyKey];

}

The down side of using validation methods is that you have to write one for every property of every data classes you wish to use. That can be extremely tedious. So tedious that I wrote a bash script that generates them for me. It recognizes common field types such as NSString*, NSDate and NSNumber and creates a template suitable for that type. For all other data types, it just creates the method definition returning YES by default. In order to use it, just supply your .h file (containing the @property definitions) and the script will dump all validate method implementations in the console. If you are interested, here’s the code:

#!/bin/bash

function capitalizeFirstLetter

{

word=$1

word=”$(tr ‘[:lower:]’ ‘[:upper:]’ <<< ${word:0:1})${word:1}”

}

fileName=$1

allProperties=$(grep @property $fileName)

grep @property $fileName |  while read -r property

do

# handle string properties

isString=$(echo $property | grep NSString)

propertyName=$(echo $property | rev | cut -d’ ‘ -f 1 | rev | cut -d’;’ -f 1)

propertyName=”$(tr ‘[:lower:]’ ‘[:upper:]’ <<< ${propertyName:0:1})${propertyName:1}”

if [[ ! -z $isString ]]

then

echo -e “- (BOOL)validate$propertyName:(NSString**)value\n error:(NSError**)validationError\n{\n\tif ((*value).length == 0)\n\t{\n\t\tNSError* error = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t*validationError = error;\n\t\treturn NO;\n\t}\n\treturn YES;\n}”

fi

# handle date properties

isDate=$(echo $property | grep NSDate)

if [[ ! -z $isDate ]]

then

echo -e “- (BOOL)validate$propertyName:(NSDate**)date\n error:(NSError**)validationError\n{\n\tif (*date == nil)\n\t{\n\t\tNSError* noDateError = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t\t*validationError = noDateError;\n\t\t\treturn NO;\n\t\t}\n\t\telse if( [*date compare:[NSDate date]] == NSOrderedDescending )\n\t\t{\n\t\t\tNSError *futureDateError = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t\t*validationError = futureDateError;\n\t\t\treturn NO;\n\t\t}\n\t\treturn YES;\n}\n”

fi

isArray=$(echo $property | grep “Array”)

if [[ ! -z $isArray ]]

then

echo -e “- (BOOL)validate$propertyName:(NSArray**)array\n\t\terror:(NSError**)validationError\n{\n\tif ((*array).count == 0)\n\t{\n\t\t// Must have at least one object\n\t\tNSError* noDateError = [NSError errorWithCode:1\n\t\t domain:(NSString*)??? \n\t\tdescription:NSLocalizedString(@\”???\”, @\”\”)];\n\t\t*validationError = noDateError;\n\t\treturn NO;\n\t}\n\treturn YES;\n}\n”

fi

if [[ -z $isString && -z $isArray && -z $isDate ]]

then

echo -e “- (BOOL)validate$propertyName:(NSObject**)value\n\t\terror:(NSError**)validationError\n{\n\t// TODO: implement validation for $propertyName \n\treturn YES;\n}\n”

fi

echo -e ‘\n’

done

It’s not going to win any prizes, but it works for me.

The good news is that validation methods are the only ones that you have to explicitly define. Setters and getters are handled by the methods generated by @property and @synthesize.

I guess that’s it. That’s all you need to do to use Key value coding validation. I hope you found this tutorial useful and think about times when you could have used key value observing for your applications. In my next post, I’ll be covering Key-Value Observing – another great, more advanced feature of the Cocoa framework. Thanks for reading.