ANR问题分析总结

前几天遇到了一共个ANR问题,线程阻塞问题,下面分享一下分析解决思路。
从log中可以看出是System_server超时ANR问题,在一个循环中等待。

M501F5C 06-09 07:15:12.013  8619  8624 I system_server: Thread[2,tid=8624,WaitingInMainSignalCatcherLoop,Thread*=0x7111c0e000,peer=0x15800138,"Signal Catcher"]: reacting to signal 3

根据下面的log可以定位到pid为8619的trace文件。

M501C42 06-09 07:14:39.392   514   514 I /system/bin/tombstoned: registered intercept for pid 8619 and type kDebuggerdJavaBacktrace

然后打开traces文件,定位到Cmd line,说明了发生ANR的进程id、时间和进程名称。可以看到是服务器超时。

----- pid 8619 at 2019-06-09 07:15:12 -----
Cmd line: system_server

以"main" prio=5 tid=1 Native为例,“main”为线程名字,prio为线程优先级,tid为线程。
因为ANR发生在主线程中,所以tid=1,看主线程。
“main” prio=5 tid=1 Native 主线程状态为native,是正常状态,
“Signal Catcher” daemon prio=5 tid=2 Runnable,tid为2的线程也正常。

"ReferenceQueueDaemon" daemon prio=5 tid=3 Waiting
"FinalizerDaemon" daemon prio=5 tid=4 Waiting
"FinalizerWatchdogDaemon" daemon prio=5 tid=5 Waiting
"HeapTaskDaemon" daemon prio=5 tid=6 WaitingForTaskProcessor

上面四个线程都标识了daemon,为守护线程,不需要关心。

然后看tid=7,Binder线程是进程中用来处理binder请求的线程,状态为waiting,说明请求没有处理完成,还在等待,说明ANR可能发生在这,然后分析详细内容。

"Binder:8619_1" prio=5 tid=7 Waiting
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x158003e0 self=0x711004bc00
  | sysTid=8629 nice=-8 cgrp=default sched=0/0 handle=0x710094d4f0
  | state=S schedstat=( 511295433916 147414081844 1240574 ) utm=36961 stm=14168 core=0 HZ=100
  | stack=0x7100852000-0x7100854000 stackSize=1009KB
  | held mutexes=
  at java.lang.Object.wait(Native method)
  - waiting on <0x08ac2c55> (a java.lang.Object)
com.android.internal.util.AsyncChannel$SyncMessenger.sendMessageSynchronously(AsyncChannel.java:825)  - locked <0x08ac2c55> (a java.lang.Object)
com.android.internal.util.AsyncChannel$SyncMessenger.access$100(AsyncChannel.java:739)
com.android.internal.util.AsyncChannel.sendMessageSynchronously(AsyncChannel.java:653)
com.android.server.wifi.util.WifiAsyncChannel.sendMessageSynchronously(WifiAsyncChannel.java:92)
com.android.internal.util.AsyncChannel.sendMessageSynchronously(AsyncChannel.java:681)
com.android.server.wifi.WifiStateMachine.syncGetConfiguredNetworks(WifiStateMachine.java:1671)
com.android.server.wifi.WifiServiceImpl.getConfiguredNetworks(WifiServiceImpl.java:1808)
at android.net.wifi.IWifiManager$Stub.onTransact(IWifiManager.java:89)
at android.os.Binder.execTransact(Binder.java:756)

