我是靠谱客的博主 诚心悟空,这篇文章主要介绍利用Linux信号机制监控Android App ANRANR机制怎么监控SIGQUIT信号,现在分享给大家,希望可以做个参考。

ANR机制

ANR发生之后,AMS(位于system_server进程) 会给APP进程发送SIGQUIT信号,APP的Signal Catcher线程在捕获这个信号后打印本进程的调用栈。也就是说,ANR发生一定伴随着SIGQUIT信号的产生,那么我们监控此信号是不是可以监控ANR了吗?

怎么监控SIGQUIT信号

APP进程中有一个Signal Catcher线程会监听SIGQUIT信号,接收到信号后会执行dump anr trace等操作,这就是发生ANR时我们看到的一堆跟踪信息。
但是一次信号只能被一个线程消费,消费了就没了,所以问题的关键在于如何提前截胡,把信号捕获到!

先看看Android 是怎么监听的

源码可以查看:http://androidxref.com/9.0.0_r3/xref/art/runtime/runtime.cc#930

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Runtime::init(){ .... BlockSignals(); .... } // 主线程忽略SIGPIPE,SIGQUIT,SIGUSR1信号 void Runtime::BlockSignals() { SignalSet signals; signals.Add(SIGPIPE); // SIGQUIT is used to dump the runtime's state (including stack traces). signals.Add(SIGQUIT); // SIGUSR1 is used to initiate a GC. signals.Add(SIGUSR1); signals.Block(); }

源码地址: http://androidxref.com/9.0.0_r3/xref/art/runtime/signal_catcher.cc#234

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void* SignalCatcher::Run(void* arg) { SignalCatcher* signal_catcher = reinterpret_cast<SignalCatcher*>(arg); CHECK(signal_catcher != nullptr); Runtime* runtime = Runtime::Current(); CHECK(runtime->AttachCurrentThread("Signal Catcher", true, runtime->GetSystemThreadGroup(), !runtime->IsAotCompiler())); Thread* self = Thread::Current(); DCHECK_NE(self->GetState(), kRunnable); { MutexLock mu(self, signal_catcher->lock_); signal_catcher->thread_ = self; signal_catcher->cond_.Broadcast(self); } // Set up mask with signals we want to handle. SignalSet signals; signals.Add(SIGQUIT); //监听SIGQUIT信号 signals.Add(SIGUSR1); while (true) { int signal_number = signal_catcher->WaitForSignal(self, signals); //监听 if (signal_catcher->ShouldHalt()) { runtime->DetachCurrentThread(); return nullptr; } switch (signal_number) { case SIGQUIT: signal_catcher->HandleSigQuit(); break; case SIGUSR1: signal_catcher->HandleSigUsr1(); break; default: LOG(ERROR) << "Unexpected signal %d" << signal_number; break; } } }

总的来说就是: 主线程忽略SIGQUIT信号,由于子线程都是基于主线程fork出来的,如果未作特殊处理,也和主线程一样忽略了SIGQUIT信号;
然后Android专门用了一个SignalCatcher 线程来监听SIGQUIT信号,这样就保证了只有这个线程接收到SIGQUIT信号,也就能够处理ANR了事件了。

注册我们的信号处理器

仿照Android,我们新建一个线程,并且监听SIGQUIT信号,是不是就可以大功告成了呢?
直觉告诉我没那么容易,不然Android为啥还要特意让SignalCatcher线程外的所有线程忽略sigquit信号呢?

事实上,linux kernel 对于发给进程(线程组)信号的处理原则是,根据curr_target来确定信号的处理线程,而curr_target 并不是固定不变的,它是最新一次处理信号的线程。
那么我们怎么做才能让curr_target变为我们的线程呢?

源码地址:https://elixir.bootlin.com/linux/latest/source/kernel/signal.c#L997

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static void complete_signal(int sig, struct task_struct *p, enum pid_type type) { struct signal_struct *signal = p->signal; struct task_struct *t; /* * Now find a thread we can wake up to take the signal off the queue. * * If the main thread wants the signal, it gets first crack. * Probably the least surprising to the average bear. */ if (wants_signal(sig, p)) t = p; else if ((type == PIDTYPE_PID) || thread_group_empty(p)) /* * There is just one thread and it does not need to be woken. * It will dequeue unblocked signals before it runs again. */ return; else { /* * Otherwise try to find a suitable thread. */ t = signal->curr_target; while (!wants_signal(sig, t)) { t = next_thread(t); if (t == signal->curr_target) /* * No thread needs to be woken. * Any eligible threads will see * the signal in the queue soon. */ return; } signal->curr_target = t; // 更新curr_target } /* * Found a killable thread. If the signal will be fatal, * then start taking the whole group down immediately. */ if (sig_fatal(p, sig) && (signal->core_state || !(signal->flags & SIGNAL_GROUP_EXIT)) && !sigismember(&t->real_blocked, sig) && (sig == SIGKILL || !p->ptrace)) { /* * This signal will be fatal to the whole group. */ if (!sig_kernel_coredump(sig)) { /* * Start a group exit and wake everybody up. * This way we don't have other threads * running and doing things after a slower * thread has the fatal signal pending. */ signal->flags = SIGNAL_GROUP_EXIT; signal->group_exit_code = sig; signal->group_stop_count = 0; t = p; do { task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); sigaddset(&t->pending.signal, SIGKILL); signal_wake_up(t, 1); } while_each_thread(p, t); return; } } /* * The signal is already in the shared-pending queue. * Tell the chosen thread to wake up and dequeue it. */ signal_wake_up(t, sig == SIGKILL); return; }

监听思路

我们能否发一个信号,且只能被我们的线程捕获到,那么这样之后,curr_target 就会变为我们的线程了,以后的SIGQUIT信号就会提前被我们的线程截胡。

事实上,从上一节我们看到,App进程的所有线程都忽略了SIGPIPE信号,那么我们是否可以利用这个信号呢?完全可以。我们可以给进程发送一个SIGPIPE信号,这样kernel遍历了一遍之后发现只有我们的线程能够处理,就将curr_target 变为我们的线程了。
等等,那么我们把SIGQUIT信号截胡了,那么系统的ANR怎么办?别慌,简单,我们只需要在我们的线程通过tgkill 给Signal Catcher线程定向发送一个SIGQUI信号就行了,它不关心谁发的信号,只要能收到就行。

至此,通过Linux信号机制监听Android 的SIGQUIT信号就结束了,但是回到最开始的问题,有了SIGQUIT 信号就代表APP发生了ANR吗?
显然不是,一个应用发生ANR后,system_server进程会向此APP进程以及cpu占用最高的几个进程都发送SIGQUI信号,所以我们收到SIGQUIT信号,有可能是其他APP ANR了。
那么我们需要怎么才能筛选出真正的ANR呢? 且关注下一篇

最后

以上就是诚心悟空最近收集整理的关于利用Linux信号机制监控Android App ANRANR机制怎么监控SIGQUIT信号的全部内容,更多相关利用Linux信号机制监控Android内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部