APP crashes can be divided into two types: signal-grabber crashes and signal-grabber crashes.

Signal can be captured by crash

  • Array out of bounds: APP crashes due to index out of bounds while fetching data. Adding nil to an array crashes.
  • Multithreading problem: Multiple threads accessing data may crash. For example, one thread is emptying data while another thread is reading data.
  • Wild pointer problem: A wild pointer crash occurs when a pointer accesses a memory area that has been deleted. Wild pointer problems are the most common cause of App crashes and the most difficult to locate.
  • NSNotification threading problem: There are many thread implementations of NSNotification, synchronous, asynchronous, and aggregated, so improper thread sending and receiving can cause crashes.
  • KVO problem: ‘If your app targets iOS 9.0 and later or OS X V10.11 and later, You don’t need to unregister an observer in its deallocation method. Before 9.0, remove the observer manually, observer crash will occur if it is not removed.

Signal uncatched crash

  • Background Task Timeout
  • The App was killed when the memory size exceeded the system limit. Procedure
  • The main thread, Caton, was killed

The type of signal for a crash

Crashes are caused by Mach exceptions, Objective-C exceptions, which are converted to the corresponding Signal Signal by the BSD layer. You can obtain Crash information by capturing signals.

Crash reports often read:

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Copy the code

EXC_BAD_ACCESS this exception will find the thread in question via SIGSEGV signal. There are many types of signals, and the following table lists some types and explanations:

Exception Type Signal describe
EXC_BAD_ACCESS SIGSEGV It is usually an illegal memory access error, such as accessing a wild pointer that has been freed, or an out-of-bounds C array
SIGBUS Invalid address, meaning that the address corresponding to the pointer is a valid address, but the bus cannot use the pointer properly. This is usually the result of unaligned data access
EXC_BAD_INSTRUCTION SIGILL Illegal instruction. Is a signal sent to a process when it tries to execute an invalid instruction. The reason why an executable program contains illegal instructions is usually because the CPU architecture is incorrect and the march specified at compile time is different from the march of the actual machine executing it. In this case, because the toolchain is the same, the connection script is the same, so the executable can be executed without an exec Format error. However, it may contain some incompatible instructions. Another possibility is that the program does not have sufficient execution permissions. For example, a program running in user mode can only execute non-privileged instructions. Once the CPU encounters a privileged instruction, an illegal Instruction error will occur
EXC_ARITHMETIC SIGFPE A signal sent to a process when it has performed an incorrect arithmetic operation
EXC_EMULATION SIGEMT An implementation-defined hardware failure
EXC_SOFTWARE SIGSYS Indicates an invalid system call. For some unknown reason, the process executed a system call instruction, but its parameters indicating the type of the system call were invalid
SIGPIPE Pipe burst. This signal is usually generated in interprocess communication, such as when two processes communicate using FIFO(pipe). If the reading pipe is not opened or terminates unexpectedly, the writing process receives the SIGPIPE signal. In addition, for the two processes that use the Socket to communicate, the writing process terminates while writing the Socket
SIGABRT The most common causes of this type of exception crash are uncaught Objective-C/ C ++ exceptions and calls to ABORT (). If the application Extension Extension takes too long to start (for example, it is terminated by the Watch Dog), the application Extension is terminated with this exception type. If an extension terminates due to a hang at startup, the exception subtype of the generated crash report will be LAUNCH_HANG. Because the extension has no main function, any time spent on initialization is done in the static constructor and the +load method in the extension library. You should put off this work as long as possible
SIGKILL The process was terminated at the request of the system. Look at the termination reason field to better understand the termination reason. The termination reason field will contain the namespace followed by the code. The following code is for watchOS: Termination code 0xC51BAD01 indicates that a monitoring application was terminated because it was taking up too much CPU time while performing background tasks. You can solve this problem by optimizing the code that performs background tasks to improve CPU efficiency, or by reducing the amount of work that is performed while the application is running in the background. The termination code 0xC51BAD02 indicates that a monitoring application was terminated because it failed to complete a background task within the allotted time. To solve this problem, reduce the amount of work that the application executes in the background. Termination code 0xC51BAD03 indicates that a monitoring application failed to complete a background task within the allotted time, and that the system is generally so busy that the application may not receive sufficient CPU time to perform the background task. While an application can avoid this problem by reducing the amount of work it performs in background tasks, 0xC51BAD03 does not indicate that the application is doing anything wrong. More likely, the application will not be able to do its job due to the overall system load.
EXC_BREAKPOINT SIGTRAP Trace trap, created by breakpoint instruction or other trap instruction, used by the debugger. Similar to an exception exit, this exception is intended to give additional debuggers the opportunity to interrupt the process at a specific point in its execution. You can use the _builtin_trap() function to trigger this exception from your own code. If no debugger is attached, the process is terminated and a crash report is generated. A null pointer to swift or a type conversion failure can also cause this signal

