1. 程式人生 > >ANR日誌traces.txt分析 (mark~ 很詳細)

ANR日誌traces.txt分析 (mark~ 很詳細)

ANR日誌traces.txt分析

96 DD_Dog 關注

2018.07.24 19:58* 字數 806 閱讀 234評論 0喜歡 0

導致ANR的幾種情況

  • KeyDispatchTimeout(5s): 按鍵或觸控事件在特定時間內無法處理完成
  • BroadcastTimeout(前臺10s,後臺60s): 廣播在特定時間內無法處理完成
  • ServiceTimeout(前臺20s,後臺200s): Service在特定的時間無法處理完成 另外還有ProviderTimeout和WatchDog等導致的ANR

常見的原因

    A.耗時操作,如複雜的layout,龐大的for迴圈,IO等。
B.被Binder 對端block
C.被子執行緒同步鎖block
    D.Binder被佔滿導致主執行緒無法和SystemServer通訊
E.得不到系統資源(CPU/RAM/IO)
其中ABCD比較好分析,而E比較困難。

應用ANR產生的時候,ActivityManagerService的appNotResponding方法就會被呼叫,然後在/data/anr/traces.txt檔案中寫入ANR相關資訊.
ANR就不作介紹了,下面只介紹如何分析ANR異常.

通常發生了ANR,ActivityManager會列印報錯資訊:

E/ActivityManager: ANR in com.rui.android.poc  // ANR出現的程序包名
PID: 1322  // ANR程序ID
Reason: Broadcast of Intent { act=android.intent.action.SCREEN_ON flg=0x50000010 }//導致ANR的原因
Load: 4.13 / 2.52 / 1.05
CPU usage from 0ms to 12843ms later://CPU在ANR發生後的使用情況
98% 1322/com.rui.android.poc: 0.5% user + 98% kernel / faults: 1044 minor 104 major
5.6% 567/system_server: 3.4% user + 2.1% kernel / faults: 1940 minor 217 major
2.3% 766/com.android.systemui: 1.2% user + 1% kernel / faults: 251 minor 165 major
1.4% 268/kworker/0:2: 0% user + 1.4% kernel
1.2% 1690/adbd: 0.1% user + 1% kernel / faults: 2 minor 3 major
1% 756/com.android.phone: 0.6% user + 0.3% kernel / faults: 347 minor 61 major
0.9% 139/surfaceflinger: 0.3% user + 0.5% kernel / faults: 1 minor 5 major
0.2% 929/com.sprd.opm: 0.2% user + 0% kernel / faults: 279 minor 115 major
0.2% 23/kworker/u4:1: 0% user + 0.2% kernel
0.2% 368/kworker/u5:1: 0% user + 0.2% kernel
0.1% 914/com.android.modemassert: 0% user + 0% kernel / faults: 182 minor 66 major
0.1% 44/ksmd: 0% user + 0.1% kernel
0% 909/com.spreadst.validator: 0% user + 0% kernel / faults: 164 minor 39 major
0% 8/rcu_preempt: 0% user + 0% kernel
0% 43/kswapd0: 0% user + 0% kernel
0% 117/mmcqd/0: 0% user + 0% kernel
+0% 1770/kworker/0:3: 0% user + 0% kernel
+0% 1774/debuggerd: 0% user + 0% kernel
97% TOTAL: 3.7% user + 55% kernel + 38% iowait  // CUP佔用情況
CPU usage from 12301ms to 12824ms later:
100% 1322/com.rui.android.poc: 0% user + 100% kernel
100% 1322/rui.android.poc: 0% user + 100% kernel
1.9% 1333/Binder_2: 0% user + 1.9% kernel
 3.8% 567/system_server: 1.9% user + 1.9% kernel
3.8% 581/ActivityManager: 1.9% user + 1.9% kernel
1.3% 139/surfaceflinger: 1.3% user + 0% kernel
1.3% 249/DispSync: 0% user + 1.3% kernel
1.5% 766/com.android.systemui: 1.5% user + 0% kernel / faults: 2 minor
1.5% 766/ndroid.systemui: 0% user + 1.5% kernel
1.6% 1770/kworker/0:3: 0% user + 1.6% kernel
100% TOTAL: 0% user + 54% kernel + 45% iowait

Log分析:

log列印了ANR的基本資訊,我們可以分析CPU使用率得知ANR的簡單情況;如果CPU使用率很高,接近100%,可能是在進行大規模的計算更可能是陷入死迴圈;如果CUP使用率很低,說明主執行緒被阻塞了,並且當IOwait很高,可能是主執行緒在等待I/O操作的完成.
對於ANR只是分析Log很難知道問題所在,我們還需要通過Trace檔案分析stack呼叫情況.

traces.txt是如何生成的

當APP(包括系統APP和使用者APP)程序出現ANR、應用響應慢或WatchDog的監視沒有得到回饋時,系統會dump此時的top程序,程序中Thread的執行狀態就都dump到這個Trace檔案中了.每次發生ANR, 這個檔案都會被清空,寫入新的內容. 如果想檢視以前發生ANR的資訊, 可以去檢視DB檔案.

DropBox中的log

