转载自: http://blog.csdn.net/oujunli/article/details/9102101#reply

对于从事Android开发的人来说,遇到ANR(Application Not Responding)是比较常见的问题。一般情况下,如果有ANR发生,系统都会在/data/anr/目录下生成trace文件,通过分析trace文件,可以定位产生ANR的原因。产生ANR的原因有很多,比如CPU使用过高、事件没有得到及时的响应、死锁等,下面将通过一次因为死锁导致的ANR问题,来说明如何通过trace文件分析ANR问题。

对应的部分trace文件内容如下:

"PowerManagerService" prio=5 tid=24 MONITOR
  | group="main" sCount=1 dsCount=0 obj=0x41dd0eb0 self=0x5241b218
  | sysTid=567 nice=0 sched=0/0 cgrp=apps handle=1380038664
  | state=S schedstat=( 6682116007 11324451214 33313 ) utm=450 stm=219 core=1
  at com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:~13045)
  - waiting to lock <0x41a874a0> (a com.android.server.am.ActivityManagerService) held by tid=12 (android.server.ServerThread)
  at android.app.ContextImpl.sendBroadcast(ContextImpl.java:1144)
  at com.android.server.power.PowerManagerService$DisplayBlankerImpl.unblankAllDisplays(PowerManagerService.java:3442)
  at com.android.server.power.DisplayPowerState$PhotonicModulator$1.run(DisplayPowerState.java:456)
  at android.os.Handler.handleCallback(Handler.java:800)
  at android.os.Handler.dispatchMessage(Handler.java:100)
  at android.os.Looper.loop(Looper.java:194)
  at android.os.HandlerThread.run(HandlerThread.java:60)
  
  "Binder_B" prio=5 tid=85 MONITOR
  | group="main" sCount=1 dsCount=0 obj=0x42744770 self=0x58329e88
  | sysTid=3700 nice=-20 sched=0/0 cgrp=apps handle=1471424616
  | state=S schedstat=( 1663727513 2044643318 6806 ) utm=132 stm=34 core=1
  at com.android.server.power.PowerManagerService$DisplayBlankerImpl.toString(PowerManagerService.java:~3449)
  - waiting to lock <0x41a7e420> (a com.android.server.power.PowerManagerService$DisplayBlankerImpl) held by tid=24 (PowerManagerService)
  at java.lang.StringBuilder.append(StringBuilder.java:202)
  at com.android.server.power.PowerManagerService.dump(PowerManagerService.java:3052)
  at android.os.Binder.dump(Binder.java:264)
  at android.os.Binder.onTransact(Binder.java:236)
  at android.os.IPowerManager$Stub.onTransact(IPowerManager.java:373)
  at android.os.Binder.execTransact(Binder.java:351)
  at dalvik.system.NativeStart.run(Native Method)
  
"android.server.ServerThread" prio=5 tid=12 MONITOR
  | group="main" sCount=1 dsCount=0 obj=0x41a76178 self=0x507837a8
  | sysTid=545 nice=-2 sched=0/0 cgrp=apps handle=1349936616
  | state=S schedstat=( 15368096286 21707846934 69485 ) utm=1226 stm=310 core=0
  at com.android.server.power.PowerManagerService.isScreenOnInternal(PowerManagerService.java:~2529)
  - waiting to lock <0x41a7e2e8> (a java.lang.Object) held by tid=85 (Binder_B)
  at com.android.server.power.PowerManagerService.isScreenOn(PowerManagerService.java:2522)
  at com.android.server.wm.WindowManagerService.sendScreenStatusToClientsLocked(WindowManagerService.java:7749)
  at com.android.server.wm.WindowManagerService.setEventDispatching(WindowManagerService.java:7628)
  at com.android.server.am.ActivityManagerService.updateEventDispatchingLocked(ActivityManagerService.java:8083)
  at com.android.server.am.ActivityManagerService.wakingUp(ActivityManagerService.java:8077)
  at com.android.server.power.Notifier.sendWakeUpBroadcast(Notifier.java:474)
  at com.android.server.power.Notifier.sendNextBroadcast(Notifier.java:455)
  at com.android.server.power.Notifier.access$700(Notifier.java:62)
  at com.android.server.power.Notifier$NotifierHandler.handleMessage(Notifier.java:600)
  at android.os.Handler.dispatchMessage(Handler.java:107)
  at android.os.Looper.loop(Looper.java:194)
  at com.android.server.ServerThread.run(SystemServer.java:1328)

