anr問題的處理技巧
hi, 大家好,我是愛喫香蕉的猴子,今天記錄一下ANR問題的處理套路吧,這也是自己分析ANR問題一般的思路;
- 根據測試的描述,區分是Monkey測試偶現的 ,還是可以手動復現,一般情況前者多一些,我也是根據前者寫這個記錄;
- 首先,要會區分ANR類型,是什麼類型的ANR ??
- 用戶輸入事件處理超時: KeyDispatchTimeout-主要類型按鍵或觸摸事件,input事件在5S內沒有處理完成發生ANR
- 關鍵字:Reason: Input dispatching timed out xxxx
- Broadcast超時: BroadcastReceiver onReceiver處理事務時前臺廣播在10S內,後臺廣播在60s內沒有處理完成發生ANR
- 關鍵字:Timeout of broadcast XXX/Receiver during timeout:XXX/Broadcast of XXX
- Service超時:ServiceTimeout-bind,create,start,unbind等在主線程處理耗時,前臺Service在20s內,後臺Service在200s內沒有處理完成發生ANR
- 關鍵字:Timeout executing service:/executing service XXX
- ContentProvider超時: publish在10s內沒有處理完成發生ANR
-
- 關鍵字:timeout publishing content providers
- 用戶輸入事件處理超時: KeyDispatchTimeout-主要類型按鍵或觸摸事件,input事件在5S內沒有處理完成發生ANR
- 補充一種類型 窗口獲取焦點超時(input 類型的一種子類型)
- 關鍵字:Reason: Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)需要注意區分同爲Input dispatching timed out大類的用戶輸入事件處理超時,這兩類超時括號內的提示語是不同的。
- 這個問題,我可以簡單理解爲窗口切換時,A --> B, 開始焦點在A 窗口在A ,後面 焦點在A 這時窗口時null,如果這個時候超過了5s,那就會發生anr.
查看現場
- ag “am_anr” / ag ag “ANR in” # 這個時候,你會發現很多或者幾個anr的地方, 我先看時間最早的,我這裏就不貼log了
- 確定最早發生的anr的時間,進程,(如果還沒有確定anr類型) 這裏基本也會看到特定類型anr的信息
- 然後既然確定了一個anr的位置,那我們就要分析,這個是怎麼造成的?? 猜測思路如下
- 1. 主線程耗時操作,如複雜的layout,龐大的for循環,IO等
- 2. 主線程被子線程同步鎖block
- 3. 主線程被Binder 對端block
- 4. Binder被佔滿導致主線程無法和SystemServer通信
- 5. 得不到系統資源(CPU/RAM/IO)(有一個關鍵搜索:CPU usage)
- 如果,並沒有有效的收穫,那就檢索 上面的anr類型的關鍵字grep -v ‘mmm|nnn’ abc.txt
看anr的trace文件 我一般是大概有了瞭解,最後看trace文件
- 匹配一下,上面看到的時間 進程 線程(如果不匹配,也可以反向去找)
- 補充一下:
- main tid=1 //主線程
- group: 線程組名稱
- sCount: suspendCount個數
- dsCount: debugSuspendCount個數
- obj: 0x4025b1b8 線程java對象地址
- self: 線程native的對象地址
- Binder Thread #2: Binder線程是進程的線程中用來處理binder請求的線程.
- SUSPENDED:線程暫停,可能是由於輸出Trace、GC或debug被暫停
- NATIVE: 正在執行JNI本地函數
- MONITOR: 線程阻塞,等待獲取對象鎖
- WAIT: 執行了無限等待的wait函數
- TIMED_WAIT: 執行了帶有超時參數的wait、sleep或join函數
- VMWAIT: 正在等待VM資源
- RUNNING/RUNNABLE: 線程可運行或正在運行
- INITALIZING: 新建,正在初始化,爲其分配資源
- STARTING: 新建,正在啓動
- ZOMBIE: 線程死亡,終止運行,等待父線程回收它
- 一般我們要首先查看main線程,是否出現 block wait suspend ,有沒有線程間的死鎖,有沒有出現binder調用服務端沒有響應,有沒有阻塞在system server.
- 死鎖:線程在獲得一個鎖L1的情況下再去申請另外一個鎖L2,也就是鎖L1想要包含了鎖L2,也就是說在獲得了鎖L1,並且沒有釋放鎖L1的情況下,又去申請獲得鎖L2,這個是產生死鎖的最根本原因。看一個android中的簡單Log
Cmd line: com.android.inputmethod.latin
"main" prio=5 tid=1 NATIVE
......
at android.os.BinderProxy.transact(Native Method)
at com.android.internal.view.IInputMethodManager$Stub$Proxy.getCurrentInputMethodSubtype(IInputMethodManager.java:908) ......
Cmd line: system_server
"main" prio=5 tid=1 MONITOR
- waiting to lock <0x41c5a650> (a java.lang.Object) held by tid=12 (WindowManager)......
"WindowManager" prio=5 tid=12 MONITOR
- waiting to lock <0x41c0c118> (a android.view.inputmethod.InputMethodManager$H) held by tid=61 (Binder_F)......
"Binder_F" prio=5 tid=61 MONITOR
at com.android.server.InputMethodManagerService.getCurrentInputMethodSubtype(InputMethodManagerService.java:~3078)
片
- 解析一下:main等待WindowManager ,WindowManager 又等待Binder_F。因此inputmethod主線程的遠程調用無法返回,導致ANR。
- 還有一點,這裏查看這個cpu佔用信息
11 CPU usage from 9818ms to 0ms ago (2020-12-09 06:03:39.534 to 2020-12-09 06:03:49.352):
12 109% 10198/system_server: 85% user + 24% kernel / faults: 12620 minor 11 major
13 35% 2724/pulseaudio: 34% user + 1.7% kernel
14 13% 10545/com.android.car: 10% user + 3.1% kernel / faults: 3518 minor
............
110 74% TOTAL: 49% user + 24% kernel + 0.1% iowait + 0.1% softirq
- binder調用服務端沒有響應: 基本通過clent和server共同的方法去grep,看服務端在做什麼?
- system server出現block
- 有時候,也可以根據:id threadid 進行grep看執行了什麼程序,例如:1089 1089
- 其實,74%這個是負載,這個負載是算是高,在這個負載的情況出現anr也是有可能是系統性能問題,但不能明確,起碼我們還要找到什麼原因造成的負載高。cpu其他進程佔用信息,基本都相對容易判斷。
- 其實還有一些分析策略,但最常用的還是這些套路吧,有了好的且可以分享的案例,我會補充上來。
Code的搬運工V1.0