Android死機問題分析

1. 死機/phong hang 定義
當手機長時間無法再被用戶控制操作時,我們稱爲死機或者hang 機。
在這裏我們強調長時間,如果是短時間,我們歸結爲Perfomance 問題,即性能問題.

2. 死機表現
* 用戶操作手機無任何響應, 如觸摸屏幕,按鍵操作等.
* 手機屏幕黑屏, 無法點亮屏幕.

* 手機界面顯示內容和用戶輸入風馬牛不相及.


1. 系統簡圖
當用戶對手機進行操作時, 對應的數據流將是下面一個概括的流程圖.
hang process

* HW 如傳感器, 觸摸屏(TP), 物理按鍵(KP)等感知到用戶操作後,觸發相關的中斷(ISR) 傳遞給Kernel, Kernel 相關的driver 對這些ISR 進行處理後,轉化成標準的InputEvent. 
* User Space 的System Server 中的Input System 則持續監聽Kernel 傳遞上來的原始InputEvent, 對其進行進一步的處理後, 變成上層APP 可直接處理的Input Event, 如button 點擊, 長按, 滑動等等.
* APP 對相關的事件進行處理後,請求更新相關的邏輯界面,而這個則由System Server 中的WMS 等來負責.
* 相關的邏輯界面更新後(Z-Window), 則會請求SurfaceFlinger 來產生FrameBuffer 數據, SurfaceFlinger 則會利用GPU 等來計算生成.
* Display System/Driver 則會將FrameBuffer 中的數據更新顯示出來, 這樣用戶才能感知到他的操作行爲.
 
2. 可能死機的原因
原則上上面流程中,每一步出現問題,都可能引發死機問題. 大的方面將,可以分成硬件HW 和 軟件SW 兩個層次, 硬件HW 不在我們的講訴之內.
軟件SW 上,死機的原因可以分成兩種:
(1). 邏輯行爲異常
 ** 邏輯判斷錯誤
 ** 邏輯設計錯誤
 
(2). 邏輯卡頓(block)
 * 死循環 (Deadloop)
 * 死鎖 (Deadlock)
 
從具體的原因上將,可以進一步分成:
(1). Input Driver
* 無法接收HW 的中ISR,產生原始的InputEvent, 或者產生的InputEvent 異常.
(2). Input System
* 無法監聽Kernel 傳遞上來的原始InputEvent, 或者轉換與傳遞異常.
(3). System Logic
* 無法正常響應Input System 傳遞過來的InputEvent, 或者響應出錯.
(4). WMS/Surfaceflinger 行爲異常
* WMS/SF 無法正確的對Z-Window 進行疊加轉換
(5). Display System
* 無法更新Framebuffer 數據,或者填充的數據錯誤
(6). LCM Driver
* 無法將Framebuffer 數據顯示在LCM 上
 
對應硬件HW hang, 經常見得的情況有:
* Power 
* Clock
* Memory & Memory Controller
* Fail IC

3. 哪些數據可以用來分析.
* Backtrace 
Backtrace 又分成Java backtrace, Native Backtrace, Kernel Backtrace. 它是分析死機的非常重要的手段,我們可以快速的知道,對應的process/thread 在當時正在執行哪些動作,卡住哪裏等。可以非常直觀的分析死機現場。
 
* 系統運行環境
客觀的反應系統的執行環境,通常包括如CPU 利用率,Memory 使用情況, Storage 剩餘情況等。這些資料也非常重要,比如可以快速的知道,當時是否有Process 在瘋狂的執行,當時是不是處於嚴重的low memory 情況, Storage 是否有耗盡的情況發生等。
 
* 程序執行環境
客觀的反應當時某個程序(Kernel 也可以看成一個程序)的執行現場, 此類資訊通常包括如process 的coredump, java heap prof, kernel 的memory dump 等。完整的執行環境,我們可以快速的知道當時具體的變量的值,寄存器值等,可以精細的分析問題。
 
* 其他的一些資訊
這些資訊相對來說,比較零散了,如通常的LOG, 一些debug 命令的結果, JTAG & CVD 的數據等。
1. Java Backtrace
從Java Backtrace, 我們可以知道當時Process 的虛擬機執行狀態. Java Backtrace 依靠SignalCatcher 來抓取.
Google default: SignalCatcher catchs SIGQUIT(3), and then print the java backtrace to /data/anr/trace.txt
MTK Enhance:  SignalCatcher catchs SIGSTKFLT(16), and then print the java backtrace to /data/anr/mtktrace.txt( After 6577.SP/ICS2.MP)
You can update system properties dalvik.vm.stack-trace-file to  Change the address,  default is /data/anr/traces.txt
 
1.1 抓取的方式
* 在ENG Build 中
adb remount
adb shell chmod 0777 data/anr
adb shell kill -3 pid
adb pull /data/anr
 
* 在User Build 中
沒有root 權限的情況下,只能直接pull 已經存在的backtrace.
adb pull /data/anr
 
* 你可以嘗試直接使用下面的腳本一次性抓取

adb remount
adb shell chmod 0777 data/anr
adb shell ps
@echo off
set processid=
set /p processid=Please Input process id:
@echo on
adb shell kill -3 %processid%
@echo off
ping -n 8 127.0.0.1>nul
@echo on
adb pull data/anr/traces.txt trace-%processid%.txt
pause
 
1.2 JavaBacktrace 解析
下面是一小段system server 的java backtrace 的開始
----- pid 682 at 2014-07-30 18:04:53 -----
Cmd line: system_server
JNI: CheckJNI is off; workarounds are off; pins=4; globals=1484 (plus 50 weak)
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x4193fde0 self=0x418538f8
  | sysTid=682 nice=-2 sched=0/0 cgrp=apps handle=1074835940
  | state=S schedstat=( 47858718206 26265263191 44902 ) utm=4074 stm=711 core=0
  at android.os.MessageQueue.nativePollOnce(Native Method)
  at android.os.MessageQueue.next(MessageQueue.java:138)
  at android.os.Looper.loop(Looper.java:150)
  at com.android.server.ServerThread.initAndLoop(SystemServer.java:1468)
  at com.android.server.SystemServer.main(SystemServer.java:1563)
  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:829)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645)
  at dalvik.system.NativeStart.main(Native Method)
  
我們一行一行來解析.
* 0# 最開始是 -----PID at Time  然後接着 Cmd line: process name
 
* 1#  the backtrace header: dvm thread :“DALVIK THREADS:”
 
* 2#  Global DVM mutex value: if 0 unlock,  else lock
 tll:  threadListLock, 
 tsl:  threadSuspendLock, 
 tscl: threadSuspendCountLock
 ghl:  gcHeapLock
 
 (mutexes: tll=0 tsl=0 tscl=0 ghl=0)
 
* 3# thread name, java thread Priority, [daemon], DVM thread id, DVM thread status.
 "main"  -> main thread -> activity thread
 prio  -> java thread priority default is 5, (nice =0, linux thread priority 120),  domain is [1,10]
 DVM thread id, NOT linux thread id
 DVM thread Status:
  ZOMBIE,  RUNNABLE, TIMED_WAIT, MONITOR, WAIT, INITALIZING,STARTING, NATIVE, VMWAIT, SUSPENDED,UNKNOWN
 
 "main" prio=5 tid=1 NATIVE
 
* 4# DVM thread status 
 group: default is “main”
   Compiler,JDWP,Signal Catcher,GC,FinalizerWatchdogDaemon,FinalizerDaemon,ReferenceQueueDaemon are system group
 sCount:  thread suspend count
 dsCount: thread dbg suspend count
 obj:     thread obj address
 Sef:  thread point address
 
 group="main" sCount=1 dsCount=0 obj=0x4193fde0 self=0x418538f8
 
* #5 Linux thread status
 sysTId:  linux thread tid
 Nice: linux thread nice value
 sched: cgroup policy/gourp id
 cgrp:  c group  
 handle:  handle address
 sysTid=682 nice=-2 sched=0/0 cgrp=apps handle=1074835940
 
* #6 CPU Sched stat
 Schedstat (Run CPU Clock/ns, Wait CPU Clock/ns,  Slice times)
 utm: utime, user space time(jiffies)
 stm: stime, kernel space time(jiffies)
 Core now running in cpu.
 state=S schedstat=( 47858718206 26265263191 44902 ) utm=4074 stm=711 core=0
 
* #7-more Call Stack
 
1.3 幾種常見的java backtrace
 
1.3.1 ActivityThread 正常狀態/ActivityThread Normal Case

Message Queue is empty, and thread wait for next message.
 "main" prio=5 tid=1 NATIVE
   | group="main" sCount=1 dsCount=0 obj=0x4193fde0 self=0x418538f8
   | sysTid=11559 nice=0 sched=0/0 cgrp=apps/bg_non_interactive handle=1074835940
   | state=S schedstat=( 2397315020 9177261498 7975 ) utm=100 stm=139 core=1
   at android.os.MessageQueue.nativePollOnce(Native Method)
   at android.os.MessageQueue.next(MessageQueue.java:138)
   at android.os.Looper.loop(Looper.java:150)
   at android.app.ActivityThread.main(ActivityThread.java:5299)
   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:829)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:645)
   at dalvik.system.NativeStart.main(Native Method)
  
1.3.2 Java Backtrace Monitor case

Synchronized Lock: 等待同步鎖時的backtrace.
 "AnrMonitorThread" prio=5 tid=24 MONITOR
   | group="main" sCount=1 dsCount=0 obj=0x41fd80c8 self=0x551ac808
   | sysTid=711 nice=0 sched=0/0 cgrp=apps handle=1356369328
   | state=S schedstat=( 8265377638 4744771625 6892 ) utm=160 stm=666 core=0
   at com.android.server.am.ANRManager$AnrDumpMgr.dumpAnrDebugInfoLocked(SourceFile:~832)
   - waiting to lock <0x42838968> (a com.android.server.am.ANRManager$AnrDumpRecord) held by tid=20 (ActivityManager)
   at com.android.server.am.ANRManager$AnrDumpMgr.dumpAnrDebugInfo(SourceFile:824)
   at com.android.server.am.ANRManager$AnrMonitorHandler.handleMessage(SourceFile:220)
   at android.os.Handler.dispatchMessage(Handler.java:110)
   at android.os.Looper.loop(Looper.java:193)
   at android.os.HandlerThread.run(HandlerThread.java:61)
   
