Author: Fei Tai

background

The Application Not Responding frequency of Android is always an important indicator of Android user experience. However, on Android 6.0+ devices, due to the convergence of device ANR directory permissions, It is no longer possible to get anR files by scanning /data/anr/ testamp.txt, so today we will briefly talk about another way to get ANR, SIGQUIT(3) intercepting on Android.

Semaphore processing

For SIGQUIT interception, we need to understand some semaphore processing related functions. Kill, signal, SIGaction, SIGwait, pthread_SIGmask and other system semaphore processing related functions are necessary knowledge for reading this article. Therefore, in this chapter, a brief introduction is given. For more information on system functions, see Advanced Programming in UNIX Environments.

kill [1]

Header file: #include

Define function: int kill(pid_t pid,int signo)

SendSignal (PID) is used to communicate with Android AMS when ANR occurs. Process.sendSignal(PID,signal) is used to communicate with Android AMS when ANR occurs

For those of you who want to know more about ANR, check it out

  • AppErrors.java:androidxref.com/9.0.0_r3/xr…
  • Android_util_Process.cpp:androidxref.com/9.0.0_r3/xr…

signal [2]

Header file: #include

Sig_t signal(int signum, sig_T handler);

Signal () is used to determine the processing method when the signal sig appears later. If the value of handler is SIG_DFL, the implementation-defined default behavior is used; If the value of handler is SIG_IGN, the signal is ignored; Otherwise, the function pointed to by handler is called (with the argument of signal type). Valid signals include:

SIGABRT Abort, as in the call abort().
SIGFPE Arithmetic operation error, such as divisor 0 or overflow.
SIGILL An image of an invalid function, such as an invalid instruction.
SIGINT Interactive signals, such as interrupts.
SIGSEGV Illegal access to memory, as to a non-existent memory location.
SIGTERM Termination request signal sent to the program.

Signal () returns the original handler of the signal sig; If there is an error, SIG_ERR is returned. When a signal SIG is subsequently present, the operation being performed is interrupted and the signal processing function (* Handler)(SIG) is executed instead. If returned from a signal handler, execution continues from where it was interrupted.

sigaction [3]

Header file: #include

Sigaction (int signum,const struct sigaction *act,struct sigaction *oldact)

Sigaction will set the signal processing function according to the signal number specified by parameter signum. The signum argument can specify all signals except SIGKILL and SIGSTOP. As the parameter structure sigaction is defined as follows:

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};
Copy the code

Code 1 sigAction structure

Signal handlers can use void (*sa_handler)(int) or void (*sa_sigaction)(int, siginfo_t *, void *). Void (* sa_SIGaction)(int, siginfo_t *, void *) is used to send additional information to the handler. By default, void (*sa_handler)(int) is used, and only the numeric value of the signal can be sent to the handler.

  • Sa_handler: This parameter is the same as the handler parameter of signal() and represents the new signal processing function. For other meanings, see signal().
  • Sa_mask: used to set the signal set specified by sa_mask to be shelved temporarily while the signal is processed;
  • Sa_restorer: This parameter is not used;
  • Sa_flags: Used to set other operations related to signal processing. The following values are available. Sa_flags can also set other flags:
  • SA_RESETHAND: Reset the signal handler function to the default value SIG_DFL when it is called
  • SA_RESTART: If the signal interrupts a system call of the process, the system automatically starts the system call
  • SA_NODEFER: Normally, the kernel will block this given signal while the signal handler is running. However, if the SA_NODEFER flag is set, the kernel will not block the signal while the signal handler is running.

sigwait [4]

Header file: #include

Int sigwait(const sigset_t *set, int *sig);

Function description: SIGwait provides a mechanism to wait for the arrival of signals and take signals out of the signal queue for processing in a serial manner. Sigwait waits only for the signal set specified in the function argument, that is, if the newly generated signal is not in the specified signal set, sigwait continues to wait. We generally have some questions about a stable and reliable program:

  1. Do not block SIGSTOP and SIGKILL signals that cannot be ignored in the signal mask of the thread;
  2. Do not block SIGFPE, SIGILL, SIGSEGV, SIGBUS in the signal mask of the thread;
  3. Ensure that sigWait is waiting for a signal set that is blocked by all threads in the process;
  4. When the main thread or other worker thread generates a signal, you must call kill() to send the signal to the entire process, instead of using pthread_kill() to send a specific worker thread, or the signal processing thread will not receive the signal.
  5. Because SIGwait uses a serial way to process the arrival of signals, in order to avoid the delay in signal processing or the loss of non-real-time signals, the code for processing each signal should be as simple and fast as possible, and avoid calling library functions that may cause blocking.

