With the November 2016 rewrite of the Uber rider app, we made significant inroads towards reducing our measured crash rate and improving the uptime of our core features. However, one nagging problem was app terminations caused by out of memory (OOM) exceptions or other reasons that are hard to detect. Anecdotally, we received reports of the app failing to launch in low-memory situations, particularly on older devices, but had little in the way of tooling to track down these issues.
With this in mind, Uber Engineering developed and open sourced our own Startup Reason Reporter. In this article, we discuss how we designed this powerful tool to help us—and others—detect startup reason on iOS and in the process build more reliable apps.
So…why did my app crash?
From a developer point of view, detecting a typical crash on iOS is fairly straightforward. The foundation framework provides an NSSetUncaughtExceptionHandler callback, which gives the developer an opportunity to log information about uncaught exceptions. When an an application terminates for other reasons, however, it can potentially be very difficult to determine what went wrong. In both cases, the experience is the same: the app stops working and crashes. We sought to gain more insight into the frequency of these sorts of crashes to determine how we could track, and in turn, prevent them.
Our implementation of the Startup Reason Reporter is based on Facebook’s process of elimination for reducing OOM events outlined on the Facebook Engineering Blog. According to this framework, a fresh app launch may occur for a finite number of reasons, including:
- The app was launched for the first time
- The app is under development and the app crashed as part of debugging
- The app crashed on a prior run
- The OS was upgraded
- The app was upgraded
- The app was force quit on a prior run
- The phone was restarted
- The app was terminated in the background
- The phone ran out of memory and terminated the application
To narrow down the list of possible reasons, the Startup Reason Reporter assumes that the application was launched for the first time. It then runs through each of these possibilities in sequence based on information saved from the prior launch. If we are unable to determine the reason the application launched from the stored data, then we assume the app was terminated because the phone ran OOM.
Persistence of prior run information
Critically, the Startup Reason Reporter requires that there be some sort of storage mechanism in order to persist the required information from the prior app run. Our implementation of the Startup Reason Reporter allows you to use whatever storage mechanism you prefer, so long as it implements UBApplicationStartupReasonReporterPriorRunInfoProtocol. This protocol abstracts all of the information required to determine the startup reason, such as the prior app version and whether the application was last in the background or foreground. We provide a concrete implementation, UBApplicationStartupReasonReporterPriorRunInfo, which is based on NSUserDefaults, but recognize that this may not be appropriate for all use cases.
The Startup Reason Reporter relies heavily on the data available. In particular, the previousRunDidCrash flag must be accurate to properly report OOM issues. In short, the default assumption is that all crashes that do not fall into known buckets are considered OOM events, so if the provided data is inaccurate, some reported by the Startup Reason Reporter may actually be other issues. Capturing all variants of crashes is beyond the scope of this discussion, but it is important to capture all sorts of crashes such as watchdog timeouts, segmentation faults, and exceptions.
The Startup Reason Reporter is written in Objective-C, but works when bridged to Swift as well. Usage is fairly straightforward: shepherd the required information to an instance of UBApplicationStartupReasonReporter. The UBApplicationStartupReasonReporter will then make the startup reason available via the startupReason property. The result can then be fed to your analytics pipeline of choice for further analysis.
See something that we missed or have an idea for a hack that might make this project better? Share your contributions to the Startup Reason Reporter on Github!
If tackling mobile engineering challenges at Uber-scale appeals to you, consider applying for a role on our iOS Platform team.
Tuomas Artman and Alex Medearis are software engineers on Uber’s iOS Platform team.