1.3.3 執行JNI code 未返回,狀態是native 的情況

 "WifiP2pService" prio=5 tid=37 NATIVE
   | group="main" sCount=1 dsCount=0 obj=0x427a9910 self=0x55f088d8
   | sysTid=734 nice=0 sched=0/0 cgrp=apps handle=1443230288
   | state=S schedstat=( 91121772 135245305 170 ) utm=7 stm=2 core=1
   #00  pc 00032700  /system/lib/libc.so (epoll_wait+12)
   #01  pc 000105e3  /system/lib/libutils.so (android::Looper::pollInner(int)+94)
   #02  pc 00010811  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+92)
   #03  pc 0006c96d  /system/lib/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, int)+22)
   #04  pc 0001eacc  /system/lib/libdvm.so (dvmPlatformInvoke+112)
   #05  pc 0004fed9  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+484)
   #06  pc 00027ea8  /system/lib/libdvm.so
   #07  pc 0002f4b0  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
   #08  pc 0002c994  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+188)
   #09  pc 000632a5  /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+340)
   #10  pc 000632c9  /system/lib/libdvm.so (dvmCallMethod(Thread*, Method const*, Object*, JValue*, ...)+20)
   #11  pc 00057961  /system/lib/libdvm.so
   #12  pc 0000dd40  /system/lib/libc.so (__thread_entry+72)
   at android.os.MessageQueue.nativePollOnce(Native Method)
   at android.os.MessageQueue.next(MessageQueue.java:138)
   at android.os.Looper.loop(Looper.java:150)
   at android.os.HandlerThread.run(HandlerThread.java:61)
   
1.3.4 執行object.wait 等待狀態

 "AsyncTask #1" prio=5 tid=33 WAIT
   | group="main" sCount=1 dsCount=0 obj=0x427a8480 self=0x56036b40
   | sysTid=733 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1443076000
   | state=S schedstat=( 1941480839 10140523154 4229 ) utm=119 stm=75 core=0
   at java.lang.Object.wait(Native Method)
   - waiting on <0x427a8618> (a java.lang.VMThread) held by tid=33 (AsyncTask #1)
   at java.lang.Thread.parkFor(Thread.java:1212)
   at sun.misc.Unsafe.park(Unsafe.java:325)
   at java.util.concurrent.locks.LockSupport.park(LockSupport.java:157)
   at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2017)
   at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:410)
   at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1035)
   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1097)
   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
   at java.lang.Thread.run(Thread.java:848)
 
1.3.5 Suspend 狀態,通常表明是抓取backtrace 時,當時還正在執行java code, 被強制suspend 的情況
 "FileObserver" prio=5 tid=23 SUSPENDED
   | group="main" sCount=1 dsCount=0 obj=0x41fd1dc8 self=0x551abda0
   | sysTid=710 nice=0 sched=0/0 cgrp=apps handle=1427817920
   | state=S schedstat=( 130152222 399783851 383 ) utm=9 stm=4 core=0
   #00  pc 000329f8  /system/lib/libc.so (__futex_syscall3+8)
   #01  pc 000108cc  /system/lib/libc.so (__pthread_cond_timedwait_relative+48)
   #02  pc 0001092c  /system/lib/libc.so (__pthread_cond_timedwait+64)
   #03  pc 00055a93  /system/lib/libdvm.so
   #04  pc 0005614d  /system/lib/libdvm.so (dvmChangeStatus(Thread*, ThreadStatus)+34)
   #05  pc 0004ae7f  /system/lib/libdvm.so
   #06  pc 0004e353  /system/lib/libdvm.so
   #07  pc 000518d5  /system/lib/libandroid_runtime.so
   #08  pc 0008af9f  /system/lib/libandroid_runtime.so
   #09  pc 0001eacc  /system/lib/libdvm.so (dvmPlatformInvoke+112)
   #10  pc 0004fed9  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+484)
   #11  pc 00027ea8  /system/lib/libdvm.so
   #12  pc 0002f4b0  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
   #13  pc 0002c994  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+188)
   #14  pc 000632a5  /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+340)
   #15  pc 000632c9  /system/lib/libdvm.so (dvmCallMethod(Thread*, Method const*, Object*, JValue*, ...)+20)
   #16  pc 00057961  /system/lib/libdvm.so
   #17  pc 0000dd40  /system/lib/libc.so (__thread_entry+72)
   at android.os.FileObserver$ObserverThread.observe(Native Method)
   at android.os.FileObserver$ObserverThread.run(FileObserver.java:88)
 
2. Native Backtrace
 
2.1 Native Backtrace 抓取方式
 
2.1.1 利用debuggerd 抓取
MTK 已經制作了一個利用debuggerd 抓取Native backtrace 的tool RTT(Runtime Trace), 對應的執行命令是:
rtt built timestamp (Apr 18 2014 15:36:21)
USAGE : rtt [-h] -f function -p pid [-t tid]
  -f funcion : current support functions:
                 bt  (Backtrace function)
  -p pid     : pid to trace
  -t tid     : tid to trace
  -n name    : process name to trace
  -h         : help menu
  
2.1.2 添加代碼直接抓取
Google 默認提供了CallStack API, 請參考
system/core/include/libutils/CallStack.h 
system/core/libutils/CallStack.cpp
可快速打印單個線程的backtrace.
 
2.2 解析Native Backtrace
你可以使用GDB, 或者addr2line 等 tool 來解析抓回的Native Backtrace, 從而知道當時正在執行的native 代碼.
如addr2line 執行
arm-linux-androideabi-addr2line -f -C -e symbols address
 
3. Kernel Backtrace
 
3.1 Kernel Backtrace 抓取方式
 
3.1.1 運行時抓取
* AEE/RTT 工具

 
* Proc System
  cat proc/pid/task/tid/stack

 
* Sysrq-trigger
 adb shell cat proc/kmsg > kmsg.txt
 adb shell "echo 8 > proc/sys/kernel/printk“ //修改printk loglevel
 adb shell "echo t > /proc/sysrq-trigger“ //打印所有的backtrace
 adb shell "echo w > /proc/sysrq-trigger“//打印'-D' status 'D' 的 process
 
* KDB
 Long press volume UP and DOWN more then 10s
 btp             <pid>                
 Display stack for process <pid>
 bta             [DRSTCZEUIMA]        
 Display stack all processes
 btc                                  
 Backtrace current process on each cpu
 btt             <vaddr>              
 Backtrace process given its struct task add
 
3.1.2 添加代碼直接抓取
* #include <linux/sched.h>
 當前thread:  dump_stack();
 其他thread:  show_stack(task, NULL);
 
3.2 Process/Thread 狀態
 "R (running)",  /*   0 */
 "S (sleeping)",  /*   1 */
 "D (disk sleep)", /*   2 */
 "T (stopped)",  /*   4 */
 "t (tracing stop)", /*   8 */
 "Z (zombie)",  /*  16 */
 "X (dead)",  /*  32 */
 "x (dead)",  /*  64 */
 "K (wakekill)",  /* 128 */
 "W (waking)",  /* 256 */
 
通常一般的Process 處於的狀態都是S (sleeping), 而如果一旦發現處於如D (disk sleep), T (stopped), Z (zombie) 等就要認真審查.
 
4. 幾種典型的異常情況

4.1 Deadlock
下面這個case 可以看到PowerManagerService, ActivityManager, WindowManager 相互之間發生deadlock.
"PowerManagerService" prio=5 tid=25 MONITOR
  | group="main" sCount=1 dsCount=0 obj=0x42bae270 self=0x6525d5c0
  | sysTid=913 nice=0 sched=0/0 cgrp=apps handle=1696964440
  | state=S schedstat=( 5088939411 10237027338 34016 ) utm=232 stm=276 core=2
  at com.android.server.am.ActivityManagerService.bindService(ActivityManagerService.java:~14079)
  - waiting to lock <0x42aa0f78> (a com.android.server.am.ActivityManagerService) held by tid=16 (ActivityManager)
  at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1665)
  at android.app.ContextImpl.bindService(ContextImpl.java:1648)
  at com.android.server.power.PowerManagerService.bindSmartStandByService(PowerManagerService.java:4090)
  at com.android.server.power.PowerManagerService.handleSmartStandBySettingChangedLocked(PowerManagerService.java:4144)
  at com.android.server.power.PowerManagerService.access$5600(PowerManagerService.java:102)
  at com.android.server.power.PowerManagerService$SmartStandBySettingObserver.onChange(PowerManagerService.java:4132)
  at android.database.ContentObserver$NotificationRunnable.run(ContentObserver.java:181)
  at android.os.Handler.handleCallback(Handler.java:809)
  at android.os.Handler.dispatchMessage(Handler.java:102)
  at android.os.Looper.loop(Looper.java:139)
  at android.os.HandlerThread.run(HandlerThread.java:58)
  
  "ActivityManager" prio=5 tid=16 MONITOR
  | group="main" sCount=1 dsCount=0 obj=0x42aa0d08 self=0x649166b0
  | sysTid=902 nice=-2 sched=0/0 cgrp=apps handle=1687251744
  | state=S schedstat=( 39360881460 25703061063 69675 ) utm=1544 stm=2392 core=2
  at com.android.server.wm.WindowManagerService.setAppVisibility(WindowManagerService.java:~4783)
  - waiting to lock <0x42d17590> (a java.util.HashMap) held by tid=12 (WindowManager)
  at com.android.server.am.ActivityStack.stopActivityLocked(ActivityStack.java:2432)
  at com.android.server.am.ActivityStackSupervisor.activityIdleInternalLocked(ActivityStackSupervisor.java:2103)
  at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.activityIdleInternal(ActivityStackSupervisor.java:2914)
  at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.handleMessage(ActivityStackSupervisor.java:2921)
  at android.os.Handler.dispatchMessage(Handler.java:110)
  at android.os.Looper.loop(Looper.java:147)
  at com.android.server.am.ActivityManagerService$AThread.run(ActivityManagerService.java:2112)
  
  "WindowManager" prio=5 tid=12 MONITOR
  | group="main" sCount=1 dsCount=0 obj=0x42a92550 self=0x6491dd48
  | sysTid=898 nice=-4 sched=0/0 cgrp=apps handle=1687201104
  | state=S schedstat=( 60734070955 41987172579 219755 ) utm=4659 stm=1414 core=1
  at com.android.server.power.PowerManagerService.setScreenBrightnessOverrideFromWindowManagerInternal(PowerManagerService.java:~3207)
  - waiting to lock <0x42a95140> (a java.lang.Object) held by tid=25 (PowerManagerService)
  at com.android.server.power.PowerManagerService.setScreenBrightnessOverrideFromWindowManager(PowerManagerService.java:3196)
  at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedInner(WindowManagerService.java:9686)
  at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop(WindowManagerService.java:8923)
  at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLocked(WindowManagerService.java:8879)
  at com.android.server.wm.WindowManagerService.access$500(WindowManagerService.java:170)
  at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:7795)
  at android.os.Handler.dispatchMessage(Handler.java:110)
  at android.os.Looper.loop(Looper.java:147)
  at android.os.HandlerThread.run(HandlerThread.java:58)
 
 