Note: The Android Signal Catcher thread uses sigWait to wait for SIGQUIT signals.

pthread_sigmask [5]

Header file: #include

Int pthread_sigmask (int how,const sigset_t *set,sigset_t *oset);

Function description: Each thread has its own signal masking set (signal mask), you can use the pthread_sigmask function to mask a thread for some signal response processing, leaving only the thread that needs to process the signal to process the specified signal. This is done by using the inheritance relationship of the thread signal mask set (after setting sigmask in the main thread, the threads created by the main thread will inherit the mask of the main thread).

Difference between Signal, SIGAction, sigWAIT

Signal, SIGAction, and SIGwait all receive signals and process them. What are the differences, and in what order?

The difference between SIGAction and Signal Signal is a subset of sigAction. Sigaction is a subset of sigAction. Sigaction is a subset of sigAction. Both signal and SIGAction end up calling the system call RT_SIGaction.

The difference between SIGAction and SIGWAIT is that if multiple threads are waiting for the same signal in a SIGwait call, only one thread can return from sigWAIT when the signal is delivered, and the specific thread is undefined (at the system’s decision). If the signal is caught (the process has set up a signal handler by using SIGAction) and the thread is waiting for the same signal in a SIGWait call, then it is up to the operating system implementation to decide how to deliver the signal. In this case, the operating system implementation can either make SIGWAIT return or activate the signal handler, but it is not possible to do both.

We did an experiment to execute sigWAIT if the semaphore is sent to the target thread and the target now has sigWAIT; If the semaphore is sent to the target thread, and the target thread sets SIG_BLOCK (masking information), other threads are in SIGWAIT, then sigWAIT is executed; Other states perform sigaction behavior – this was only tested on the author’s MAC.

The advantage of using SIGwait is that it simplifies signal processing, allowing asynchronously generated signals to be processed synchronously. To prevent the signal from interrupting the thread, the signal can be added to the signal-masking word of each thread and then assigned to a dedicated thread for signal processing. These dedicated threads can make function calls without having to worry about which functions are safe to call in a signal handler, because the function calls come from a normal threading environment, rather than traditional signal handlers, which typically interrupt the thread’s normal execution. See Libev source Code Analysis 06 [7].

Android system ANR SIGQUIT(3) processing

How does Android ANR signal work? To put it simply: When the target process is created, a special “Signal Catcher” thread is launched to process the semaphore. The AMS pop-up dialog is followed by a system call that issues the SIGQUIT(3) semaphore. The “Signal Catcher” thread is dedicated to processing the SIGQUIT(3) semaphore. Dump the thread state of the target process to /data/anr/ testamp.txt.

SignalCatcher thread created

When Android is running an application, if the application process has not been created, ActivityManagerService requests Zygote fork (see Android Process Creation [8]) and eventually creates the SignalCatcher thread through the Runtime.

1550 // Look for a native bridge. 1551 // 1552 // The intended flow here is, in the case of a running system: 1553 // 1554 // Runtime::Init() (zygote): 1555 // LoadNativeBridge -> dlopen from cmd line parameter. 1556 // | 1557 // V 1558 // Runtime::Start() (zygote): 1559 // No-op wrt native bridge. 1560 // | 1561 // | start app 1562 // V 1563 // DidForkFromZygote(action) 1564 // action = kUnload -> dlclose native bridge. 1565 // action = kInitialize -> initialize library 1566 // bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { 1110 // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc. 1111 // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc. 1112 env_snapshot_.TakeSnapshot(); . // This line of code is very important. The reason why SIGQUIT semaphore is not available will be explained later. 1359 BlockSignals(); 1360 InitPlatformSignalHandlers(); 1361... 1408 std::string error_msg; 1409 java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg); 1410 if (java_vm_.get() == nullptr) { 1411 LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg; 1412 return false; 1413}... 1623 return true; 1624}Copy the code

Code 2 Runtime::Init method