These signals can be caught by registering signalHandler. The implementation code is as follows:

void registerSignalHandler(void) { signal(SIGSEGV, handleSignalException); signal(SIGFPE, handleSignalException); signal(SIGBUS, handleSignalException); signal(SIGPIPE, handleSignalException); signal(SIGHUP, handleSignalException); signal(SIGINT, handleSignalException); signal(SIGQUIT, handleSignalException); signal(SIGABRT, handleSignalException); signal(SIGILL, handleSignalException); } void handleSignalException(int signal) { NSMutableString *crashString = [[NSMutableString alloc]init]; void* callstack[128]; int i, frames = backtrace(callstack, 128); char** traceChar = backtrace_symbols(callstack, frames); for (i = 0; i <frames; ++i) { [crashString appendFormat:@"%s\n", traceChar[i]]; } // write crashString to your log file NSLog(crashString); }Copy the code

Collectable crash information

The captured crash information can be captured through a third-party open source library such as PLCrashReporter and uploaded to a company server for monitoring, or it can be monitored through Bugly without having to upload to a company’s server.

Crash collection can capture Crash events either by catching Mach exceptions or by catching Unix signals

  • Mach exception mode

  • Unix signal
signal(SIGSEGV,signalHandler);
Copy the code

PLCrashReporter and other three-party libraries adopt the Mach exception +Unix signal mode. Even in the case of optimal capture of Mach exception, they give up catching EXC_CRASH exception and choose to capture the corresponding SIGABRT signal.

After PLCrashReporter stores the crash stack information, the App crashes, and all the data in the memory after the crash is gone. When the App restarts next time, it can retrieve the logs and upload them for analysis.

Uncatable crash information collection

Background Task mode

The iOS Background can be kept alive in Background Mode, Background Fetch, Silent Push, PushKit, and Background Task.

  • Background Mode, such as music playback, VOIP, map app
  • Background Fetch sets an interval to request network data at intervals. Because users can turn this mode off in Settings, it is rarely used
  • Slience Push is a Push, will wake up in the background for 30 seconds, the priority is low, will call application: didReceiveRemoteNotifiacation: fetchCompletionHandler: This delegate is the same as the delegate for a normal remote Push notification push call.
  • Push Kit keeps the app alive for 30 seconds after it wakes up in the background, which is mainly used for VOIP applications to improve experience.
  • Background Task, the most common and used mode, requests 3 minutes for additional operations when entering the Background.

Once the APP is back in the background, it has only a few seconds to continue executing code before it is suspended by the system and all threads are suspended. Whether the thread is reading or writing to files or memory is suspended. However, the data read/write process cannot be suspended but can only be interrupted. When interrupted, data read/write is abnormal and files are easily damaged. Therefore, the system will actively kill the App process.

For the Background Task you can use a UIApplication beginBackgroundTaskWithName: expirationHandler: Or beginBackgroundTaskWithExpirationHandler: ways to apply for some extra time to extend the time of the background. Use as follows:


- (void)applicationDidEnterBackground:(UIApplication *)application {
    self.backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^( void) {[self yourTask];
    }];
}
Copy the code

The task will run for 3 minutes at most, after which the APP will be killed and crash. This is also why apps tend to crash when they retire to the background.

For this kind of crash, we usually start a timer in the application of 3-minute Task. When the app is still running, it means that the app is about to be killed by the system. At this time, we record logs to achieve monitoring effect.

OOM

OOM, which stands for out-of-memory, an “alternative” Crash caused by iOS Jetsam mechanism. For example, the overall Memory usage Of the system is high, and the system kills apps with lower priorities based on their priorities, or the iOS system kills apps after setting the Memory usage upper limit for a single App.

You can analyze JetsamEvent logs by choosing your phone’s Settings -> Privacy -> Analysis -> Analyze Data to find the related log information at the beginning of JetsamEvent. Find per-process-limit and use rPages * pageSize to get the OOM threshold

{ "uuid" : "092ff2cc-0290-3310-a375-cc69c192b94d", "states" : [ "daemon", "idle" ], "killDelta" : 6781, "lifetimeMax" : 8241, "age" : 4581135570382, "purgeable" : 485, "fds" : 50, "genCount" : 0, "coalition" : 832, "rpages" : 7736, "reason" : "per-process-limit", "pid" : 8326, "idleDelta" : 174877955757, "name" : "photoanalysisd", "cpuTime" : 111.697557},Copy the code

“Rpages” : 7736 indicates the number of memory pages is 7736, and find the pageSize field from the file

  "largestZoneSize" : 15192064,
  "pageSize" : 4096,
  "uncompressed" : 77779,
  "zoneMapSize" : 70713344,
Copy the code

Calculate the current app memory limit 7736*4096/1024/1024 = 30MB