4.2 執行JNI native code 後一去不復返
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x41bb3d98 self=0x41ba2878
  | sysTid=814 nice=-2 sched=0/0 cgrp=apps handle=1074389380
  | state=D schedstat=( 22048890928 19526803112 32612 ) utm=1670 stm=534 core=0
  (native backtrace unavailable)
  at android.hardware.SystemSensorManager$BaseEventQueue.nativeDisableSensor(Native Method)
  at android.hardware.SystemSensorManager$BaseEventQueue.disableSensor(SystemSensorManager.java:399)
  at android.hardware.SystemSensorManager$BaseEventQueue.removeAllSensors(SystemSensorManager.java:325)
  at android.hardware.SystemSensorManager.unregisterListenerImpl(SystemSensorManager.java:194)
  at android.hardware.SensorManager.unregisterListener(SensorManager.java:560)
  at com.android.internal.policy.impl.WindowOrientationListener.disable(WindowOrientationListener.java:139)
  at com.android.internal.policy.impl.PhoneWindowManager.updateOrientationListenerLp(PhoneWindowManager.java:774)
  at com.android.internal.policy.impl.PhoneWindowManager.screenTurnedOff(PhoneWindowManager.java:4897)
  at com.android.server.power.Notifier.sendGoToSleepBroadcast(Notifier.java:518)
  at com.android.server.power.Notifier.sendNextBroadcast(Notifier.java:434)
  at com.android.server.power.Notifier.access$500(Notifier.java:63)
  at com.android.server.power.Notifier$NotifierHandler.handleMessage(Notifier.java:584)
  at android.os.Handler.dispatchMessage(Handler.java:110)
  at android.os.Looper.loop(Looper.java:193)
  at com.android.server.ServerThread.initAndLoop(SystemServer.java:1436)
  at com.android.server.SystemServer.main(SystemServer.java:1531)
  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:824)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
  at dalvik.system.NativeStart.main(Native Method)
  
===>
KERNEL SPACE BACKTRACE, sysTid=814
 [<ffffffff>] 0xffffffff from [<c07e5140>] __schedule+0x3fc/0x950
 [<c07e4d50>] __schedule+0xc/0x950 from [<c07e57e4>] schedule+0x40/0x80
 [<c07e57b0>] schedule+0xc/0x80 from [<c07e5ae4>] schedule_preempt_disabled+0x20/0x2c
 [<c07e5ad0>] schedule_preempt_disabled+0xc/0x2c from [<c07e3c3c>] mutex_lock_nested+0x1b8/0x560
 [<c07e3a90>] mutex_lock_nested+0xc/0x560 from [<c05667d8>] gsensor_operate+0x1bc/0x2c0
 [<c0566628>] gsensor_operate+0xc/0x2c0 from [<c0495fa0>] hwmsen_enable+0xa8/0x30c
 [<c0495f04>] hwmsen_enable+0xc/0x30c from [<c0496500>] hwmsen_unlocked_ioctl+0x2fc/0x528
 [<c0496210>] hwmsen_unlocked_ioctl+0xc/0x528 from [<c018ad98>] do_vfs_ioctl+0x94/0x5bc
 [<c018ad10>] do_vfs_ioctl+0xc/0x5bc from [<c018b33c>] sys_ioctl+0x7c/0x8c
 [<c018b2cc>] sys_ioctl+0xc/0x8c from [<c000e480>] ret_fast_syscall+0x0/0x40
 [<ffffffff>]  from [<ffffffff>] 

1. 系統運行環境
客觀的反應系統的執行環境,通常包括如CPU 利用率,Memory 使用情況, Storage 剩餘情況等。這些資料也非常重要,比如可以快速的知道,當時是否有Process 在瘋狂的執行,當時是不是處於嚴重的low memory 情況, Storage 是否有耗盡的情況發生等。
 
2. CPU Usage
追查CPU 利用率可大體的知道,當時機器是否有Process 在瘋狂的運行,  當時系統運行是否繁忙。通常死機分析,只需要抓取基本的使用情況即可。下面說一下一般的抓取方式
 
2.1 top
top 可以簡單的查詢Cpu 的基本使用情況
Usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]
    -m num  Maximum number of processes to display.
    -n num  Updates to show before exiting.
    -d num  Seconds to wait between updates.
    -s col  Column to sort by (cpu,vss,rss,thr).
    -t      Show threads instead of processes.
    -h      Display this help screen.
 
注意的是top 的CPU% 是按全部CPU 來計算的,如果以單線程來計算,比如當時有開啓4個核心,那麼最多吃到25%. 
 
個人常見的操作方式如:  top -t -m 5 -n 2
I.E 正常情況
User 2%, System 12%, IOW 0%, IRQ 0%
User 14 + Nice 0 + Sys 67 + Idle 471 + IOW 0 + IRQ 0 + SIRQ 2 = 554
  PID   TID PR CPU% S     VSS     RSS PCY UID      Thread          Proc
 2423  2423  1   8% R   2316K   1128K     root     top             top
  270   270  0   1% S   2160K    924K     root     aee_resmon      /system/bin/aee_resmon
  159   159  0   0% D      0K      0K     root     bat_thread_kthr
    3     3  0   0% S      0K      0K     root     ksoftirqd/0
   57    57  0   0% D      0K      0K     root     hps_main
 
User 1%, System 7%, IOW 0%, IRQ 0%
User 10 + Nice 0 + Sys 41 + Idle 494 + IOW 0 + IRQ 0 + SIRQ 0 = 545
  PID   TID PR CPU% S     VSS     RSS PCY UID      Thread          Proc
 2423  2423  1   8% R   2324K   1152K     root     top             top
   57    57  0   0% D      0K      0K     root     hps_main
  242   419  0   0% S   8600K   4540K     shell    mobile_log_d    /system/bin/mobile_log_d
  982   991  0   0% S   4364K   1156K     media_rw sdcard          /system/bin/sdcard
  272   272  0   0% S  30680K   9048K     root     em_svr          /system/bin/em_svr
  
  從上面可以看出, 系統基本運行正常,沒有很吃CPU 的進程.
 
I.E 異常情況
 
User 59%, System 4%, IOW 2%, IRQ 0%
User 1428 + Nice 0 + Sys 110 + Idle 811 + IOW 67 + IRQ 0 + SIRQ 1 = 2417
  PID   TID PR CPU% S     VSS     RSS PCY UID      Thread          Proc
16132 32195  3  14% R 997100K  53492K  bg u0_a60   Thread-1401     com.android.mms
16132 32190  1  14% R 997100K  53492K  bg u0_a60   Thread-1400     com.android.mms
16132 32188  2  14% R 997100K  53492K  bg u0_a60   Thread-1399     com.android.mms
16132 32187  0  14% R 997100K  53492K  bg u0_a60   Thread-1398     com.android.mms
18793 18793  4   1% R   2068K   1020K     shell    top             top
 
User 67%, System 3%, IOW 7%, IRQ 0%
User 1391 + Nice 0 + Sys 75 + Idle 435 + IOW 146 + IRQ 0 + SIRQ 1 = 2048
  PID   TID PR CPU% S     VSS     RSS PCY UID      Thread          Proc
16132 32195  3  16% R 997100K  53492K  bg u0_a60   Thread-1401     com.android.mms
16132 32188  2  16% R 997100K  53492K  bg u0_a60   Thread-1399     com.android.mms
16132 32190  0  16% R 997100K  53492K  bg u0_a60   Thread-1400     com.android.mms
16132 32187  1  16% R 997100K  53492K  bg u0_a60   Thread-1398     com.android.mms
18793 18793  4   2% R   2196K   1284K     shell    top             top
 
可以明顯的看到,貴司的mms 的4個thread 都有進入了deadloop, 分別佔用了一個cpu core.  同時可以快速抓取他們的java trace,  進一步可以看到當時MMS 的四個backtrace, 以便快速分析.
 
"Thread-1401" prio=5 tid=32 SUSPENDED JIT
  | group="main" sCount=1 dsCount=0 obj=0x4264f860 self=0x7b183558
  | sysTid=32195 nice=0 sched=0/0 cgrp=apps/bg_non_interactive handle=2078705952
  | state=S schedstat=( 3284456714198 104216273858 383002 ) utm=324720 stm=3725 core=5
  at com.yulong.android.mms.c.f.d(MmsChatDataServer.java:~1095)
  at com.yulong.android.mms.ui.MmsChatActivity$37.run(MmsChatActivity.java:7582)
  at java.lang.Thread.run(Thread.java:841)
 
