iOS-APP crash analysis

※ background

  • A crash is a signal sent by the operating system to an app that is running abnormally., program crash is the direct factor that declares the death of the app. After we have accumulated a certain amount of experience in iOS development, we need to consider facing various problems that cause the app to crash in our project. Carefully look at the project crash log, you will There are some corresponding signals output to us
  • The first thing the Xcode crash log provides us with is the crash type. Through the accumulation of certain development experience, according to the crash type, we can quickly think about the crash scenario corresponding to its type. So what are the crash types?
  • At the application level and actual development scenarios, what improper operations will cause the app to crash?

1. Crash type scenarios

APP crashes can be divided into two categories: signal-catchable crashes and signal-uncatchable crashes.

Signal catchable crashes

  • Array out of bounds: The index goes out of bounds when fetching data, and the APP crashes. Adding nil to an array will crash.
  • Multi-threading problem: Multiple threads may crash when accessing data. For example, one thread is blanking data while another thread is reading data.
  • Wild pointer problem: When a pointer points to a deleted object and accesses a memory area, a wild pointer crash occurs. Wild pointer problems are the most common cause of App crashes and are also the most difficult to locate.
  • NSNotification thread problem: NSNotification has many thread implementation methods, synchronous, asynchronous, and aggregation, so inappropriate thread sending and receiving will cause crash problems.
  • KVO question: '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, you need to manually remove the observer. If it is not removed, the observer will crash.
  • Improper memory management issues:

Signal uncatchable crash

  • Background task timeout
  • App exceeding system limit memory size is killed
  • The main thread is stuck and killed

2. Crash log

1. Under what circumstances will a crash log be generated?

  • There are two main situations that will generate crash logs:
    • 1. The application violates operating system rules.
    • 2. There are bugs in the application.

Violating operating system rules

  • This includes watchdog timeouts on startup, resume, suspend, exit, user force exit, and low memory termination.
  • Watchdog timeout mechanism
    Starting from iOS 4.x, when exiting the application, the application will not terminate immediately, but will retreat to the background. However, if your app doesn't respond quickly enough, the operating system may terminate your app and generate a crash log. These events correspond to the following UIApplicationDelegate methods:
    application:didFinishLaunchingWithOptions:
    applicationWillResignActive:
    applicationDidEnterBackground:
    applicationWillEnterForeground:
    applicationDidBecomeActive:
    applicationWillTerminate:
    For all of the above methods, the application only has a limited time to complete processing. If it takes too long, the operating system will terminate the app.
    Note: This can easily happen if you don't put operations that take a long time (such as network access) on a background thread.

There is a bug in the application

3. Parse the crash report after symbolization

  • Use the symbolicatecrash tool to parse crash logs.
    You can use the following command to find the location of the local symbolicatecrash tool
    find /Applications/Xcode.app -name symbolicatecrash -type f
    to copy it, and then use the following command to parse the crash log.
./symbolicatecrash ./xxx.crash ./xxx.app.dSYM > symbol.crash

1. Key information in the header

  • Incident Identifier: A unique identifier. There will be no crash reports sharing the same identifier.
  • CrashReporter Key: is the unique key value corresponding to the device identification. Although it is not a real device identifier, it is also a very useful piece of intelligence: if you see 100 crash logs with the same CrashReporter Key value, or only a few different CrashReport values, it means this is not a common problem. The problem only occurs on one or a few devices.
  • Process: is the application name. The number in square brackets is the process ID of the application when it crashed.
  • Version: The version number of the crashed process. This value is contained in CFBundleVersion and CFBundleVersionString.
  • Code Type: The architecture of the device where the crash log is located. Will be one of ARM-64, ARM, x86-64, or x86.
  • OS Version: The system version when the crash occurred

2. Key fields in exception information

  • Exception Codes: Processor-specific information about the exception is encoded into one or more 64-bit numbers. Normally, this area is not rendered because parsing the exception code into a human-readable description is done in another area.
  • Exception Subtype: A human-readable name for the exception code
  • Exception Message: Additional human-readable information extracted from the exception code.
  • Exception Note: Extra information that is not specific to an exception type. If this field contains SIMULATED (this is not a crash) then the process did not crash, but was killed by the watchdog
  • Termination Reason: The reason when a process is terminated.
  • Triggered by Thread: The thread where the exception occurs.