traces.txt只保留最後一次發生ANR時的資訊, android 2.2開始增加了DropBox功能, 保留歷史上發生的所有ANR的log.
“/data/system/dropbox”是DB指定的檔案存放位置.
日誌儲存的最長時間, 預設是3天.

[email protected]_Rui8503:/data # cd system/dropbox/
[email protected]_Rui8503:/data/system/dropbox # ls
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]_Rui8503:/data/system/dropbox #

SystemServer在啟動時, 會建立並新增DROPBOX_SERVICE.

//SystemServer.java
private void startOtherServices() {
... ...
            try {
                Slog.i(TAG, "DropBox Service");
                ServiceManager.addService(Context.DROPBOX_SERVICE,
                        new DropBoxManagerService(context, new File("/data/system/dropbox")));
            } catch (Throwable e) {
                reportWtf("starting DropBoxManagerService", e);
            }
... ...
}

在traces.txt中可以看到如下資訊:

//說明了發生ANR的程序id、時間和程序名稱。
----- pid 2023 at 2018-07-24 19:33:17 -----
Cmd line: com.rui.android.poc
//下面三行是執行緒的基本資訊
JNI: CheckJNI is off; workarounds are off; pins=0; globals=348
DALVIK THREADS://以下是各個執行緒的函式堆疊資訊
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)

//下面一行說明了執行緒名稱、daemon表示守護程序,執行緒的優先順序(預設5)、執行緒鎖id和執行緒狀態,
//執行緒名稱是啟動執行緒的時候手動指明的,這裡的main標識是主執行緒,是Android自動設定的
//一個執行緒名稱,如果是自己手動建立的執行緒,一般會被命名成“Thread-xx”的格式,其中xx是
//執行緒id,它只增不減不會被複用;注意這其中的tid不是執行緒的id,它是一個在Java虛擬機器中用
//來實現執行緒鎖的變數,隨著執行緒的增減,這個變數的值是可能被複用的;
"main" prio=5 tid=1 NATIVE

//group是執行緒組名稱。sCount是此執行緒被掛起的次數,dsCount是執行緒被偵錯程式掛起的次數,
//當一個程序被除錯後,sCount會重置為0,除錯完畢後sCount會根據是否被正常掛起增長,
//但是dsCount不會被重置為0,所以dsCount也可以用來判斷這個執行緒是否被除錯過。obj表示
//這個執行緒的Java物件的地址,self表示這個執行緒Native的地址。
  | group="main" sCount=1 dsCount=0 obj=0x4164dcf0 self=0x41565628

//此後是執行緒的排程資訊,sysTid是Linux下的核心執行緒id,nice是執行緒的排程優先順序,
//sched分別標誌了執行緒的排程策略和優先順序,cgrp是排程屬組,handle是執行緒的處理函式地址。
  | sysTid=2023 nice=-1 sched=0/0 cgrp=apps handle=1074626900

//執行緒當前上下文資訊,state是排程狀態;schedstat從 /proc/[pid]/task/[tid]/schedstat讀出,
//三個值分別表示執行緒在cpu上執行的時間、執行緒的等待時間和執行緒執行的時間片長度,有的
//android核心版本不支援這項資訊,得到的三個值都是0;utm是執行緒使用者態下使用的時間
//值(單位是jiffies);stm是核心態下的排程時間值;core是最後執行這個執行緒的cpu核的序號。
  | state=S schedstat=( 0 0 0 ) utm=49 stm=21 core=0

//執行緒的呼叫棧資訊(這裡可檢視導致ANR的程式碼呼叫流程,分析ANR最重要的資訊)
  (native backtrace unavailable)
  at libcore.io.Posix.open(Native Method)
  at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
  at libcore.io.IoBridge.open(IoBridge.java:430)
  at java.io.FileInputStream.<init>(FileInputStream.java:78)
  at com.ruiven.android.Peripheral.util.kingFiles.write(kingFiles.java:58)
  at com.ruiven.android.Peripheral.util.kingFiles.write(kingFiles.java:51)
  at com.ruiven.android.Peripheral.util.kingLog.writeToFiles(kingLog.java:71)
  at com.ruiven.android.Peripheral.util.kingLog.f(kingLog.java:47)
  at com.ruiven.android.poc.receiver.ReceiverBatteryChanged.onReceive(ReceiverBatteryChanged.java:114)
  at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:767)
  at android.os.Handler.handleCallback(Handler.java:769)
  at android.os.Handler.dispatchMessage(Handler.java:97)
  at android.os.Looper.loop(Looper.java:136)
  at com.ruiven.android.Peripheral.util.Cockroach$1.run(Cockroach.java:38)
  at android.os.Handler.handleCallback(Handler.java:769)
  at android.os.Handler.dispatchMessage(Handler.java:97)
  at android.os.Looper.loop(Looper.java:136)
  at android.app.ActivityThread.main(ActivityThread.java:5375)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:976)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:792)
  at dalvik.system.NativeStart.main(Native Method)