"Thread-1400" prio=5 tid=31 SUSPENDED JIT
  | group="main" sCount=1 dsCount=0 obj=0x41f5d8f0 self=0x7be2a8e8
  | sysTid=32190 nice=0 sched=0/0 cgrp=apps/bg_non_interactive handle=2078029504
  | state=S schedstat=( 3284905134412 105526230562 382946 ) utm=324805 stm=3685 core=5
  at com.yulong.android.mms.ui.MmsChatActivity$37.run(MmsChatActivity.java:~7586)
  at java.lang.Thread.run(Thread.java:841)
 
"Thread-1399" prio=5 tid=30 SUSPENDED JIT
  | group="main" sCount=1 dsCount=0 obj=0x42564d28 self=0x7b0e6838
  | sysTid=32188 nice=0 sched=0/0 cgrp=apps/bg_non_interactive handle=2077662640
  | state=S schedstat=( 3288042313685 103203810616 375959 ) utm=325143 stm=3661 core=7
  at com.yulong.android.mms.ui.MmsChatActivity$37.run(MmsChatActivity.java:~7586)
  at java.lang.Thread.run(Thread.java:841)
 
"Thread-1398" prio=5 tid=29 SUSPENDED
  | group="main" sCount=1 dsCount=0 obj=0x4248e5a8 self=0x7be0d128
  | sysTid=32187 nice=0 sched=0/0 cgrp=apps/bg_non_interactive handle=2079251904
  | state=S schedstat=( 3287248372432 105116936413 379634 ) utm=325055 stm=3669 core=6
  at com.yulong.android.mms.ui.MmsChatActivity$37.run(MmsChatActivity.java:~7586)
  at java.lang.Thread.run(Thread.java:841)
 
當時處於suspend, 即意味着當時這四個thread 正在執行java code, 而抓取backtrace 時強制將thread suspend 。看起來客戶改動所致,並且客戶有Proguard, 麻煩客戶自己review 代碼。
 
2.2 cputime
通常用cputime 來打印一段時間內, CPU 的利用率的統計情況, 資訊比top 更加詳細.

cputime -h
Usage: cputime  [-start/-stop]  [-n iterations] [-d delay] [-e time] [ -m max_count ]  [-p] [-s sort_column] [-i id] [-h]
 -start       Start cpu time monitor.
 -stop        Stop cpu time monitor.
 -n  num      Updates to show before exiting.
 -d  num      Seconds to wait between updates.
 -m  num      Maximum number of information to display.
 -e  num      Enable CPU time monitor and stop monitor after num seconds. If no this parameter will
              show last cputime monitor data.
 -p           Show process instead of thread. If no this parameter default will show thread information.
 -s  col      Column to sort by time/user/kernel/id/isr_c/isr_t(cputime/user time/kernel time/id/isr_count/
              isr_time). If no this parameter default will sort by cputime.
 -i  id       show isr information of thread id.
 -h           Display this help screen.
*       Example     *
cputime -e 100: Enable cputime monitor and stop after 100 seconds. Then show thread cputime, and sort by cputime.
cputime -e 200  -s user: Enable cputime monitor and stop after 200 seconds. Then show thread cputime, and sort by user time.
cputime: Show thread cputime, and sort by cputime.
cputime -p -s id: Show process cputime, and sort by process id.
 
2.3 ftrace
ftrace 可以紀錄CPU 最爲詳細的執行情況, 即linux scheduler 的執行情況. 通常默認只開啓 sched_switch. 
如何抓取ftrace 可以查詢相關的FAQ.
 
2.4 Kernel core status
有的時候我們需要追查一下,當時Kernel 的基本調度情況,以及接收中斷的情況,以判斷當前CPU 執行的基本情況是否異常。比如有時候如果某個中斷上來太過頻繁,就容易導致系統運行緩慢,甚至死機。
* CPU Sched status
     adb shell cat proc/sched_debug
     Use sysrq-trigger
* CPU interrupts
     adb shell cat proc/interrupts
     adb shell cat proc/irq/irq_id/spurious
 
3. Memory Usage
Memory Usage, 我們通常會審查,系統當時memory 是否足夠, 是否處於low memory 狀態, 是否可能出現因無法申請到memory 而卡死的情況.

常見的一些基本信息如下:
* meminfo: basic memory status
adb shell cat proc/meminfo
adb shell cat proc/pid/maps
adb shell cat proc/pid/smaps
* procrank info: all process memory status
adb shell procrank
adb shell procmem pid
adb shell dumpsys meminfo pid
* zoneinfo:
adb shell cat proc/zoneinfo
* buddyinfo:
adb shell cat /proc/buddyinfo
 
4. Storage Usage
查看Storage 的情況,通常主要是查詢data 分區是否已經刷滿, sdcard 是否已經刷滿, 剩餘的空間是否足夠。以及是否有產生超大文件等。
通常使用的命令如 df
df
Filesystem               Size     Used     Free   Blksize
/dev                   446.0M   128.0K   445.8M   4096
/sys/fs/cgroup         446.0M    12.0K   445.9M   4096
/mnt/secure            446.0M     0.0K   446.0M   4096
/mnt/asec              446.0M     0.0K   446.0M   4096
/mnt/obb               446.0M     0.0K   446.0M   4096
/system                  1.2G   915.3M   355.5M   4096
/data                    1.1G   136.7M  1010.1M   4096
/cache                 106.2M    48.0K   106.2M   4096
/protect_f               4.8M    52.0K     4.8M   4096
/protect_s               4.8M    48.0K     4.8M   4096
/mnt/cd-rom              1.2M     1.2M     0.0K   2048
/mnt/media_rw/sdcard0     4.6G     1.1G     3.4G   32768
/mnt/secure/asec         4.6G     1.1G     3.4G   32768
/storage/sdcard0         4.6G     1.1G     3.4G   32768
以及ls, du 等命令,如du

du -help
usage: du [-H | -L | -P] [-a | -d depth | -s] [-cgkmrx] [file ...]

du -LP -d 1
8       ./lost+found
88      ./local
384     ./misc
48      ./nativebenchmark
912     ./nativetest
8       ./dontpanic
13376   ./data
8       ./app-private
8       ./app-asec
129424  ./app-lib
8       ./app
136     ./property
16      ./ssh
116312  ./dalvik-cache
8       ./resource-cache
48      ./drm
8       ./mediadrm
8       ./security
3888    ./nvram
8       ./amit
8       ./acdapi
88      ./@btmtk
32      ./sec
8       ./user
16      ./media
16      ./agps_supl
8       ./anr
8       ./gps_mnl
8       ./nfc_socket
16      ./ccci_cfg
32      ./mdlog
1312    ./system
176     ./recovery
32      ./backup
274688  .

1. 系統運行環境
當我們懷疑死機問題可能是某個進程出現問題而引發時,通常我們需要對這個進程進行深入的分析, 即進程運行環境分析。通常包括分析如,線程狀態,各種變量值,寄存器狀態等。在Android 系統中,我們將其劃分成三個層次。
即 Java 運行環境分析, Native 運行環境分析, Kernel 運行環境分析. 下面分別說明.

2. Java 運行環境分析
我們對於Zygote fork 出來的process, 如APP 以及system_server, 都會進行Java 運行環境分析. 其關鍵是分析Java Heap, 以便快速知道某個Java 變量的值, 以及Java 對象的分佈和引用情況。
通常Java Heap 的分析方式則是抓取Java Hprof, 然後使用MAT 等工具進行分析.
* 抓取Hprof 的手法,如:
第一種方式: 使用am 命令
    adb shell am dumpheap {Process} file
    如: 
 adb shell chmod 777 /data/anr
 adb shell am dumpheap com.android.phone /data/anr/phone.hprof
    adb pull /data/anr/phone.hprof
 
第二種方式: 使用DDMS 命令
    在DDMS 中選擇對應的process, 然後在Devices 按鈕欄中選擇Dump Hprof file, 保存即可
 
第三種方式: 通過代碼的方式
    在android.os.Debug 這個class 中有定義相關的抓取hprof 的method.
 如: public static void dumpHprofData(String fileName) throws IOException;
 這樣即可在代碼中直接將這個process 的hprof 保存到相對應的文件中,注意這個只能抓取當時的process.
 如果想抓其他的process 的hprof, 那麼就必須通過AMS 幫忙了。
 可以先獲取IActivityManager 接口,然後調用它的dumpheap 方法。具體的代碼,大家可以參考
 frameworks/base/cmds/am/src/com/android/commands/am/am.java 中的調用代碼
 
第四種方式: 發送SIGUSER1 
    在部分機器中,如果具有root 權限,可以直接發送SIG 10 來抓取, 此時對應的Hprof 保存在/data/misc下面,文件名如:
 heap-dump-tm1357153307-pid1882.hprof
 
* 快速分析
首先, DVM 的Hprof 和 標準的Java Hprof 有一些差別, 需要使用hprof-conv 進行一次轉換, 將DVM 格式的hprof 轉換成標準的java 命令的hprof
    hprof-conv in.hprof out.hprof
其次, 使用如MAT Tool, 打開轉換後的hprof 文件, 通常我們會
 analysis  java thread information
 analysis  java var value
 analysis  Object reference
 analysis  GC path
具體如何使用MAT 分析可以參考MAT 的官方網站.
 
3. Native 運行環境分析
Native 運行環境分析,我們通常會採用Core dump 分析手法. Core dump 紀錄了當時進程的各類關鍵資訊,比如變量參數,線程stack, heap, register 等。通常可以認爲是這個Process 當時最爲完整的資訊了。 但Core dump 往往比較大, 有時甚至會超過1G, 屬於比較重量型的分析手法了。
* 如何抓取Core Dump.
   目前MTK 的機器會將Core Dump 轉換成AEE DB. 否則對應的Core dump 文件即存放在/data/core 目錄下
   手工抓取時, 可以:
   adb shell aee -d coreon
   adb shell kill -31 PID
   此時core dump 就可能存放在兩個目錄下: /data/core, 以及 /sdcard/mtklog/aee_exp 下面新的DB 文件.