3. Other common abnormalities

  • Bad Memory Access [EXC_BAD_ACCESS // SIGSEGV // SIGBUS]
    The process attempted to access invalid memory, or attempted to access memory in a way that the memory's protection level does not allow (for example, writing to read-only memory). The exception type field (Exception Subtype) contains a kern_return_t describing the error, and the memory address of the error access. Here are some tips for debugging a Bad Memory Access:
    • (1). If objc_msgSend or objc_release is near the top of the backtraces, the process may be trying to send a message to a released object. You should use the Zombies instrument to better understand this crash.
    • (2). If gpus_ReturnNotPermittedKillClient is near the top of the traceback, the process was killed due to trying to render with OpenGL ES or Metal in the background. See QA1766: How to fix OpenGL ES application crashes when moving to the background.
    • (3).用 Address Sanitizer (xcode7引入的新特性)来跑程序。
      The address sanitizer adds additional instrumentation around memory access in your compiled code. As your application runs, Xcode will alert you if memory is accessed in a way that could lead to a crash.
  • Abnormal Exit [EXC_CRASH // SIGABRT]
    The process exited abnormally. The most common causes of crashes with this exception type are uncaught Objective-C and C++ exceptions and calls to abort().
    If they take too much time to initialize, the program will be terminated because the watchdog is triggered. If it is suspended during startup, the generated crash report exception type (Exception Subtype) will be launch_hang.
  • Trace Trap [EXC_BREAKPOINT // SIGTRAP]
    is similar to an exit exception, this exception is intended to give the attached debugger the opportunity to interrupt the process at a specific point in its execution. You can raise this exception by actively calling the __builtin_trap() function. If no debugger is connected, the process will be terminated and a crash report will be generated.
  • Illegal Instruction (Illegal instruction) [EXC_BAD_INSTRUCTION // SIGILL]
    The process attempted to execute an illegal or undefined instruction. The process may be trying to jump to an invalid address via a misconfigured function pointer.
  • Resource Limit [EXC_RESOURCE]
    This process has exceeded the resource consumption limit. This is a notification from the operating system that the process is using too many resources. This is not a crash but will generate a crash log.
  • Other exception information
    • 0x8badf00d: pronounced as "ate bad food"! (Change the numbers into letters, it looks very similar :p) This code indicates that the application was terminated by iOS due to a watchdog timeout. Typically an application takes too long to start, terminate, or respond to application system events.
    • 0xbad22222: This code indicates that the VoIP application was terminated due to too frequent restarts.
    • 0xdead10cc: pronounced as "dead lock"! This code indicates that the application was terminated because it occupied system resources while running in the background, such as the address book database was not released.
    • 0xdeadfa11: pronounced as "dead fall"! This code indicates that the application was forced to quit by the user. According to Apple's documentation, force quit occurs when the user presses and holds the on/off button until "Slide to power off" appears, and then presses and holds the Home button. Force quit will generate a crash log containing the 0xdeadfa11 exception code, because most force quits are caused by the application blocking the interface.

4. Thread traceback

The following thread frame records this information

2 
YDTCMoments 
0x34648e88 
0x83000 + 8740
  • Frame number - here it's 2. (The order of occurrence of numbers from large to small)
  • The name of the binary library - in this case YDTCMoments.
  • The address of the calling method - here is 0x34648e88.
  • The fourth column is divided into two sub-columns, a base address and an offset. Here it is 0×83000 + 8740, the first number points to the file, and the second number points to the line of code in the file.

4. Crash signal

Defined in header <signal.h>

#define SIGTERM /*implementation defined*/
#define SIGSEGV /*implementation defined*/
#define SIGINT /*implementation defined*/
#define SIGILL /*implementation defined*/
#define SIGABRT /*implementation defined*/
#define SIGFPE /*implementation defined*/
... ...
  • Commonly seen crash signals include: SIGTERM, SIGSEGV, SIGINT, SIGILL, SIGABRT, SIGFPE, SIGBUS, SIGTRAP, EXC_BAD_ACCESS, EXC_ARITHETIC, (watchdog, OOM), etc.

TARGET TERM

  • Program end (terminate) signal. Unlike SIGKILL, this signal can be blocked and processed. Usually used to require the program to exit normally by itself.
  • This signal is generally not processed in iOS

SIGSEGV

  • Segmentation fault message : invalid memory access (segmentation fault), invalid memory address reference signal, is a serious problem caused by the operating system. This error occurs when a hardware error occurs (generally uncommon), an unreadable memory address is accessed, or data is written to a protected memory address.
  • By default, the code page does not allow write operations. When a pointer in the app points to the code page and attempts to modify the value of the corresponding location, you will receive this crash type. This crash type is also received when the value of a pointer is to be read, but it is initialized to a garbage value pointing to an invalid memory address.
  • This type of crash is slightly more difficult to debug, and the most common cause of this type of crash is: incorrect type conversion. Avoid overusing pointers or trying to manually modify pointers to read private data structures. Otherwise, you need to pay attention to memory alignment and padding issues when modifying pointers. If not handled properly, you will receive this crash type.
  • Scenarios in iOS : Common wild pointer access:
    In non-ARC mode, wild pointer access to Delegate objects often occurs in iOS.
    In ARC mode, iOS often appears to hold objects that may be released in the Block code block.

SIGINT

  • external interrupt, usually initiated by the user An integer interrupt signal
    usually input by the user
  • This signal is generally not processed in iOS

SEAL

  • Signal illegal instruction : invalid program image, such as invalid instruction, a signal that must kill the process under any circumstances, occurs when an illegal instruction is executed on the processor.
  • Execution of illegal instructions refers to when the function pointer points to another function, but there is a problem with the function pointer and points to a section of memory or a data segment that has been released. Sometimes the received signal SIGILL or EXC_BAD_INSTRUCTION is the same thing, but signals such as EXC_* are general domain signals that do not depend on the architecture.
  • Scenario in iOS : Due to the limitations of the iOS application platform, it is prohibited to kill the process in the iOS APP, so it is generally not processed.

SIGABRT

  • Interrupt signal (SIGNAL ABORT) : abnormal termination condition, as is eg initiated by abort(), usually an interrupt signal caused by an exception. When an exception occurs, the system will call the abort() function to send the signal. When the operating system detects an unsafe situation, this type of model will take more control and ask the process to clean up if necessary. When it occurs, the console will usually output a large amount of information explaining exactly what went wrong. Since it is a controlled crash, the traceback information can be printed by typing the bt command while running LLDB.
  • Scenarios in iOS : One is due to a method calling error (a method that cannot be called is called), and the other is due to an array access out-of-bounds problem

SIGFPE

  • erroneous arithmetic operation such as divide by zero
    floating point number exception signaling notification
  • Scenario in iOS : Generally caused by the divisor being 0

SIGBUS

  • Bus error signal : represents invalid memory access, that is, the memory accessed is an invalid memory address (the location pointed to by the address is not a physical memory address, but may be the address of a hardware chip)

SIG TRAP

  • Trap signal : Not really a crash signal. The trap instruction will be executed on the CPU and sent. The LLDB debugger normally handles this signal and stops execution at the specified breakpoint. (If you receive this signal for unknown reasons, it can usually be solved by clearing the last output and then rebuilding it)

EXC_BAD_ACCESS

  • Crash caused by wild pointers : caused by accessing a memory that has been released, and will occur when sending a message to a released object. The most common cause of this crash is that the wrong ownership modifier is used when initializing a variable in the initialization method, causing the object to be released prematurely.
    • eg: Create an NSDictionary containing elements for UIViewController in the viewDidLoad method, and set the dictionary ownership modifier to assign instead of strong. At this time, if you access in viewWillAppear, you will get this type of crash, because the object accessed is an object that has been released.
  • When this crash occurs, you often cannot get useful stack information by viewing the crash log. You can consider using the NSZombieEnabled environment variable to solve this problem. It can track the release process of objects and be used to debug memory-related issues.
    • NSZombieEnabled works: APP starts NSZombie instead of letting the APP crash directly. An incorrect memory access will turn into a five-point failure message and send it to the zombie object. The zombie object displays the received message and then jumps into the debugger so you can see exactly what went wrong.
    • NSZombieEabled setting: In Xcode->Product-Edit Scheme->Open the scheme page->Diagnostics of the Run option->Set the NSZombieEnabled environment variable (check Zombie Objects)
    • Zombies were very useful before ARC. Since ARC, if you pay attention to the ownership of the objects you use during development, you usually won't encounter this memory-related crash.
  • Scenario in iOS : Wild pointers cause crash:
    • 1. The memory has not been changed after the object is released. The original memory is well preserved and may not crash or may have logical errors (random crashes).
    • 2. The memory has not been changed after the object is released, but some necessary things have been deleted when it was destructed. It may not crash. Crash may cause logical errors (random crashes) when accessing dependent objects such as class members.
    • 3. After the object is released, the memory is modified, and inaccessible data is written, and an error occurs immediately. The crash is likely to be on objc_msgSend (Crash must occur, common).
    • 4. After the object is released, the memory has been modified and accessible data has been written. It may not crash, a logic error may occur, or inaccessible data may be indirectly accessed (random crash).
    • 5. After the object is released, the memory has been modified and accessible data has been written. However, when accessing again, the code executed corrupts other data. If you encounter this kind of crash, you can only cry (random crash, difficult, Low probability)! !
    • 6. Release the object again after releasing it (Crash is almost inevitable, but there are exceptions and it is very common).

EXC_ARITHETIC

  • In iOS, the app will generally receive a signal from this general field when dividing by zero. Same as: SIGFPE

watchdog

  • Watchdog timeout : This type of crash is easier to identify because the error code is fixed 0x8badf00d. It often occurs when performing synchronous network calls that block the main thread.

OOM

  • Out-of-memory crash: Literally means that the memory exceeds the limit. It is an "alternative" Crash caused by the Jetsam mechanism of iOS. It is different from regular Crash. OOM events cannot be captured through Crash monitoring solutions such as Signal capture.
    Of course, there will also be FOOM/BOOM, where FOOM stands for Foreground-out-of-memory, which means that the App consumes too much memory in the foreground and causes the system to kill it. This is what this article will discuss. OOM in the background is not necessarily caused by the app itself. Most of the time it is because the current App in the foreground takes up too much memory. In order to ensure the normal operation of the foreground application, the system cleans up the background application.

5. Customize code to obtain crash log information

swift:

NSSetUncaughtExceptionHandler {
    
     exception in
	print("exception: \(exception.callStackSymbols)")
}

OC:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    
    
    NSSetUncaughtExceptionHandler(&caughtExceptionHandler);
    /*
     Changes the top-level error handler.
      Sets the top-level error-handling function where you can perform last-minute logging before the program terminates
    */
    return YES;
}

void caughtExceptionHandler(NSException *exception){
    
    
    /**
     *  获取异常崩溃信息
     */
    NSArray *callStack = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    NSString *content = [NSString stringWithFormat:@"========异常错误报告========\nname:%@\nreason:\n%@\ncallStackSymbols:\n%@",name,reason,[callStack componentsJoinedByString:@"\n"]];
    
    //把异常崩溃信息发送至开发者邮件
    NSMutableString *mailUrl = [NSMutableString string];
    [mailUrl appendString:@"mailto:[email protected]"];
    [mailUrl appendString:@"?subject=程序异常崩溃信息,请配合发送异常报告,谢谢合作!"];
    [mailUrl appendFormat:@"&body=%@", content];
    // 打开地址
    NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
}

※Summarize

  • 1. In fact, for app-side crashes in iOS, understanding these crashes and the underlying logic can help you locate the problem faster.
  • 2. More manifestations at the application level include: out of bounds, early release, double free, improper memory management, etc.

I haven’t sorted out these things in these years of iOS development. Now I calm down and start sorting them out. I will continue to update them in the future. If the content is useful, thank you for liking it. If you have any questions, please leave a comment.

Guess you like

Origin blog.csdn.net/MOON_YZM/article/details/127553830