Today, I saw an article on the wechat official account. We can try to make our own Crash log records together

Developing iOS applications to solve the Crash problem is always a difficult problem. Crash can be divided into two types. One is caused by EXC_BAD_ACCESS. The reason is that the memory address that does not belong to the process is accessed. The other is an uncaught Objective-C exception (NSException) that causes the program to crash by sending a SIGABRT signal to itself. There are ways to log uncaught Objective-C exceptions, and if properly logged, most crashes can be resolved.

I. System Crash

For abnormal program exit caused by system Crash, it can be caught by UncaughtExceptionHandler mechanism. That is, anything other than a catch is caught by the system’s own error handling. All we need to do is replace the ExceptionHandler with a custom function.

Handle signal

Objective-c exception handling is not used to obtain signal. If you want to process signal, you need to register SIGABRT, SIGBUS, SIGSEGV and other handlers when signal occurs using the SIGNAL mechanism of Unix standard. From this function we can output stack information, version information, and anything else we want.

SIGHUP This signal is sent at the end of a connection (normal or abnormal) between the user terminal, usually at the end of the control process at the terminal, to inform the individual jobs in the same session that are no longer associated with the control terminal. When you log in to Linux, the system assigns a terminal (Session) to the user. All programs running on this terminal, including foreground and background process groups, generally belong to this Session. When the user logs out of Linux, the foreground process group and the background processes that have output to the terminal will receive the SIGHUP signal. The default action of this signal is to terminate the process, so the foreground process group and processes with terminal output in the background are aborted. However, it is possible to catch this signal, for example wget can catch the SIGHUP signal and ignore it so that wGET can continue downloading even after logging out of Linux. In addition, this signal is used to tell a daemon that is disconnected from the terminal to re-read the configuration file.

2) SIGINT interrupt signal, emitted when the user types INTR character (usually CtrL-C), to inform the foreground process group to terminate the process.

3) SIGQUIT is similar to SIGINT, but is controlled by the QUIT character (usually Ctrl-). A core file is generated when a process exits due to SIGQUIT, which is similar to a program error signal in this sense.

4) SIGILL executed illegal instructions. This is usually due to an error in the executable itself or an attempt to execute a data segment. This signal can also be generated when the stack overflows.

5) SIGTRAP is generated by breakpoint instruction or other trap instruction. Used by the debugger.

6) SIGABRT calls abort to generate the signal.

7) Invalid SIGBUS addresses, including memory address alignment errors. Such as accessing a four-word integer whose address is not a multiple of 4. It differs from SIGSEGV in that the latter is triggered by illegal access to a valid storage address (such as access to a non-own storage space or read-only storage space).

8) SIGFPE is emitted when a fatal arithmetic error occurs. This includes not only floating-point errors, but also overflow and all other arithmetic errors such as divisor zero.

9) SIGKILL is used to end the program immediately. This signal cannot be blocked, processed, or ignored. If the administrator finds that a process cannot be terminated, you can attempt to send this signal.

10) SIGUSR1 is left to the user

11) SIGSEGV tries to access memory that is not allocated to it, or tries to write data to a memory address that has no write permission.

12) SIGUSR2 is left to the user

13) The 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.

14) SIGALRM clock timing signal, which is used by the alarm function to calculate the actual time or clock time.

15) SIGTERM program terminate signal, which, unlike SIGKILL, can be blocked and processed. The shell command kill produces this signal by default. We only try SIGKILL if the process cannot be terminated.

17) The parent process receives this signal when the SIGCHLD child process terminates. If the parent process does not process the signal and does not wait for the child process, the child process is called a zombie if it terminates but still holds entries in the kernel process table. This situation should be avoided (the parent either ignores SIGCHILD, catches it, waits for its derived child, or terminates first, in which case the init process automatically takes over).

SIGCONT causes a stopped process to continue execution. This signal cannot be blocked. A handler can be used to make a program do specific work when the stopped state changes to continue. For example, redisplay the prompt

SIGSTOP Stops the execution of the process. Notice the difference between this and terminate and interrupt: the process is not finished; it is just paused. This signal cannot be blocked, processed, or ignored.

20) SIGTSTP stops the process, but the signal can be processed and ignored. This signal is emitted when the user types SUSP characters (usually Ctrl-Z)