* 如何分析Core Dump.
  因爲通常已經將Core Dump 轉換成了AEE DB. 所以首先將AEE DB 解開, 即可以看到PROCESS_COREDUMP 的文件,有的時候此文件很大, 比如超過1G.
  而分析Core Dump 的Tools 很多, 比如traces32, GDB 等,這裏就不詳加說明,可以參考網絡上的相關文檔.
 
4. Kernel 運行環境分析
從82平臺上多了ramdump功能,可以發生KE後將82/92的物理內存壓縮保存到EMMC的內置卡(默認保存到EMMC內置卡上)(92可以選擇外置t卡)上,拿到該文件後就可以轉換爲kernel space,查看kernel各種變量,比查看kernel log更加方便快捷.
只有在eng版本下支持該功能,並且是EMMC的,存在內置T卡才行,
在projectConfig.mk裏的MTK_SHARED_SDCARD必須爲no即MTK_SHARED_SDCARD=no
連上adb後:
adb shell
#echo Y > /sys/module/mrdump/parameters/enable
#echo emmc > /sys/module/mrdump/parameters/device  (注意82只能在EMMC內置t卡上,不能下這條命令,92可以下這條命令修改到sdcard:#echo sdcard > /sys/module/mrdump/parameters/device)
這樣就開啓了ramdump功能,注意重啓後無效,必須重新設置才行
之後重新開機,此時會在
內置T卡:/storage/sdcard0/
或外置T卡:/storage/sdcard1/
看到CEDump.kdmp文件,結合kernel/out/vmlinux或out/target/product/$proj/obj/KERNEL_OBJ/vmlinux一起提供給Mediatek即可做進一步kernel異常重啓的分析.

1. 死機現場分析手法

死機分析如同醫生給病人診斷病情,所有的手法不在乎分爲兩種。
* 高科技的診斷設備,分析病人的病情。死機分析 就依靠各種技術方法去診斷當時手機的運行的真實狀態。
* 通過病人的各種活動,分析病人的潛在病因。 就依靠各種對手機的操作,以及命令,讓手機跑相應的流程,從而進一步分析。
 
See the doctor
 
 
2. 基本分析流程
 
Phone hang analysis flow
 
下面我們將針對每一個流程進行具體的說明.
 
3. Test Phone Usage
* 通過做一些基本的測試, 可大體上確認可能引發死機的模塊, 爲後續Debug 確定好方向。
* 通常用於QA 發現hang 機時,或者讓客戶進行初步的確認。
* 通常只能正向推理,可以做什麼 =》推斷什麼模塊正常;逆向推理可能因爲各種原因而失常。
 
* Touch Panel
   - 屏幕是否有響應(一般情況下沒響應 ^_^).
   - 如果有響應,可能機器已經活過來了,或者客戶當時把ANR 認爲了hang 機;需要進一步和客戶確認情況.
   - 確認虛擬按鍵的情況,通常都設定振動反饋, 如果有, 那麼就認爲當時按鍵資訊可以傳遞到System Server, 此時可能System Server 邏輯異常.
 
* Power Key/ Volumn Up/Down Key
   - 是否可以點亮,關閉屏幕(注意最好關燈測試,不然背光可能觀測不到)
   - 可點亮關閉屏幕,說明KPD ->input manager->PowerManagerService->Power->LCD driver 正常;通常可以懷疑TPD, 以及SurfaceFlinger.
 
* 是否可以顯示音量調整情況
   - 可顯示音量調整情況,進一步說明SurfaceFlinger 也正常,進一步懷疑TPD,或者直接的APP 無響應的情況。
 
* SystemUI & Status Bar
   - Status Bar 是否可以拉下,以便防止只是活動區卡住的情況, 可下拉,說明只是APP 卡住,或者lockscreen 無法解鎖的清理。
 
* LockScreen & Call MT Phone
   - 測試LockScreen 是否可以解鎖,
   - 測試modem 是否已經關閉
   - 可進入電話界面,說明是lockscreen 無法解鎖的情況
   
* Home Key/ Back Key/Search Key
   - 確認當時是否只是APP hang 住的情況,避免將ANR 誤判爲hang 機
 
* 插入USB 觀察充電情況
   - 可確認Surfaceflinger, System Server 的運行情況
 
4. USB/ADB Debug Hang
 
整體的分析流程如下圖.
 
adb debug hang
 
 
4.1 USB/ADB Debug 準備
* 插入USB, 確認ADB 是否可以使用
 - 首先查看windows 的設備管理器裏面是否出現對應的設備
 - 在命令行中輸入adb devices, 看是否可以打印設備信息,在輸入之前您最好先輸入adb kill-server 保證pc 上的adb client 沒有卡住
 - 請確保您使用的PC 上已經安裝ADB,USB 端口本身正常
 

* 正常的ADB 連接情況如下圖
adb connect

* 正常adb devices 查看結果如下圖

4.2 USB/ADB 連接分析
 
* Case1: adb 能正常連接,adb shell #
- 恭喜您adb 目前可以正常接入,可以進行USB/ADB Debug
 
* Case2: windows 設備管理器有反映,adb shell 提示device not found.
- 確認adb 驅動是否安裝好,USB 的uid 和 vid 是否和driver 匹配。
- 可能機器的adbd 已經無法對接,嘗試adb kill-server 來確認
 
* Case3:  windows 設備管理器有反映,adb shell 提示offline.
- 機器的adbd 拒絕連接,或者adbd 無法拉起。
- PC adb 版本過低
- 很可能adbd 已經卡住, adbd process status 爲 ‘D’
 
* Case4:  RSA 指紋認證無法通過
- 需要手機確認PC  RSA 指紋
 
4.3 機器狀態基本檢測
* ps or ps -t or ps -t -p查看進程基本狀態
  -t  以線程爲單位打印
  -p  附加打印線程優先級信息
 
** 追查如system server, surfaceflinger, service manager, media server(ss, sf, sm, ms),zygote,等關鍵進程的狀態,進行初步確認。正常情況下,都應處於’S’, 異常情況有如’D’, ‘T’, ‘Z’ , ‘R’等
** 大體追查ss, sf, sm, ms, zygote 等的memory rss 情況,是否有明顯的溢出情況

** 大體查看當時的ss pid, sf pid,在ICS 上如果機器上層沒有重啓過,通常sf pid < 200, ss pid < 600,如果pid 比較大就說明上層重啓過。

** 是否還存在特別進程,如ipod, 表示在ipo 關機中,如aee_core_forworder, 表示在抓core dump,aee_dumpstate 表示aee 在抓取db 信息等。 
** 追查debuggerd 的process 數量,通常正常時 ,只有一個debuggerd process , pid < 200, 如果有達到4個debuggerd,這個時候將無法進行rtt 之類的操作
 
 
* df
- 審查storage 的使用情況,查看SD 卡和 data 分區使用情況,特別是如SD 卡已滿,或者data 分區寫滿等。
 
* cat proc/meminfo, procrank
- 審查當前的memory 使用情況, 追查各個進程的memory 情況
 
* getprop 
- 審查當前system properties 情況
 
* top -t -m 5 -n 2
- 摸一摸手機,感覺有點熱,或者發燙的話,說明通常是CPU 利用率比較高
- 大體上審查當前CPU 的利用情況,不求精湛

總之,機器狀態基本檢測,目標就是通過簡單幾個命令直接偵測當前手機最爲可能的異常情況。包括關鍵進程基本狀態, CPU 利用率,memory 狀況,storage 狀況等。做出基本的預先分析,從而爲下一步的debug 打好基礎。
 
 
4.4 確認hang機的module
 
我們又回到了最開始的可能影響hang 的那些modules, 下面我們將針對每一個module 做詳細的說明.
 
4.4.1 Input Driver-Input System
* 確認Input Driver-Input System 通路是否正常,即input driver 是否可以傳上正常的輸入
* 最常見的檢測方式是 adb shell getevent
如直接輸入adb shell getevent 可以看到:
C:\Users\mtk71029\Desktop>adb shell getevent
add device 1: /dev/input/event0
  name:     "mtk-kpd"
could not get driver version for /dev/input/mouse0, Not a typewriter
add device 2: /dev/input/event3
  name:     "mtk-tpd"
add device 3: /dev/input/event2
  name:     "hwmdata"
add device 4: /dev/input/event1
  name:     "ACCDET"
could not get driver version for /dev/input/mice, Not a typewriter
 
對應的操作命令是:
Usage: getevent [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]
    -t: show time stamps
    -n: don't print newlines
    -s: print switch states for given bits
    -S: print all switch states
    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)
    -d: show HID descriptor, if available
    -p: show possible events (errs, dev, name, pos. events)
    -i: show all device info and possible events
    -l: label event types and names in plain text
    -q: quiet (clear verbosity mask)
    -c: print given number of events then exit
    -r: print rate events are received
 
通常我們會一項一項的確認native 層是否可以收到Kernel 傳遞來的input event 事件.
比如我們追查KPD 是否有效果, 按power key 可以看到相關的行爲.
C:\Users\mtk71029\Desktop>adb shell getevent -t -l /dev/input/event0
[1388629316.433356] EV_KEY       KEY_POWER            DOWN
[1388629316.433356] EV_SYN       SYN_REPORT           00000000
[1388629316.527596] EV_KEY       KEY_POWER            UP
[1388629316.527596] EV_SYN       SYN_REPORT           00000000
[1388629317.824871] EV_KEY       KEY_POWER            DOWN
[1388629317.824871] EV_SYN       SYN_REPORT           00000000
[1388629317.996095] EV_KEY       KEY_POWER            UP
[1388629317.996095] EV_SYN       SYN_REPORT           00000000
[1388629319.495346] EV_KEY       KEY_POWER            DOWN
[1388629319.495346] EV_SYN       SYN_REPORT           00000000
[1388629319.530963] EV_KEY       KEY_POWER            UP
[1388629319.530963] EV_SYN       SYN_REPORT           00000000
 