倒数第二行可以看到,binder在处理IWifiManager的请求,IWifiManager是一个接口,由WIfiServiceImpl实现这个接口,再往上看,可以看到调用了WifiServiceImpl的getConfiguredNetworks函数。我们继续看这个函数的具体实现,发现它调用了wifistatemachine的syncGetConfiguredNetworks函数。

        if (mWifiStateMachineChannel != null) {
            List<WifiConfiguration> configs = mWifiStateMachine.syncGetConfiguredNetworks(
                    Binder.getCallingUid(), mWifiStateMachineChannel);

然后我们看wifistatemachine的syncGetConfiguredNetworks函数。
    public List<WifiConfiguration> syncGetConfiguredNetworks(int uuid, AsyncChannel channel) {
        Message resultMsg = channel.sendMessageSynchronously(CMD_GET_CONFIGURED_NETWORKS, uuid);
        if (resultMsg == null) { // an error has occurred
            return null;
        } else {
            List<WifiConfiguration> result = (List<WifiConfiguration>) resultMsg.obj;
            resultMsg.recycle();
            return result;

可以看出这个函数主要是利用AsyncChannel发送消息,得到消息处理的结果。
根据tid=7里面的函数调用栈可以看出,最后waiting发生在AsyncChannel.java中的sendMessageSynchronously函数,没有得到消息处理的结果,Wifi状态机没有处理这个消息。

        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
            SyncMessenger sm = SyncMessenger.obtain();
            Message resultMsg = null;
            try {
                if (dstMessenger != null && msg != null) {
                   msg.replyTo = sm.mMessenger;
                   synchronized (sm.mHandler.mLockObject) {
                       if (sm.mHandler.mResultMsg != null) {
                            Slog.wtf(TAG, "mResultMsg should be null here");
                            sm.mHandler.mResultMsg = null;
                        }
                        dstMessenger.send(msg);
                        sm.mHandler.mLockObject.wait();
                        resultMsg = sm.mHandler.mResultMsg;
                        sm.mHandler.mResultMsg = null;
                   }
                }

Wifi状态机为什么没有处理消息,下面具体分析WifiStateMachine这个线程的相关情况。

"WifiStateMachine" prio=5 tid=57 Native
  at java.io.BufferedReader.readLine(BufferedReader.java:335)
  - locked <0x03d038d1> (a java.io.InputStreamReader)
  at java.io.BufferedReader.readLine(BufferedReader.java:400)
  at com.android.server.wifi.WifiDiagnostics.getLogcat(WifiDiagnostics.java:604)
com.android.server.wifi.WifiDiagnostics.captureBugreport(WifiDiagnostics.java:537)
com.android.server.wifi.WifiDiagnostics.captureBugReportData(WifiDiagnostics.java:215) - locked <0x039e0f36> (a com.android.server.wifi.WifiDiagnostics)
atcom.android.server.wifi.WifiStateMachine$ConnectModeState.processMessage(WifiStateMachine.java:4186)
com.android.internal.util.StateMachine$SmHandler.processMsg(StateMachine.java:992)
  atcom.android.internal.util.StateMachine$SmHandler.handleMessage(StateMachine.java:809)
  at android.os.Handler.dispatchMessage(Handler.java:106)
  at android.os.Looper.loop(Looper.java:192)
  at android.os.HandlerThread.run(HandlerThread.java:65)

因为WifiStateMachine继承了StatMachine,所以我们直接看WifiStateMachine.java,直接定位到WifiStateMachine.java:4186,再往上走,根据(WifiDiagnostics.java:537),可以判断是下面这一句出现问题。根据函数调用关系可以看出走到了captureBugreport函数。

                    mWifiDiagnostics.captureBugReportData(
然后跳转到captureBugReportData函数,
    public synchronized void captureBugReportData(int reason) {
        BugReport report = captureBugreport(reason, isVerboseLoggingEnabled());
        mLastBugReports.addLast(report);
    }

然后在captureBugreport函数中,走到了getLogcat函数。

        report.logcatLines = getLogcat(127);

getLogcat函数中调用了BufferedReader中的readLine函数,因为readLine函数是一个阻塞函数,读取数据时,会把数据存到一个缓存区域,读取到\n或\r时,返回这一行数据,如果一直没有遇到\n或\r,就会一直阻塞在缓存区域,也不会返回null。这样WifiStateMachine这个线程就在这阻塞,导致WIfiStateMachine不能去处理之前的消息,最终导致ANR问题。

    public String readLine() throws IOException {
        return readLine(false);
    }

至于readLine函数为什么会阻塞,可以看本人的另一篇博客。
https://blog.csdn.net/qq_43804080/article/details/93378518

关注公众号,获取更多开发必备知识
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值