21) SIGTTIN When a background job reads data from a user terminal, all processes in the job receive a SIGTTIN signal. These processes are stopped by default.

22) SIGTTOU is similar to SIGTTIN, but is received when writing a terminal (or modifying terminal mode).

23) Generated when SIGURG has “urgent” data or out-of-band data arriving on the socket.

24) THE SIGXCPU exceeds the CPU time resource limit. This limit can be read/changed by getrLimit/setrLimit.

25) SIGXFSZ when the process attempts to expand the file to exceed the file size resource limit.

26) SIGVTALRM Virtual clock signal. Similar to SIGALRM, but counting the CPU time consumed by the process.

27) SIGPROF is similar to SIGALRM/SIGVTALRM, but includes the CPU time used by the process and the system call time.

28) SIGWINCH is emitted when the window size changes.

29) The SIGIO file descriptor is ready to start input/output operations.

30) SIGPWR Power failure

31) Invalid system call by SIGSYS.

Key Points Among the signals listed above, the program cannot capture, block, or ignore the following signals: SIGKILL; SIGSTOP; SIGILL; SIGTRAP; SIGABRT SIGBUS, SIGFPE, SIGILL, SIGIOT, SIGQUIT, SIGSEGV, SIGTRAP, SIGXCPU, SIGXFSZ default process leads to exit signals are: SIGALRM SIGHUP, SIGINT, SIGKILL, SIGPIPE, SIGPOLL, SIGPROF, SIGSYS, SIGTERM, SIGUSR1, SIGUSR2, SIGVTALRM default process can lead to stop signals are: SIGSTOP, SIGTSTP, SIGTTIN, ignoring SIGTTOU default process signals are: SIGCHLD, SIGPWR, SIGURG, SIGWINCH in addition, in an SVR4 SIGIO is quit, in 4.3 BSD is ignored; SIGCONT continues when the process is suspended, otherwise ignored, and cannot be blocked. [Specific reasons for why, I do not know 🙃, ask wechat god to]

1. Appdelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point forcustomization after application launch. InstallSignalHandler(); / / semaphore truncation InstallUncaughtExceptionHandler (); // System exception capturereturn YES;
}
Copy the code

2. SignalHandler. M

void SignalExceptionHandler(int signal)
{
    NSMutableString *mstr = [[NSMutableString alloc] init];
    [mstr appendString:@"Stack: "];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i <frames; ++i) {
        [mstr appendFormat:@"%s ", strs[i]];
    }
    [SignalHandler saveCreash:mstr];
 
}

void InstallSignalHandler(void)
{
    signal(SIGHUP, SignalExceptionHandler);
    signal(SIGINT, SignalExceptionHandler);
    signal(SIGQUIT, SignalExceptionHandler);
    
    signal(SIGABRT, SignalExceptionHandler);
    signal(SIGILL, SignalExceptionHandler);
    signal(SIGSEGV, SignalExceptionHandler);
    signal(SIGFPE, SignalExceptionHandler);
    signal(SIGBUS, SignalExceptionHandler);
    signal(SIGPIPE, SignalExceptionHandler);
}

Copy the code

3. UncaughtExceptionHandler. M

Void HandleException(NSException *exception) {NSArray *stackArray = [exception callStackSymbols]; NSString *reason = [exception reason]; NSString *name = [exception name]; NSString *exceptionInfo = [NSString stringWithFormat:@"Exception Reason: % @exception name: % @exception stack: %@",name, reason, stackArray];
    NSLog(@"% @", exceptionInfo);
    [UncaughtExceptionHandler saveCreash:exceptionInfo];
}
 
void InstallUncaughtExceptionHandler(void)
{
    NSSetUncaughtExceptionHandler(&HandleException);
}
Copy the code

4, Test the most critical step here, SignalHandler does not test in debug environment. Because system debug will intercept first. After we run it once, we turn off the debug state. We should directly click on the app we built on the simulator to run it. UncaughtExceptionHandler can be caught in the debug state

- (void)buttonClick:(UIButton *)sender { //1. Semaphore Test *pTest = {1,2}; free(pTest); PTest ->a = 5; pTest->a = 5; } - (IBAction)buttonOCException:(UIButton *)sender {//2"tom"The @"xxx"The @"ooo"];
    [array objectAtIndex:5];
}
Copy the code

Attach demo: [demo download address]