1905 void Runtime::BlockSignals() { 1906 SignalSet signals; 1907 signals.Add(SIGPIPE); 1908 // SIGQUIT is used to dump the runtime's state (including stack traces). 1909 signals.Add(SIGQUIT); 1910 // SIGUSR1 is used to initiate a GC. 1911 signals.Add(SIGUSR1); // add SIGPIPE, SIGQUIT, SIGUSER1 to the target signal set, and call pthread_sigmask again. See attachment 1912 messs.block (); 1913}Copy the code

Runtime::BlockSignals method

SIGPIPE, SIGQUIT, SIGUSER1; signal_set.h; signal_set.h; SIGPIPE, SIGQUIT, and SIGUSER1 semaphores sent by the system are not handled by the current thread (main thread), but by other threads.

When Zogyte forks the child process, the child process inherits the signal set of the parent process. Therefore, the main thread created by the child process and the child thread created by the main process inherit the signal set. As a result, the main thread and its child thread of the fork process will not process SIGPIPE, SIGQUIT, SIGUSER1 signals. This can only be done by sigwait.

SignalCatcher principle

[->signal_catcher.cc] void* SignalCatcher::Run(void* arg) { ... SignalSet signals; SignalSet signals; SignalSet signals; Add(SIGQUIT); // Add SIGQUIT and SIGUSER1 to the target signal set. signals.Add(SIGUSR1); While (true) {// Wait for SIGQUIT, SIGUSR1, sigWAIT Int signal_number = signal_catcher->WaitForSignal(self, signals); if (signal_catcher->ShouldHalt()) { runtime->DetachCurrentThread(); return nullptr; Case SIGQUIT: signal_catcher->HandleSigQuit(); break; case SIGUSR1: signal_catcher->HandleSigUsr1(); break; default: LOG(ERROR) << "Unexpected signal %d" << signal_number; break; }}}Copy the code

Code 4 SignalCatcher::Run method

SignalCatcher::Run: SignalCatcher::Run: SignalCatcher::Run: SignalCatcher: SignalCatcher::Run The Android system is designed in this way to ensure that the processing of SIGQUIT and SIGUSR1 must be completed by the “SignalCatcher” thread. I think there are two advantages:

  1. Prevent semaphore processing by other threads to ensure the correctness of ANR file contents;
  2. By a separate thread processing, can prevent stack damage to other threads, to ensure the integrity of the thread stack.

Android custom SIG_QUIT interception implementation

Void RunSigQuitMonitor() {sigset_t set, old_set; sigemptyset(&set); sigaddset(&set, SIGQUIT); /* * We need to call SIG_UNBLOCK because when the target process is forked by Zogyte, the main thread inherits the mask of Zogyte's main thread. When Zogyte's main thread is initialized, Pthread_sigmask SIG_UNBLOCK = SIGQUIT SIG_UNBLOCK = pthread_sigmask SIG_UNBLOCK This will invalidate SignalCatcher Sigwait, */ int r = pthread_SIGmask (SIG_UNBLOCK, &set, &old_set); if (0 ! = r) return; . . struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGQUIT); sa.sa_sigaction = SignalHandler; sa.sa_flags = SA_ONSTACK | SA_SIGINFO; if (sigaction(SIGQUIT, &sa, &old_handler) ! = 0) { goto Failed; } return; Pthread_sigmask (SIG_SETMASK, &old_set, NULL); }Copy the code

Code 5 SIG_QUIT intercept custom SigPad: : RunSigQuitMonitor method

Code 5 SigPad: : RunSigQuitMonitor method, with Linux signal pre knowledge as well as to the Android “SignalCatcher” initialization, We can unmask SIG_UNBLOCK from the main thread of the current process by setting SIG_UNBLOCK to pthread_sigmask. Then the SIGQUIT semaphore processing method is redirected by sa_SIGAction to achieve its own ANR monitoring method.

summary

Above, with the understanding of Android SIGQUIT signal processing, we can quickly implement Android custom SIGQUIT signal interceptor, also welcome the majority of readers to leave a message.

reference

[1] baike.baidu.com/item/kill ()…

[2] baike.baidu.com/item/signal…

[3] baike.baidu.com/item/sigact…

[4] baike.baidu.com/item/sigwai…

[5] baike.baidu.com/item/pthrea…

[6] source analysis of the difference between signal and sigaction: blog.csdn.net/wangzuxi/ar…

[7] Libev 06:www.cnblogs.com/gqtcgq/p/72 source analysis…

[8] the Android process creation process: www.jianshu.com/p/b4cb8608d…

Follow us every week for 3 mobile technology practices & dry goods for you to think about!