前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ANR问题分析总结

ANR问题分析总结

作者头像
用户7557625
发布2020-07-15 10:34:11
2.5K0
发布2020-07-15 10:34:11
举报
文章被收录于专栏:程序猿的那点事

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

代码语言:javascript
复制
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文件。

代码语言:javascript
复制
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、时间和进程名称。可以看到是服务器超时。

代码语言:javascript
复制
----- 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的线程也正常。

代码语言:javascript
复制
"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可能发生在这,然后分析详细内容。

代码语言:javascript
复制
"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函数。

代码语言:javascript
复制
        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状态机没有处理这个消息。

代码语言:javascript
复制
        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这个线程的相关情况。

代码语言:javascript
复制
"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函数。

代码语言:javascript
复制
                    mWifiDiagnostics.captureBugReportData(
然后跳转到captureBugReportData函数,
    public synchronized void captureBugReportData(int reason) {
        BugReport report = captureBugreport(reason, isVerboseLoggingEnabled());
        mLastBugReports.addLast(report);
    }

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

代码语言:javascript
复制
        report.logcatLines = getLogcat(127);

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

代码语言:javascript
复制
    public String readLine() throws IOException {
        return readLine(false);
    }

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/08/21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档