Special note
This article is a simple translation and processing of Apple’s SIGKILL literature, and does not represent my own views
What is a CRASH (SIGKILL)
When there is SIGKILL or EXC_CRASH (SIGKILL) in the crash log, it indicates that the operating system killed our process from above. Typically, crash logs contain a specific cause and an error code indicating the cause. Below, the error code for this crash is 0xdead10cc
Exception Type: EXC_CRASH (SIGKILL)Exception Codes: 0x0000000000000000, 0x0000000000000000Exception Note: EXC_CORPSE_NOTIFYTermination Reason: Namespace RUNNINGBOARD, Code 0xdead10cc
Copy the code
The following lists and explains the meanings of each error code in detail
-
The 0x8badF00D sound (ate bad food) means eating bad food. The operating system killed the app because of the watchdog. See the Apple documentation Addressing Watchdog Terminations for details.
-
0xC00010FF pronounced (cool off). For more information on how to make your application more efficient, see iOS Performance and Power Optimization with Instruments WWDC Session.
-
0xdead10CC sounds (dead Lock), indicating that the operating system killed the app due to file and database locks that occurred while the app was suspended; This requires that we must perform background tasks in the main thread calls the API beginBackgroundTask (withName: after expirationHandler:), to ensure our mission and lock cleaning operation completed before the app to hang up. Also in the app plugin, use the beginActivity(Options: Reason 🙂 API to do this.
-
0xBaadca11 Pronunciation (bad call). App responds to PushKit and the CallKit call fails, so app is killed by the system.
-
0xBAD22222. Killed by the system because the VoIP call is too frequent.
-
0xC51bad01.watchos was killed because the background task was consuming too much CPU time. This requires optimizing and reducing the CPU time of background tasks, improving CPU efficiency, or eliminating a large number of tasks while in the background.
-
0xc51bad02.watchos Kills app because background tasks cannot be completed during initialization time. Reducing the number of background tasks can solve this problem.
Addressing Watchdog Terminations
preface
Users typically expect apps to turn on in seconds and respond to gestures and clicks. Therefore, the iOS operating system enables the watchdog monitoring during startup. If the APP does not respond to user operations for a long time during startup, the operating system will kill the app. A watchdog crash is indicated in the log by the common code 0x8BADF00D (pronounced ate bad food)
Exception Type: EXC_CRASH (SIGKILL)Exception Codes: 0x0000000000000000, 0x0000000000000000Exception Note: EXC_CORPSE_NOTIFYTermination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
Copy the code
When an app blocks the main thread for a period of time, it is killed by the watchdog. The general event is as follows:
- Synchronous network requests
- Tasks that deal with large amounts of data, such as loading and processing large JSON files or 3D models
- Trigger a large number of synchronous Core Data save operations
- Vision request operations
To see why the blocking main thread crashes, let’s first consider the most common example: refreshing the UI with synchronous network requests for data. If the main thread is busy with network requests, the app can’t handle UI events, such as clicks and swipes, until the network requests return data. If an app is spending too much time on web requests instead of responding to the user’s swipe gestures. This makes the app feel unresponsive and a terrible experience.
Description Of App watchdog cause in crash logs
When an app is killed due to slow startup or unresponsiveness, the crash log contains important information such as how long it took the app to start. As follows, an iOS app cannot draw its UI quickly during startup:
Termination Description: SPRINGBOARD, scene-create watchdog transgression: Application < com. Example. MyCoolApp > : 667 exhausted of real (wall clock) time most egregious nest-feathering of 19.97 seconds | ProcessVisibility: Foreground | ProcessState: Running | WatchdogEvent: scene-create | WatchdogVisibility: Foreground | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 15.290 15.290 (user, system 0.000) and 28% CPU, "|" Elapsed application CPU time (seconds) : 0.367, 1% CPU "|)Copy the code
Special reminder:
For ease of reading, this example includes line breaks. In the original crash log, watchdog messages are rarely wrapped.
When scene-create appears in the Termination Description, it means that the app has not drawn the first frame within the allowed wall clock time. If scene-update appears in the Termination Description, it indicates that the app is not refreshing the UI because the master thread is too busy.
Special reminder:
Scene-create and scene-update appear in the crash log to indicate that the screen is displayed on the device. It has nothing to do with UIScene in UIKit.
Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time = Elapsed total CPU time This CPU time, like application CPU time, includes all CPU kernel time, which can exceed 100%. For example, if one CPU core is 100% consumed and the other is 20% consumed, the total time is 120%.
Elapsed Application CPU time indicates how much time our app spent in the Wall clock time. If this number is extreme, there may be something wrong with the code. If this number is extremely high, it means that all threads in your app are doing important work — this number means all threads, not just the main thread. A low number indicates that the app is idle, probably waiting for system resources or network requests to return.
App Background Task watchdog Reason explanation message (watchOS)
In addition to the app having a watchdog, watchOS also has a watchdog to monitor the execution of background tasks. As follows: App failed to complete the background tasks of Watch Connectivity within the time limit.
Termination Reason: CAROUSEL, WatchConnectivity watchdog transgression. Exhausted wall time allowance of 15.00 seconds.Termination Description: SPRINGBOARD, CSLHandleBackgroundWCSessionAction watchdog transgression: Xpcservice < com. Example. MyCoolApp. Watchkitapp. Watchextension > : 220:220 exhausted real (wall clock) time most egregious nest-feathering of 15.00 seconds | <FBExtensionProcess: 0x16df02a0; xpcservice<com.example.MyCoolApp.watchkitapp.watchextension>:220:220; typeID: com.apple.watchkit> Elapsed total CPU time (seconds): 24.040 24.040 (user, system 0.000) and 81% CPU | Elapsed application CPU time (seconds) : 1.223, 6% CPU, lastUpdate 2020-01-20 11:56:01 +0000Copy the code
Special note:
For readability, this example includes multiple line breaks. In the original crash log, watchdog messages are rarely wrapped.
For more descriptions of wall clock time and CPU time, refer to the document Interpret the App Responsiveness Watchdog Information.
Identify the cause of the watchdog trigger
Most of the time, the stack shows you exactly where the main thread is spending its time. For example, if an app uses a synchronous network request in the main thread, the network request method is displayed in the main thread stack.
Thread 0 name: Dispatch queue: com.apple.main-threadThread 0 Crashed:0 libsystem_kernel.dylib 0x00000001c22f8670 semaphore_wait_trap + 81 libdispatch.dylib 0x00000001c2195890 _dispatch_sema4_wait$VARIANT$mp + 242 libdispatch.dylib 0x00000001c2195ed4 _dispatch_semaphore_wait_slow + 1403 CFNetwork 0x00000001c57d9d34 CFURLConnectionSendSynchronousRequest + 3884 CFNetwork 0x00000001c5753988 +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 116 + 147285 Foundation 0x00000001c287821c -[NSString initWithContentsOfURL:usedEncoding:error:] + 2566 libswiftFoundation.dylib 0x00000001f7127284 NSString.__allocating_init+ 680580 (contentsOf:usedEncoding:) + 1047 libswiftFoundation.dylib 0x00000001f712738c String.init+ 680844 (contentsOf:) + 96 MyCoolApp 0x00000001009d31e0 ViewController.loadData() (in MyCoolApp) (ViewController.swift:21)Copy the code
However, the stack for the main thread does not necessarily contain the code that caused the problem. For example, imagine if your app performs a task that costs 4s, but the wall clock time is only 5s. When the application is killed after 5 seconds, the task function that took 4 seconds does not show up on the stack, even though it spent 80% of the time. The crash log records the current stack in which the program was killed, even if the current stack was not the source of the problem. During app startup, you can use all the stacks in the Watchdog crash log to help you find the stack at the end of the application. With the current stack, you can go back to previous tasks and narrow down the problem with the current code.
In addition, before the release of the app, potential problems should be solved during development, and after the release of the app, the performance of the program needs to be monitored. Two documents from Apple, Reducing Your App’s Launch Time and Improving App Responsiveness, provide additional information.
Find hidden network library synchronization code
The problem of the main thread being blocked by synchronous network requests and being killed by the watchdog is sometimes not directly visible and may be lurking in the shadows, with an eye on the enemy. For example, this stack:
Thread 0 name: Dispatch queue: com.apple.main-threadThread 0 Crashed:0 libsystem_kernel.dylib 0x00000001c22f8670 semaphore_wait_trap + 81 libdispatch.dylib 0x00000001c2195890 _dispatch_sema4_wait$VARIANT$mp + 242 libdispatch.dylib 0x00000001c2195ed4 _dispatch_semaphore_wait_slow + 1403 CFNetwork 0x00000001c57d9d34 CFURLConnectionSendSynchronousRequest + 3884 CFNetwork 0x00000001c5753988 +[NSURLConnection sendSynchronousRequest:returningResponse:error:] + 116 + 147285 Foundation 0x00000001c287821c -[NSString initWithContentsOfURL:usedEncoding:error:] + 2566 libswiftFoundation.dylib 0x00000001f7127284 NSString.__allocating_init+ 680580 (contentsOf:usedEncoding:) + 1047 libswiftFoundation.dylib 0x00000001f712738c String.init+ 680844 (contentsOf:) + 96 MyCoolApp 0x00000001009d31e0 ViewController.loadData() (in MyCoolApp) (ViewController.swift:21)Copy the code
Shows that the app triggers a download by calling init(contentsOf:) via an HTTPS URL. On frame 7, the API implicitly initiates a synchronous network request prior to initialization. Even if this initialization is done quickly and does not show up in the crash log, it may still be the cause of the watchdog. Other classes that initialize with a URL parameter, such as XMLParser and NSData, have similar effects:
Other examples of similar operations implicitly involving synchronous network requests are as follows
- SCNetworkReachability, the accessibility API, the default operation is synchronous. Seems harmless method such as SCNetworkReachabilityGetFlags (: :), collapse could trigger guard dog.
- The DNS family of methods provided by BSD, for example
gethostbyname(_:)
 和Âgethostbyaddr(_:_:_:)
Calling on the main thread is never safe. Methods likegetnameinfo(_:_:_:_:_:_:_:)
 和Âgetaddrinfo(_:_:_:_:)
Only if you use only IP addresses instead of DNS names (specifiedAI_NUMERICHOST
å’ŒNI_NUMERICHOST
) is safe.
The problem of synchronizing network requests depends largely on the current network environment. If you always test in an office with a good network, you’ll never find a problem. However, once the app is available to users, the user’s network environment is strange: problems with synchronizing network requests become common. In Xcode, you can simulate a variety of possible network environments to help you find problems. Refer to the Test Under Adverse Device Conditions (iOS) documentation.
Remove code from the main thread
Move all non-essential long tasks in your app to the background thread. By moving the task to the background thread, the app main thread can quickly open and process the response event gesture. As follows, place the network request on the child thread instead of the main thread. By doing this, the main thread can handle clicks, swipes, etc., making the app smooth.
If the long task code is in a system library code, see if the system library provides a non-mainline mode. For example, you can use the asynchronous method loadAsync(contentsOf:withName:) in RealityKit instead of the synchronous method load(contentsOf:withName:). Similarly, in the Vision provides preferBackgroundProcessing, related processing logic is not in the main thread.
If the network request code is the cause of the watchdog, consider the following common solutions
- Use the asynchronous networking code URLSession. This is the best method, and the asynchronous approach has many benefits, including secure access to the network without worrying about multithreading
- Use NWPathMonitor instead of SCNetworkReachability to monitor network path changes. When you call start(queue:), the operating system passes network changes in a queue so that the network state changes are safe on the main thread
- Network requests that are synchronized in child threads. If your network code is difficult to call asynchronously, such as when using cross-platform code to request the network, to avoid the watchdog, you can put code synchronization code in child threads
- Manual parsing is not recommended in many cases. useURLSessionThere are DNS resolution code, not manual resolution. If code switching is difficult and you still need to resolve DNS addresses manually, use the asynchronous API,
CFHost
Or in the<dns_sd.h>
The API declared in.