从trace文件看,是因为TID为24的线程等待一个TID为12的线程持有的锁,TID为12的线程等待一个TID为85的线程持有的锁,而TID为85的线程确等待一个TID为24的线程持有的锁,导致了循环等待的现象,对应的trace文件的语句如下:

TID 24:- waiting to lock <0x41a874a0> (a com.android.server.am.ActivityManagerService) held by tid=12 (android.server.ServerThread)

TID 12: - waiting to lock <0x41a7e2e8> (a java.lang.Object) held by tid=85 (Binder_B)

TID 85:- waiting to lock <0x41a7e420> (a com.android.server.power.PowerManagerService$DisplayBlankerImpl) held by tid=24 (PowerManagerService)

既然是死锁,那么先看各线程都有那些锁。

先看TID=24的线程的栈顶,ActivityManagerService的broadcastIntent函数代码如下:

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String requiredPermission, boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized(this) {
            intent = verifyBroadcastLocked(intent);
            
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo,
                    resultCode, resultData, map, requiredPermission, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }

可以看到TID=24需要ActivityManagerService这个锁。再看TID=12线程的栈顶,PowerManagerService的isScreenOnInternal函数代码如下:

private boolean isScreenOnInternal() {
        synchronized (mLock) {
            return !mSystemReady
                    || mDisplayPowerRequest.screenState != DisplayPowerRequest.SCREEN_STATE_OFF;
           }

}

可以看到需要PowerManagerService的mlock这个锁。最后看TID=85线程的栈顶,同样在PowerManagerService里面,内部类DisplayBlankerImpl的toString函数:

public String toString() {
            synchronized (this) {
                return "blanked=" + mBlanked;
            }
        }

这是在内部类DisplayBlankerImpl里面实现的,所以需要DisplayBlankerImpl这个锁。

对应的表格如下:

表一 各线程等待的锁情况

从表一来看,没有出现死锁现象,似乎并不是我们所想的那样。难道不是死锁?开始有点小怀疑自己了,难道别的原因导致的。也许只看调用堆栈的顶端可能不行,栈顶只能看出各线程需要的锁,不能仅看自己要什么吧!一味索取可不好!人不是这样做的!看一下整个的堆栈调用流程,看看自己拥有了那些锁。

跟踪TID=24线程的堆栈,在PowerManagerService内部类DisplayBlankerImpl的unblankAllDisplays函数中持有锁:

public void unblankAllDisplays() {
            synchronized (this) {
                nativeSetAutoSuspend(false);
                nativeSetInteractive(true);
                mDisplayManagerService.unblankAllDisplaysFromPowerManager();
                mBlanked = false;
                ///M: add for tvout and hdmi
                mTvOut.tvoutPowerEnable(true); 
                mHDMI.hdmiPowerEnable(true); 
                ///@}
                if (DEBUG) {
                    Slog.d(TAG_P, "unblankAllDisplays out ...");
                }
         if (mBootCompleted) {
               Intent intent = new Intent(ACTION_LOCK_SCREEN_SHOW);
               mContext.sendBroadcast(intent);
       } 
            }
        }
最后发送广播的代码,是我们自己添加的。根据unblankAllDisplays函数和broadcastIntent函数,可以看到TID=24的线程此时持有了DisplayBlankerImpl锁(unblankAllDisplays),等待ActivityManagerService锁(broadcastIntent)释放。

同样,跟踪TID=12线程的堆栈,在ActivityManagerService的wake_up函数中持有锁:

public void wakingUp() {
        if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Requires permission "
                    + android.Manifest.permission.DEVICE_POWER);
        }

synchronized(this) {
            Slog.i(TAG, "wakingUp");
            mWentToSleep = false;
            updateEventDispatchingLocked();
            comeOutOfSleepIfNeededLocked();
        }
    }

根据wakingUp函数和isScreenOnInternal函数,可以看到TID=12的线程持有ActivityManagerService锁(wakingUp),等待PowerManagerService.mLock锁(isScreenOnInternal)。到这,似乎看到了希望,迷雾要拨开了,有点小自信是死锁导致的,但还不能最终下结论。

一鼓作气,跟踪TID=85线程的堆栈,在PowerManagerService的dump有持有锁的操作:

protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
....
        synchronized (mLock) {

...

}

根据toString函数和dump函数,可以看到TID=85线程此时持有PowerManagerService.mLock锁(dump),需要DisplayBlankerImpl(toString)。

似乎谜底已经揭晓了,如果你还没有看出来(其实我也没看出来),来个表看看吧!

表二 各线程锁的情况

清楚了吗?多么清晰的循环等待呀!死锁都死的这么完美,还是图表效果好,看来有时候在纸上画画还是有用的!