當然如果屏幕是點亮時,肯定就要快速追查tpd 以及模擬按鍵是否正常
C:\Users\mtk71029\Desktop>adb shell getevent -t -l /dev/input/event5
[1388629590.432516] EV_ABS       ABS_MT_TOUCH_MAJOR   00000012
[1388629590.432516] EV_ABS       ABS_MT_TRACKING_ID   00000000
[1388629590.432516] EV_KEY       BTN_TOUCH            DOWN
[1388629590.432516] EV_ABS       ABS_MT_POSITION_X    000000d4
[1388629590.432516] EV_ABS       ABS_MT_POSITION_Y    00000280
[1388629590.432516] EV_SYN       SYN_MT_REPORT        00000000
[1388629590.432516] EV_SYN       SYN_REPORT           00000000
[1388629590.496797] EV_ABS       ABS_MT_TOUCH_MAJOR   00000010
[1388629590.496797] EV_ABS       ABS_MT_TRACKING_ID   00000000
[1388629590.496797] EV_ABS       ABS_MT_POSITION_X    000000d4
[1388629590.496797] EV_ABS       ABS_MT_POSITION_Y    0000027d
[1388629590.496797] EV_SYN       SYN_MT_REPORT        00000000
[1388629590.496797] EV_SYN       SYN_REPORT           00000000
[1388629590.506985] EV_ABS       ABS_MT_TOUCH_MAJOR   00000010
[1388629590.506985] EV_ABS       ABS_MT_TRACKING_ID   00000000
[1388629590.506985] EV_ABS       ABS_MT_POSITION_X    000000d3
[1388629590.506985] EV_ABS       ABS_MT_POSITION_Y    0000027a
[1388629590.506985] EV_SYN       SYN_MT_REPORT        00000000
[1388629590.506985] EV_SYN       SYN_REPORT           00000000
[1388629590.517713] EV_KEY       BTN_TOUCH            UP
[1388629590.517713] EV_SYN       SYN_MT_REPORT        00000000
[1388629590.517713] EV_SYN       SYN_REPORT           00000000
 
如果發現無法收到此類資訊,那麼就可以確認對應的devices 可能有故障,需要請對應的driver & vendor 工程師來分析.
 
4.4.2 system-server logic
此分析的關鍵在於審查 system-server 是否還在正常的運轉,這是非常重要的,據統計,40%的usb 可以debug 的hang 機問題,都可以通過對system-server 邏輯的審查找出root cause。
 
System server 是整個android 上層的中樞,容納了最爲重要的居多service。
 
對System server 的審查主要是通過java native 的backtrace 來追查System server 的關鍵thread 有沒有被lock/dead lock,有沒有進入dead loop,狀態是否正常。
 
從Hang 機的角度來看system server 關鍵的thread 如:

(1). JB 以及JB 以前的版本
Serverthread:
System-server 的main looper 建立在serverthread 上,所有service 如果不單獨創立thread-looper 那麼都將運行在這個Server thread 上,其關鍵性不言而喻,用watchdog 來監測該thread.
ActivityManager:
處理所有的Activity 狀態切換,broadcast ,以及ANR 監測等等,非常重要.
WindowManager:
處理各種window 疊加與切換,Input 相關處理.
WindowManagerPolicy/UI:
主要處理LockScreen 相關流程,它卡住,lockscreen 無法解鎖
PowerManagerService:
處理Power 相關事宜,它卡住,屏幕無法熄滅,點亮
InputDispatcher/InputReader
處理Input System 相關事宜, 一旦他們卡住, 那麼所有的Input Event 都無法反饋到其他的module, 機器死機就成必然.
 
(2). KK 以及KK 以後的版本
Google 取消了serverthread 這樣的main looper thread, 直接使用system server 的第一個thread 作爲main looper.  原本第一個thread 是加入binder thread pool, 作爲binder thread 使用.
Google 對system server 的threads 做了進一步的整合,以減少繁雜的線程數,  新增瞭如android.io,android.ui,android.fg,android.bg 等threads. 將大量的普通型的Handler 按時效分類分散到上面的4個threads 中.
所以KK 上除追查JB 上同樣的thread(server thread 除外), 還需要審查android.io, android.ui, android.fg, android.bg 等thread.
 
這些thread 都通過執行MQ-Looper-Handler 的模式運行,所以正常的時候的java/native backtrace 都是:
Java:
  at android.os.MessageQueue.nativePollOnce(Native Method)
  at android.os.MessageQueue.next(MessageQueue.java:138)
  at android.os.Looper.loop(Looper.java:150)
 
Native:
  #00  pc 0002599c  /system/lib/libc.so (epoll_wait+12)
  #01  pc 000105e3  /system/lib/libutils.so (android::Looper::pollInner(int)+94)
  #02  pc 00010811  /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+92)
  #03  pc 0006ca5d  /system/lib/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, int)+22)
 
一旦不是就要給出所以然來.
 
Binder Thread
System server 爲外界提供服務,絕大多數都通過binder 與其他process 對接。Binder Thread 的數量動態調整,默認不會超過16個,如果數量達到16個,即說明當前system server 可能非常繁忙,有process非常頻繁的和Process 通信。
在JB 以及以前版本, System-server 的第一個thread, 在call 起serverthread 後,自己join 到IPCthread pool 中,成爲Binder Thread 的一員。 
對於Binder Thread, 正常時對應的Backtrace:
  #00  pc 000247e8  /system/lib/libc.so (__ioctl+8)
  #01  pc 00038248  /system/lib/libc.so (ioctl+28)
  #02  pc 0001d3a9  /system/lib/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+140)
  #03  pc 0001dae3  /system/lib/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+6)
  #04  pc 0001db79  /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+48)
  #05  pc 00021a79  /system/lib/libbinder.so
  #06  pc 0000ea01  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+216)
  #07  pc 0004f1c9  /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+68)
  #08  pc 0000e533  /system/lib/libutils.so
  #09  pc 0000d600  /system/lib/libc.so (__thread_entry+72)
  at dalvik.system.NativeStart.run(Native Method)
 
抓取system server 的java backtrace,依次check serverthread(JB), ActivityManager, WindowManager, WindowManagerPolicy, PowerManagerService 以及android.io, android.bg, android.fg, android.ui的狀態,如狀態異常,則依次推導。
當發現java backtrace 最後鑽入到異常native method時,抓取其native backtace,通過native backtrace 進一步追查。
如果在native backtrace 中,發現已經call 入binder driver, 那就是通過binder 進行IPC call, 這個時候就要知道binder 的對端process, 然後查閱它的binder thread 進程進一步釐清問題.
 
Case 1: Abnormal Java Backtrace “Deadlock”
圖片deadlock-java-backtrace.png
 
Case 2: Abnormal Java Backtrace “Lock in native thread"
圖片lock-in-native-thread.png
 
Case 3: Abnormal native Backtrace “Lock in Binder”
圖片lock-in-binder.png
 
Case 4: Abnormal native Backtrace "Lock in Binder thread"
圖片lock-in-binder-thread.png
 
當確認前面的key thread 都沒用問題,而通過getevent 確認event 已經input 到system server。問題可能出在input system 中.
WindowManagerService通過InputManager提供的接口開啓一個線程驅動InputReader不斷地從/dev/input /目錄下面的設備文件讀取事件,然後通過InputDispatcher分發給連接到WindowManagerService服務的客戶端。
 
Input Reader 正常的backtrace:
"InputReader" sysTid=611
#00 pc 00027754 /system/lib/libc.so (epoll_wait+12)
#01 pc 0001f345 /system/lib/libinput.so (android::EventHub::getEvents(int, android::RawEvent*, unsigned int)+1092)
#02 pc 0002aaf7 /system/lib/libinput.so (android::InputReader::loopOnce()+142)
#03 pc 00027613 /system/lib/libinput.so (android::InputReaderThread::threadLoop()+8)
#04 pc 00015318 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+492)
#05 pc 0004d12b /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+150)
#06 pc 00014948 /system/lib/libutils.so
#07 pc 0000f66c /system/lib/libc.so (__thread_entry+80)
 
Input Dispatcher 正常的backtrace:
"InputDispatcher" sysTid=610
#00 pc 00027754 /system/lib/libc.so (epoll_wait+12)
#01 pc 0001c05c /system/lib/libutils.so (android::Looper::pollInner(int)+140)
#02 pc 0001c3d4 /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+76)
#03 pc 00026317 /system/lib/libinput.so (android::InputDispatcher::dispatchOnce()+94)
#04 pc 0001f5bd /system/lib/libinput.so (android::InputDispatcherThread::threadLoop()+8)
#05 pc 00015318 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+492)
#06 pc 0004d12b /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+150)
#07 pc 00014948 /system/lib/libutils.so
#08 pc 0000f66c /system/lib/libc.so (__thread_entry+80)
 
Backtrace 通常都可以精確的定位問題點,比如卡在了哪一行。
那有沒有可能您抓backtrace 時,恰好運行到,造成烏龍的情況呢? 這個通常需要具體情況具體分析
--- Native Backtrace 處於R 狀態
--- Java Backtrace 處於 Suspend 狀態, Running 狀態
--- Backtrace 處於明確的非block 狀態
--- Double Backtrace 確認是否CPU 有運轉
 
隨着android 版本的推進,system-server 越來越顯得龐大,爲此Google 對system-server 做了分拆動作。
After 4.0  SurfaceFlinger removed from system-server and created by init, single process SurfaceFlinger.
除SurfaceFlinger 外,對system-server 影響最大的是MediaServer.

注意對於surfaceflinger, mediaserver 等非DVM Process,切忌不要對他們send signal SIGQUIT(3), 將導致對應的process 直接退出
 
4.4.3 Display logic
Display Control Flow
圖片: display-control-flow.png
 
 
* Surface Manager(Surfaceflinger)
** Surfaceflinger 是用戶空間中ramework下libraries 中負責顯示相關的一個模塊 
** 當系統同時執行多個應用程序時,Surfaceflinger 負責管理顯示,主要包括顯示的疊加和顯示的渲染 
** 其中顯示的疊加,MTK 架構按照一定的原則和算法,主要用到GPU 和 OVL 模塊的overlay 功能來做2D/3D 繪圖的顯示合成
 
* HWC
** 基於DDP OVL 硬件做HW Overlay 功能
 
