RSS

iOS RUNNINGBOARD 0xdead10cc crash troubleshooting

iOS RUNNINGBOARD 0xdead10cc crash troubleshooting

The termination reason 0xdead10cc indicates that an iOS app was terminated by the system because it held on to a system resource (like a file or network connection) while running in the background. This typically happens when an app does not release a resource after being told to suspend, leading the system to kill the app to maintain overall system health.

Sample crash from RUNNINGBOARD 0xdead10cc:

...

Exception Type:  EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: RUNNINGBOARD 0xdead10cc

Triggered by Thread:  0


Kernel Triage:
VM - Compressor failed a blocking pager_get
VM - Compressor failed a blocking pager_get
VM - Compressor failed a blocking pager_get
VM - Compressor failed a blocking pager_get
VM - Compressor failed a blocking pager_get

...

How to troubleshooting RUNNINGBOARD 0xdead10cc crash issue?

Review Background Tasks

Check your app’s background tasks. Ensure that all tasks are properly ended before the app transitions to the background. Use the applicationDidEnterBackground: and applicationWillTerminate: methods in your app delegate to manage tasks.

Audit Resource Usage

Audit your code for places where you might be holding onto system resources like file handles or network connections. Make sure to release or close these resources when the app is about to enter the background.

Background Task API

If you need to run background tasks, use the appropriate APIs like UIBackgroundTaskIdentifier for short tasks. For longer tasks, consider using background fetch or push notifications to trigger content updates.

To use the Background Task API in iOS for executing tasks in the background, you need to follow these steps:

  1. Register Background Tasks: In your app’s Info.plist, add the UIBackgroundModes key with the fetch and processing values to declare the use of background tasks.
  2. Request Permission: In iOS 13 and later, explicitly request permission to schedule background tasks.
  3. Define and Schedule Tasks: Use BGTaskScheduler to define and schedule your tasks.

Here’s a sample code snippet demonstrating these steps:

import UIKit
import BackgroundTasks

class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Register your background task
        BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.yourapp.refresh", using: nil) { task in
            // Downcast the parameter to an app refresh task as this identifier is used for a refresh request.
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }

        return true
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        scheduleAppRefresh()
    }

    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: "com.yourapp.refresh")
        request.earliestBeginDate = Date(timeIntervalSinceNow: 1 * 60) // Fetch no earlier than 1 minute from now
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Could not schedule app refresh: \(error)")
        }
    }

    func handleAppRefresh(task: BGAppRefreshTask) {
        // Schedule a new refresh task
        scheduleAppRefresh()

        // Provide an expiration handler for the task
        task.expirationHandler = {
            // This block is called if the task expired; cancel any external requests
        }

        // Perform the background task
        updateData()

        // Call the completion handler once the task is done
        task.setTaskCompleted(success: true)
    }

    func updateData() {
        // Perform your data fetching or processing here
    }
}

This example demonstrates how to register and schedule a background app refresh task. It includes registering the task in didFinishLaunchingWithOptions, scheduling the task in applicationDidEnterBackground, and handling the task execution in a specific method (handleAppRefresh). Remember to replace “com.yourapp.refresh” with an identifier specific to your app.

Important: Always test background tasks thoroughly, as iOS imposes strict limits on background execution time and frequency to preserve battery life and improve user experience.

Debugging and Logging

Add extensive logging around your background tasks and resource management. Look for logs that indicate tasks not completing or resources not being released. Use Xcode’s debug tools to simulate background mode and observe your app’s behavior.

Review App Lifecycle

Ensure your app correctly handles transitions between foreground, background, and suspended states. Implement the delegate methods in UIApplicationDelegate to manage these transitions.

Optimize for Quick Suspension

Optimize your app to be ready for suspension as quickly as possible. This means efficiently managing tasks and resources so that when applicationDidEnterBackground: is called, your app can quickly clean up.

iOS Background Execution Limits

There are iOS background execution limits, there is good summary at https://developer.apple.com/forums/thread/685525 . Excerpt here in case link broken.

iOS provides some general-purpose mechanisms for background execution:

  • To resume your app in the background in response to an event on your server, use a background notification (aka a ‘silent’ push). For more information, see Pushing background updates to your App.
  • To request a small amount of background execution time to refresh your UI, use BGAppRefreshTaskRequest.
  • To request extended background execution time, typically delivered overnight when the user is asleep, use BGProcessingTaskRequest.
  • To prevent your app from being suspended for a short period of time so that you can complete some user task, use a UIApplication background task. For more information on this, see UIApplication Background Task Notes.
  • To download or upload a large HTTP resource, use an NSURLSession background session.

All of these mechanisms prevent you from abusing them to run arbitrary code in the background. As an example, consider the NSURLSession resume rate limiter.

For more information about these limitations, and background execution in general, I strongly recommend that you watch WWDC 2020 Session 10063 Background execution demystified. It’s an excellent resource.

Specifically, this talk addresses a common misconception about the app refresh mechanism (BGAppRefreshTaskRequest and the older background fetch API). Folks assume that app refresh will provide regular background execution time. That’s not the case. The system applies a range of heuristics to decide which apps get app refresh time and when. This is a complex issue, one that I’m not going to try to summarise here, but the take-home message is that, if you expect that the app refresh mechanism will grant you background execution time, say, every 15 minutes, you’ll be disappointed. In fact, there are common scenarios where it won’t grant you any background execution time at all! Watch the talk for the details.

When the user ‘force quits’ an app by swiping up in the multitasking UI, iOS interprets that to mean that the user doesn’t want the app running at all. So:

  • If the app is running, iOS terminates it.
  • iOS also sets a flag that prevents the app from being launched in the background. That flag gets cleared when the user next launches the app manually.

This gesture is a clear statement of user intent; there’s no documented way for your app to override the user’s choice.

Note: In some circumstances iOS will not honour this flag. The exact cases where this happens are not documented and have changed over time.

Avoid keeping the SQLite database in the shared container to prevent persistent crashes. Instead of allowing extensions to directly read/write the database, use alternative communication methods like Darwin notifications or writing plist files in the shared container.

iOS will consistently terminate the app and generate a crash log if your app or extension has an open file handle to a file in a shared container during suspension.

0xdead10cc happens, on iOS, when an application that enters the suspended state (not the background state) while holding a lock on an SQLite database stored in a shared container. Indeed, such a locked database would be impossible to use from other processes that want to use it (SQLite would refuse), because the suspended app has no opportunity to release this lock. That’s why iOS preemptively kills the app and removes the lock. By removing the lock, other processes can access the database. By killing the app, iOS makes sure the app won’t proceed using a database that’s no longer in the expected state.

GRDB specific

Locks are acquired when you use the database, and released when you stop using the database. Excuse the gross approximation, but it more or less looks like that:

// Database is not locked
try dbQueue.write { db in
    // Database is locked
}
// Database is not locked

Testing

Test your app extensively in scenarios where it transitions to the background. Use Xcode and Instruments to monitor resource usage and background activity.

More iOS Troubleshooting

OmniLock - Block / Hide App on iOS

Block distractive apps from appearing on the Home Screen and App Library, enhance your focus and reduce screen time.

DNS Firewall for iOS and Mac OS

Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps

Ad