[Android Pro] 通过Android trace文件分析死锁ANR的更多相关文章

  1. 转【Oracle】一款非常好用的trace文件分析工具

    [Oracle]一款非常好用的trace文件分析工具之一   北在南方 2016-04-14 11:23:58 浏览547 评论0 摘要: 介绍一款非常好用的10046分析工具--trca(Trace ...

  2. 使用trace文件分析ANR

    2017年02月07日 12:32:45 不死鸟JGC 阅读数 13886更多 分类专栏: Android   版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链 ...

  3. NS2中trace文件分析

    ns中模拟出来的时间最终会以trace文件的形式告诉我们,虽然说一般都是用awk等工具分析trace文件,但是了解trace文件的格式也是必不可少的.下面就介绍一下无线网络模拟中trace文件的格式. ...

  4. anr trace文件分析

    测试给的trace文件好几万行,怎么看? 1.搜索 你的包名,看它报错误报在你代码的哪里 2.在你代码里面分析 还有,synchronized 就是用来防止多线程调用的,没有那么神奇.

  5. Oracle 10046 trace文件分析

    生成10046 trace文件: SQL> create table t10046 as select * from dba_objects; Table created. SQL> se ...

  6. [Android Pro] root用户删除文件提示:Operation not permitted

    reference to : http://blog.csdn.net/evanbai/article/details/6187578 一些文件看上去可能一切正常,但当您尝试删除的时候,居然也会报错, ...

  7. [Android Pro] 关于Android 7.0无法进行https抓包的问题

    cp from  : https://www.cnblogs.com/wytings/p/6954293.html 在App进行数据请求的时候,如果每次都打印log去判断是一件很不“人性化”的操作行为 ...

  8. [Android Pro] 完美Android Cursor使用例子(Android数据库操作)

    reference to : http://www.ablanxue.com/prone_10575_1.html 完美 Android Cursor使用例子(Android数据库操作),Androi ...

  9. [Android Pro] 关于Android的HTTP客户端的小秘密

    原文:http://android-developers.blogspot.com/2011/09/androids-http-clients.html 译文:http://yunfeng.sinaa ...

随机推荐

  1. 汕头市队赛 SRM 07 B 好玩的麻将

    B 好玩的麻将 SRM 07 背景&&描述 天才麻将少女KPM立志要在日麻界闯出一番名堂.     KPM上周又打了n场麻将,又控了分使得自己的排名是1..n的一个排列.     但她 ...

  2. Yeelight介绍

    1. 介绍 Yeelight是小米生态链中的WiFi智能灯泡,本文介绍它的接入和控制实现: Yeelight使用的是自定义的私有协议,该协议采用了类似SSDP的发现机制和基于JSON的控制命令 2. ...

  3. 错误”ORA-12560: TNS: 协议适配器错误“解决方法

    错误”ORA-12560: TNS: 协议适配器错误“解决方法 原本是可以通过sqlplus连接数据库的,命令如下: C:\Users\xuwei>sqlplus /nolog SQL*Plus ...

  4. 一段js代码的分析

    function foo(a){ console.log(a+b); b=a+2; console.log(a+b); } foo(2); foo(3); var b=3; foo(4); 结果是: ...

  5. spring applicationContext.xml 中bean配置

    如果采用set get方法配置bean,bean需要有set get 方法需要有无参构造函数,spring 在生成对象时候会调用get和set方法还有无参构造函数 如果采用constructor方法则 ...

  6. AC日记——Keywords Search hdu 2222

    2222 思路: ac自动机模板题: 代码: #include <cstdio> #include <cstring> #include <iostream> #i ...

  7. The number of steps(概率dp)

    Description Mary stands in a strange maze, the maze looks like a triangle(the first layer have one r ...

  8. ACM竞赛常用头文件模板-备忘

    备忘. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> ...

  9. UVA12096 集合栈计算机(map和vector实现双射关系+集合的交并运算的STL)

    题目大意: 对于一个以集合为元素的栈,初始时栈为空. 输入的命令有如下几种: PUSH:将空集{}压栈 DUP:将栈顶元素复制一份压入栈中 UNION:先进行两次弹栈,将获得的集合A和B取并集,将结果 ...

  10. Lightoj-1356 Prime Independence(质因子分解)(Hopcroft-Karp优化的最大匹配)

    题意: 找出一个集合中的最大独立集,任意两数字之间不能是素数倍数的关系. 思路: 最大独立集,必然是二分图. 最大数字50w,考虑对每个数质因子分解,然后枚举所有除去一个质因子后的数是否存在,存在则建 ...