* GPU (Graphic Processing Unit )
** aspecialized electronic circuit designed to accelerate the image processing, frame buffer rendering for output to a display.
 
* FrameBuffer 
** 基於Linux Frame buffer 模型的MTK Framebuffer 實作,主要來管理frame buffer。
 
*DDP(Display Data Path)
** MTK Display Hardware Controller,包括OVL, DMA, Color Processor,BLS,  DSI/DBI/DPI Controller
 
Display Data Flow
圖片: display-data-flow.jpg
 
 
* Display 大致的流程說明

** APP(包括WMS)會把數據透過Surface送到SF(surfaceflinger)
** SF 按照一定規則,通過GPU/HWC把多個Surface合成,並設定到DDP OVL 的對應layer
*** MTK OVL 一般有4 layer(可能不同平臺會有差異,目前基本都是4 layer),每個layer 都可以設定一個對應的圖層,然後OVL 把這寫圖層合成後送給DDP 的下一級Module
*** 某些情況下(比如總的圖層超過4個,OVL 無法處理等各種case) ,會使用GPU 去做2D/3D 的圖層合成到frame buffer

** SF/HWC 最後把GPU合成圖層以及需要OVL 合成的圖層,設定到OVL 的HW Register,並trigger HW Engine
** MTK DDP 就會把合成的數據像管道一樣流向LCM module,最後Panel 顯示相關的畫面
 
 
對Display 的快速分析,我們主要是兩個手段, 第一個確認FrameBuffer 數據是否和理想數據一致, 第二個確認SurfaceFlinger 狀態是否正確.
* 確認FrameBuffer 數據和理想數據一致, 那麼說明android 上層的處理行爲都是正常的, 而問題就很可能出在LCM 的driver 或者 LCM 本身問題, 此時需要聯繫LCM 的driver 工程師以及 LCM vendor 來確認分析, 這裏不再詳細說明.
 
* 確認SurfaceFlinger 的狀態, 用於審查SurfaceFlinger 的行爲是否正常, 對應的Thread 是否能夠正常工作, 我司已經在SurfaceFlinger 裏面導入了Watchdog 機制, 審查SurfaceFlinger 是否有卡住的情況, 對應在main log 裏面會打印如: 
 [SF-WD] detect SF maybe hang!!! 
   這樣的LOG, 並且會紀錄卡頓的時機,如果持續卡頓,毫無疑問, SurfaceFlinger 已經卡住,此時就要分析對應的backtrace 以及 LOG.
   
* SurfaceFlinger 的線程情況在不同的版本上變化比較大, 最直接的方式是審查它binder thread 情況, 以及Event Loop 的執行情況. binder thread 和 system server 情況一致, 不再詳細說明. Event Loop 在JB 以及, JB 以及KK 上都不相同, 這裏分別說明.
 
在JB 以前的版本, SurfaceFlinger 的Event Loop 使用的是普通的pthread 控制, 並且SurfaceFlinger 的第一個Thread 在執行SurfaceFlinger 的初始化後, 轉換爲了binder thread. 而Event Loop 的thread 是新起的 "SurfaceFlinger" thread. 對應的正常backtrace 是:
圖片: surfaceflinger-event-loop-beforce-jb.png
 
在JB 版本, SurfaceFlinger 的Event Loop 換成了Message Queue-Looper, 對應的backtrace 是:
圖片: surfaceflinger-event-loop-jb.png
 
在KK 版本, SurfaceFlinger 的第一個thread 直接作爲了Event Loop thread, 對應的backtrace 是:
圖片: surfaceflinger-event-loop-kk.png
 
然後再根據SurfaceFlinger 的代碼邏輯,以及backtrace 的lock 情況進行審查分析.
 
4.5 hang module 內部分析 
當您確認了對應的module 後,通常需要拉module owner 來一起cowork.
Module owner 可能有一些獨門祕籍來確認module state。通常情況下,我們可以通過dump coredump/heap dump 的方式來確認每一個module state 的state.
對於java process, 通過hprof-MAT 分析java heap 的情況進行確認.
對於native process, 通過dump coredump gdb-trace32 分析process state.
如果module 已經深入kernel 那麼就必須依賴uart/kdb 來協助, 目前KDump 在客戶版本中沒有開啓。
 
5. UART/KDB Debug

當usb/adb 無法確認問題時或者已經無法連接時,我們需要利用UART/KDB 來進一步釐清問題。
確認UART 以及 UART Console 已經開啓。
ENG 版本默認開啓,User 版本默認關閉,參考相關FAQ。
在hang 機時,所有利用Uart Console 輸入 的命令,務必都帶 &, 轉入background 運行。
原則上在adb shell 中能夠輸入的命令,除無法adb 特有的pull/push 命令,在UART 中一樣都能夠輸入。
如果客戶是ENG 版本,默認已經開啓了KDB, 同時按住volumn UP + Down 15s 即可進入
圖片: enter-kdb.png
 
常見的debug 命令和手法有:
*追查每一個process 的kernel backtrace
** ps; btp <pid> ;  bta ;  btc;
*追查或者更新memory
** md
** mm
*追查更新register
** rd
** rm
* 設置breakproint 進行debug 確認

6. JTAG Debug

如果uart 都無法吐出任何的資訊,此時可能機器HW & BUS 都有完全卡住,需要利用JTAG/CVD 來進行深入的Debug.
常見的分析有:
* 進程狀態以及 Backtrace.
* MMU & CPU register.
* Mmeory 數據以及狀態變化情況.
通常不到萬不得已,一般不會使用JTAG/Debug 來分析。讓客戶接JTAG,併到我司來debug, 代價太大。
當問題涉及到HW 時,就需要大量的owner 進來一起分析,目前還沒有成熟的套路來處理。
 

1. 無場景猜測與分析

前面已經提到,無場景分成兩類
* 已經重啓過的手機
  死機現場已經不復存在, 但手機還沒有刷機, 可以從手機中抓取已經存在的資訊來分析.
  
* 僅僅一些LOG 或者其他的資訊
  需要從這些LOG 或者 資訊中猜測, 當時手機的狀態, 以及可能死機的原因.
  
對於前者, 如同法醫解剖逝者遺體, 分析可能的死亡原因。而對於後者, 就相當於警察叔叔對一些曾年舊案的考證, 挖掘與分析, 難度可想而知.
 
2. 死機資訊的完整紀錄

爲了能夠更好更快的分析死機問題,通常我們都強烈建議客戶保存好現場, 如果一些特殊環境下無法保存現場, 那麼就要求測試工程師詳細的紀錄當時現場環境, 包括但不限於下面的資訊:

* 發現hang 機的時間
--- 如果是發現時,感覺機器早已經hang 機,也請說明
--- 如有截圖,拍照,可以從圖像上先獲取
 
* 復現手法,操作的流程,當時環境
--- 強調在正常使用到hang 機過程中的操作。
--- 環境狀態通常包括溫度,溼度,網絡信號狀況。
--- 復現流程的視頻
 
* 復現手機情況
--- 復現的軟件版本: 版本號? USER/ENG Build ?
--- 外部設備情況:有插入SD 卡?耳機?SIM ?
--- 軟件開啓情況:   開啓藍牙? WIFI?數據服務?GPS?
 
*復現的概率
--- 多少臺手機做過測試,多少臺手機可以復現。
--- 前後多少個版本可以復現,從哪個版本開始可以復現。
 
其中hang 機的時間點至關重要, 需要現場的工程師一定要比較精確的紀錄。 如果測試工程師提一大堆LOG, 有時可能超過1G, 然後說死機, 讓你看, 有的時候真的可能會使得你吐血。 特別是有一些死機情況,普通的mobile log 無法紀錄到的話,你可能分析了大半天,把LOG 看完,發現一無所獲。
 
3. Hang 機Log 分析

* 確認hang 機時間,和當時是否已經hang 機至關重要
* 如果當時kernel 已經異常,mobile log 無法抓取log, 那麼log 的價值大打折扣。
* 反過來如果當時mobile log 還在繼續,說明至少kernel 整體功能正常,先分析android 上層的情況。
* Log 中通常沒有明確的字眼說明已經卡住,如果RD 自己知道已經這樣的情況,那就不是問題了。只能結合多方面的情況來做整體的推理分析。
 
* 從LOG 中分析System Server,  Surfaceflinger 行爲是否正常,是否有Lock 住。
* 查看System Server 中關鍵Service 執行情況, 如AMS, WMS, PowerManagerService, WindowManagerPolicy。
* 觀測AMS 是比較快速的方法,因爲AMS 工作時,會使用到很多其他Service 的Lock, 比如broadcast, start activity, start service. 通常一旦system server 有卡住, 則AMS 也會卡住,反過來如果AMS 沒有卡住, 那麼通常就意味着system server 沒有直接卡住.
* 查看Surfaceflinger, 先查sf-wd, 看看是否surfaceflinger 是否已經卡住,然後再追查fps 情況,HWComposer 等情況.【After ICS2】
 [SF-WD] detect SF maybe hang!!!
* 查看Power Key 行爲來確認上下通路是否正常,一般人看到hang 機,通常會去按一下Power Key 來確認是否hang 住.
* 查看Binder 信息,看看System server, Surfaceflinger 等的IPC 情況。
 
3.1 按Power Key 分析案例.
因爲hang 機,一般人都會習慣性的按Power key 來查看是否可以恢復,而按Power Key 的處理流程,涉及從Kernel => Input System => System Server => SurfaceFlinger 等的整個流程,我們可以觀察這個流程來查看hang 機情況。
* KPD receives Interrupt and generate Power Key
圖片: power-key-1.png

* System server receives Key and call set_screen_state
圖片: power-key-2.png

* Power State Update:  request_suspend_state
圖片: power-key-3.png

* Set screen on:  mt65xx_leds_set_cust
圖片: power-key-4.png
 
通過審查每一個階段流程,確認可能的hang 機點. 注意的是不同的版本可能有所不同,可以先用正常的機器復現一次後比對.
 
3.2 Hang 機Log 分析, trace 輔助