//Binder執行緒是程序的執行緒池中用來處理binder請求的執行緒
"Binder_3" prio=5 tid=23 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x41b765f8 self=0x4f213f18
  | sysTid=1347 nice=0 sched=0/0 cgrp=apps handle=1327603952
  | state=S schedstat=( 0 0 0 ) utm=0 stm=3 core=1
  #00  pc 000205d0  /system/lib/libc.so (__ioctl+8)
  #01  pc 0002d01f  /system/lib/libc.so (ioctl+14)
  #02  pc 0001e519  /system/lib/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+140)
  #03  pc 0001ec67  /system/lib/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+6)
  #04  pc 0001ecfd  /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+48)
  #05  pc 000236cd  /system/lib/libbinder.so
  #06  pc 0000ea19  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+216)
  #07  pc 0004e769  /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+68)
  #08  pc 0000e54b  /system/lib/libutils.so
  #09  pc 0000d240  /system/lib/libc.so (__thread_entry+72)
  #10  pc 0000d3dc  /system/lib/libc.so (pthread_create+240)
  at dalvik.system.NativeStart.run(Native Method)

//JDWP執行緒是支援虛擬機器除錯的執行緒,daemon表示守護程序,不需要關心
"JDWP" daemon prio=5 tid=4 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0x41a21868 self=0x4cc821b0
  | sysTid=785 nice=0 sched=0/0 cgrp=apps handle=1257589656
  | state=S schedstat=( 0 0 0 ) utm=0 stm=0 core=1
  #00  pc 00021420  /system/lib/libc.so (recvmsg+8)
  #01  pc 000668ab  /system/lib/libdvm.so
  #02  pc 00066adf  /system/lib/libdvm.so
  #03  pc 000697af  /system/lib/libdvm.so
  #04  pc 00059fdd  /system/lib/libdvm.so
  #05  pc 0000d240  /system/lib/libc.so (__thread_entry+72)
  #06  pc 0000d3dc  /system/lib/libc.so (pthread_create+240)
  at dalvik.system.NativeStart.run(Native Method)

//“Signal Catcher”負責接收和處理kernel傳送的各種訊號,例如SIGNAL_QUIT、SIGNAL_USR1等就是被該執行緒
//接收到並處理的,traces.txt 檔案中的內容就是由該執行緒負責輸出的,可以看到它的狀態是RUNNABLE.
"Signal Catcher" daemon prio=5 tid=3 RUNNABLE
  | group="system" sCount=0 dsCount=0 obj=0x41a21770 self=0x4af3e5e0
  | sysTid=784 nice=0 sched=0/0 cgrp=apps handle=1257677168
  | state=R schedstat=( 0 0 0 ) utm=2 stm=2 core=1
  at dalvik.system.NativeStart.run(Native Method)
----- end 2023 -----

如果ANR在native層,那麼堆疊中就不會有相關呼叫的路徑,這種情況只能在native層新增更多的Log,一步步來查找了

NATIVE表示當前執行緒的狀態:

Thread狀態及其對應的值

ThreadState (defined at “dalvik/vm/thread.h “)
THREAD_UNDEFINED = -1, /* makes enum compatible with int32_t */
THREAD_ZOMBIE = 0, /* TERMINATED */( 執行緒死亡,終止執行)
THREAD_RUNNING = 1, /* RUNNABLE or running now */(執行緒可執行或正在執行)
THREAD_TIMED_WAIT = 2, /* TIMED_WAITING in Object.wait() */(執行了帶有超時引數的wait、sleep或join函式)
THREAD_MONITOR = 3, /* BLOCKED on a monitor */(執行緒阻塞,等待獲取物件鎖)
THREAD_WAIT = 4, /* WAITING in Object.wait() */( 執行了無超時引數的wait函式)
THREAD_INITIALIZING= 5, /* allocated, not yet running */(新建,正在初始化,為其分配資源)
THREAD_STARTING = 6, /* started, not yet on thread list */(新建,正在啟動)
THREAD_NATIVE = 7, /* off in a JNI native method */(正在執行JNI本地函式)
THREAD_VMWAIT = 8, /* waiting on a VM resource */(正在等待VM資源)
THREAD_SUSPENDED = 9, /* suspended, usually by GC or debugger */(執行緒暫停,通常是由於GC或debug被暫停)                       

**特別說明一下MONITOR狀態和SUSPEND狀態,MONITOR狀態一般是類的同步塊或者同步方法造成的,SUSPENDED狀態在debugger的時候會出現,可以用來區別是不是真的是使用者正常操作跑出了ANR。

mutexes(互斥)的選項簡寫對應含義:
tll--thread list lock
tsl-- thread suspend lock
tscl-- thread suspend count lock
ghl--gc heap lock
hwl--heap worker lock
hwll--heap worker list lock
  • 通過Cmd line: packagename可以找到要分析的程序
  • 並不是trace檔案包含的應用就一定是造成ANR的幫凶,應用出現在trace檔案中,只能說明出現ANR的時候這個應用程序還活著,trace檔案的頂部則是觸發ANR的應用資訊。因此,如果你的應用出現在了trace檔案的頂部,那麼很有可能是因為你的應用造成了ANR,否則是你的應用造成ANR的可能性不大,但是具體是不是還需要進一步分析