* 如果客戶有提供/data/anr 下的trace,或者相關的db 文件。在確認hang 機的時間下,check trace 對應的時間點,如果時間在hang 機或者hang 機後,則是一份非常有價值的trace.  通常查“-----”or  “pid”
* 藉機審查system server , surfaceflinger 的狀況。
* 如果/data/anr 下有標準full_traces_xx 即標柱這個backtrace 是 SWT 產生的寶貴backtrace, 裏面一定有system server 等的backtrace.

1. 死機資訊抓取
 
通常情況下,依靠Log 可以找到對應的root module,  但無法找出root cause. 需要依賴更多的信息來debug 確認,所以針對這種情況,我們需要讓客戶抓出更多的信息來分析。
 
1. 1. 當機器死機時, 建議使用下面腳本抓取資訊:
@echo "Pull important information from hang phone, written by Yanghui Li"
adb devices

@echo "attach sdcard/mtklog"
adb pull /sdcard/mtklog mtklog/
 
@echo “attach sdcard2/mtklog”
adb pull /sdcard2/mtklog mtklog/sdcard2
 
@echo "attach trace"
adb pull /data/anr mtklog/anr
 
@echo "attach rtt dump for surfaceflinger"
adb pull /data/rtt_dump*  mtklog/sf_dump
adb pull /data/anr/sf_rtt mtklog/sf_rtt_1
 
@echo "attach data aee db"
adb pull /data/aee_exp mtklog/data_aee_exp
 
@echo "attach data mobilelog"
adb pull /data/mobilelog mtklog/data_mobilelog
 
@echo "attach NE core"
adb pull /data/core mtklog/data_core
 
@echo "attach tombstones"
adb pull /data/tombstones mtklog/tombstones
 
@echo “attach phone state”
adb shell ps -t> mtklog/ps.txt
adb shell top -t -m 5 -n 3 > mtklog/top.txt
adb shell service list  >  mtklog/serviceList.txt
adb shell cat /proc/meminfo > mtklog/meminfo
adb shell cat /proc/buddyinfo > mtklog/buddyinfo
adb shell procrank > mtklog/procrank.txt
adb shell cat proc/sched_debug > mtklog/sched_debug.txt
adb shell cat proc/interrupts > mtklog/interrupts.txt
adb shell dumpstate > mtklog/dumpstate.txt

@echo "finish."
pause

1. 2. 當機器重啓後,可以使用下面腳本抓取:
%Writed by Yanghui Li%
adb devices
@echo "抓出sdcard/mtklog"
adb pull /sdcard/mtklog mtklog/
 
@echo “抓出sdcard2/mtklog”
adb pull /sdcard2/mtklog mtklog/sdcard2
 
@echo "抓出trace"
adb pull /data/anr mtklog/anr
 
@echo "抓出data aee db"
adb pull /data/aee_exp mtklog/data_aee_exp
 
@echo "抓出data mobilelog"
adb pull /data/mobilelog mtklog/data_mobilelog
 
@echo "抓出NE core"
adb pull /data/core mtklog/data_core
 
@echo "抓出tombstones"
adb pull /data/tombstones mtklog/tombstones
 
@echo "抓sf rtt"
adb pull /data/rtt_dump* mtklog/sf_rtt
adb pull /data/anr/sf_rtt mtklog/sf_rtt_1
 
@echo "完成"
pause
 
1. 死機還是重啓

應當說針對不同的場景, 死機和重啓意義不一樣.

對於普通用戶來說, 死機的危害性和重要性遠大於重啓。死機意味着機器在很長一段時間內機器都運行不正常,不能使用。危害性僅次於不能開機。重啓很多時候用戶都無法感知到,除非當時正在操作手機,並且通常很快就可以恢復。 所以在USER 版本中,我們要儘可能的把死機轉換成重啓。

對於開發者來說,重要的是解決問題,通常把死機轉換成重啓,爲了規避一些未知因素,通常抓取的資訊比較少,難以直接分析問題。而如果有死機的現場,分析起來就相對容易得多。

所以針對ENG 版本,我們建議不要把死機轉換成重啓。針對USER 版本,儘可能的把死機轉換成重啓。
 
2. 死機如何轉換成重啓
死機轉換成重啓的通用手段就是Watchdog 機制, 通過watchdog 監測系統執行情況, 一旦發生watchdog timeout, 則做相應的異常處理。 通常對於嚴重的情況,就是重啓系統。
 
在android 中有兩個最爲重要的Watchdog 機制, 一個是System Server 內部的Watchdog, 用於監測System Server 重要Thread 是否正常, 關鍵Lock 是否可以快速獲取。一旦出現故障,則快速重啓android 上層。 另外一個就是整個系統的Watchdog, 這個依賴於不同平臺廠商的實現, 對於MTK 平臺來說, 大體的做法是每一個cpu core 對應一個Watchdog Thread [wdtk-X], 週期性(20s)的去寫RGU register, 而RGU register 的timeout 時間是30s, 即如果出現一個core 的watchdog thread 沒有按時踢狗, 那麼就會觸發一個timeout 的FIQ, 產生一個KE 引發系統完整重啓。
 
MTK 在KK 以後的平臺, 有做了hang monitor 機制, system server 的watchdog thread 週期性的踢kernel 中的一個計時器, 並設定下一次踢狗的時間, 如果在設定的時間內沒有踢狗, 即意味着system server 的watchdog 本身可能卡住, 那麼hang monitor 機制就會自己觸發一個HWT, 引發系統完整重啓。
當然並非所有的死機問題都能轉換成重啓, 下面我就對一些常見的cases 說明。
 
3. 死機VS重啓
HW Hang                           [maybe]
IRQ Disable & run long time       [After ICS Reboot, GB Hang]
SoftIRQ run long time             [Reboot]
Preempt Disable long time         [Reboot]
RT Thread busy[RT < 99]           [Hang or Hang long time then reboot]
Kernel Module Hang                [maybe]
---- Not block watchdog thread reboot.
Native Module Hang                [maybe]
---- Not block watchdog thread reboot.
SurfaceFlinger Hang               [GB reboot, ICS/JB Hang, KK Hang long time then reboot] 
Display subsystem Hang            [Hang]
LCM Hang                          [Hang]
Input Driver Fail                 [Hang]
Input System Hang                 [GB, ICS Hang and JB, KK Reboot]
WindowManagerPolicy Hang          [Maybe]
---- Need check the serverthread state
System server deadlock            [Reboot]
System server logic abnormal      [Maybe]
Surfaceflinger logic abnormal     [Maybe]

1. 案例1 ICS 版本輸入法界面拖動死機.

1.1 現象
–.進入輸入法編輯界面;
–.輸入文字,選中輸入的文字內容;3.長按發出振動並拖動文字到輸入法任意位置滑動,然後出現死機現象,需要拔電池才能重啓手機。(訊飛輸入法和谷歌輸入法都高概率出現)
 
1.2 現象初步分析
–從/data/anr 中可看到full trace 信息,說明是system server watchdog 卡住
–按power key 可開啓-關閉屏幕,說明kernel 實際正常,並且WindowManagerPolicy-SurfaceFlinger 正常
–嘗試連接adb,可正常連接。
 
1.3 Java Trace 分析
- Watchdog 最爲關心的就是system server 的ServerThread.
- 閱讀ServerThread 是否被卡住,然後根據依賴關係抓出兇手。
- 如果ServerThread 正常,再閱讀WindowManager, WindowManagerPolicy, ActivityManager, PowerManager 等關鍵thread 是否運行正常。
參考下圖:
 
 
1.4 Native Trace 分析
- 當追查Java backtrace 進入Native code 時,就需要native trace 分析。
- Java Trace 和 Native Trace 的結合部位,關鍵在於Java Trace 上的sysTid, 兩者的sysTid 是標準的linux tid. 而Java Trace 上的tid=xxx, 是在DVM 中分配的tid, 在Native Trace 中不在存在。 
- 當然您也可以根據兩者方法的調用關係來追查,不過如果對該領域不熟悉時,可能產生錯誤的匹配。
參考下圖:
 
- 結合代碼來猜測卡住哪裏,死循環while, for…; lock mutex….. 或者加LOG 確認.

- 一般稍微標準的代碼,lock, mutex 之類不會隨意使用。可review 代碼, 然後加上當前的trace 分析。查看trace 中是否有拿着lock 在幹其他事情的thread, 抓出來, 如下圖:
 

  前一頁對應的source code. android_view_InputQueue.cpp, 如下圖:
 
 
1.5 Input領域分析
- 通過一路的trace 分析,此時我們已經知道ServerThread 最終卡在哪裏了。即因爲lock 的依賴關係卡在了WindowManager 的event consume 中。因爲sem_wait 涉及到semaphore 的處理,從trace 中將比較難以分析。
- 再次閱讀LOG,是否可以從LOG 發現更加有價值的線索。打開相應領域的LOG 開關,獲取更多的信息, 對於重點地方加入相關LOG,釐清流程, 把異常流程和正常流程比較,尋找關鍵點。
如下圖:
- 關鍵LOG 對比分析,如下圖:
 
- 從LOG 對比來看,中途插入了一個event 的reset 動作,這個動作會使得message 中的semaphore 被清零,從而導致sem_wait 一直被卡住。
如下圖:

- 值得說明的是,在我們PC 上進行sem_destroy 測試,並不會使得sem_wait 卡住,而在android 會附帶有清零的動作。
- 爲什麼有這種情況發生。既然已經請求unregister input channel, 爲何此時還會有event 發生呢。Window / Input Manager 的邏輯控制上還有同步問題,應當是遲發了一個event.
- Input 中代碼兼容性問題,既然您把sem 都已經destory 了,也把event message 給destory 了,就意味者這個event 已經不能夠再被consume 了。更多的邏輯分析這裏不再一一深入撰寫了,有興趣者可以研讀代碼。
 
1.6 問題解法
- 修正Window / input Manager 的同步問題,改動比較大,有一定的風險性。
- 提高InputTransport 中的兼容性,簡單易操作,不會有side-effect.
修改方法如下圖:
 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章