1. 死機/phong hang 定義
當手機長時間無法再被用戶控制操作時,我們稱爲死機或者hang 機。
在這裏我們強調長時間,如果是短時間,我們歸結爲Perfomance 問題,即性能問題.
2. 死機表現
* 用戶操作手機無任何響應, 如觸摸屏幕,按鍵操作等.
* 手機屏幕黑屏, 無法點亮屏幕.
* 手機界面顯示內容和用戶輸入風馬牛不相及.
當用戶對手機進行操作時, 對應的數據流將是下面一個概括的流程圖.
* 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 中的數據更新顯示出來, 這樣用戶才能感知到他的操作行爲.
原則上上面流程中,每一步出現問題,都可能引發死機問題. 大的方面將,可以分成硬件HW 和 軟件SW 兩個層次, 硬件HW 不在我們的講訴之內.
軟件SW 上,死機的原因可以分成兩種:
(1). 邏輯行爲異常
** 邏輯判斷錯誤
** 邏輯設計錯誤
(2). 邏輯卡頓(block)
* 死循環 (Deadloop)
* 死鎖 (Deadlock)
從具體的原因上將,可以進一步分成:
(1). Input Driver
* 無法接收HW 的中ISR,產生原始的InputEvent, 或者產生的InputEvent 異常.
* 無法監聽Kernel 傳遞上來的原始InputEvent, 或者轉換與傳遞異常.
* 無法正常響應Input System 傳遞過來的InputEvent, 或者響應出錯.
* WMS/SF 無法正確的對Z-Window 進行疊加轉換
* 無法更新Framebuffer 數據,或者填充的數據錯誤
* 無法將Framebuffer 數據顯示在LCM 上
* Power
* Clock
* Memory & Memory Controller
* Fail IC
* 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 的數據等。
從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
* 在ENG Build 中
adb remount
adb shell chmod 0777 data/anr
adb shell kill -3 pid
adb pull /data/anr
沒有root 權限的情況下,只能直接pull 已經存在的backtrace.
adb pull /data/anr
adb remount
set processid=
set /p processid=Please Input process id:
adb shell kill -3 %processid%
ping -n 8 127.0.0.1>nul
adb pull data/anr/traces.txt trace-%processid%.txt
下面是一小段system server 的java backtrace 的開始
----- pid 682 at 2014-07-30 18:04:53 -----
Cmd line: system_server
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
| 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
tll: threadListLock,
tsl: threadSuspendLock,
tscl: threadSuspendCountLock
ghl: gcHeapLock
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"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
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
* #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.
* #7-more Call Stack
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)
"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)
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.
你可以使用GDB, 或者addr2line 等 tool 來解析抓回的Native Backtrace, 從而知道當時正在執行的native 代碼.
如addr2line 執行
arm-linux-androideabi-addr2line -f -C -e symbols address
* AEE/RTT 工具
cat proc/pid/task/tid/stack
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
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
當前thread: dump_stack();
其他thread: show_stack(task, NULL);
"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 */
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)
"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>]
客觀的反應系統的執行環境,通常包括如CPU 利用率,Memory 使用情況, Storage 剩餘情況等。這些資料也非常重要,比如可以快速的知道,當時是否有Process 在瘋狂的執行,當時是不是處於嚴重的low memory 情況, Storage 是否有耗盡的情況發生等。
追查CPU 利用率可大體的知道,當時機器是否有Process 在瘋狂的運行, 當時系統運行是否繁忙。通常死機分析,只需要抓取基本的使用情況即可。下面說一下一般的抓取方式
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
User 14 + Nice 0 + Sys 67 + Idle 471 + IOW 0 + IRQ 0 + SIRQ 2 = 554
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 10 + Nice 0 + Sys 41 + Idle 494 + IOW 0 + IRQ 0 + SIRQ 0 = 545
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 的進程.
User 1428 + Nice 0 + Sys 110 + Idle 811 + IOW 67 + IRQ 0 + SIRQ 1 = 2417
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 1391 + Nice 0 + Sys 75 + Idle 435 + IOW 146 + IRQ 0 + SIRQ 1 = 2048
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
| 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)
| 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)
| 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)
| 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)
通常用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.
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.
ftrace 可以紀錄CPU 最爲詳細的執行情況, 即linux scheduler 的執行情況. 通常默認只開啓 sched_switch.
如何抓取ftrace 可以查詢相關的FAQ.
有的時候我們需要追查一下,當時Kernel 的基本調度情況,以及接收中斷的情況,以判斷當前CPU 執行的基本情況是否異常。比如有時候如果某個中斷上來太過頻繁,就容易導致系統運行緩慢,甚至死機。
* CPU Sched status
adb shell cat proc/sched_debug
Use sysrq-trigger
adb shell cat proc/interrupts
adb shell cat proc/irq/irq_id/spurious
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
查看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
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 .
當我們懷疑死機問題可能是某個進程出現問題而引發時,通常我們需要對這個進程進行深入的分析, 即進程運行環境分析。通常包括分析如,線程狀態,各種變量值,寄存器狀態等。在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
analysis java thread information
analysis java var value
analysis Object reference
analysis GC path
具體如何使用MAT 分析可以參考MAT 的官方網站.
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 轉換成了AEE DB. 所以首先將AEE DB 解開, 即可以看到PROCESS_COREDUMP 的文件,有的時候此文件很大, 比如超過1G.
而分析Core Dump 的Tools 很多, 比如traces32, GDB 等,這裏就不詳加說明,可以參考網絡上的相關文檔.
從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異常重啓的分析.
死機分析如同醫生給病人診斷病情,所有的手法不在乎分爲兩種。
* 高科技的診斷設備,分析病人的病情。死機分析 就依靠各種技術方法去診斷當時手機的運行的真實狀態。
* 通過病人的各種活動,分析病人的潛在病因。 就依靠各種對手機的操作,以及命令,讓手機跑相應的流程,從而進一步分析。
* 通過做一些基本的測試, 可大體上確認可能引發死機的模塊, 爲後續Debug 確定好方向。
* 通常用於QA 發現hang 機時,或者讓客戶進行初步的確認。
* 通常只能正向推理,可以做什麼 =》推斷什麼模塊正常;逆向推理可能因爲各種原因而失常。
- 屏幕是否有響應(一般情況下沒響應 ^_^).
- 如果有響應,可能機器已經活過來了,或者客戶當時把ANR 認爲了hang 機;需要進一步和客戶確認情況.
- 確認虛擬按鍵的情況,通常都設定振動反饋, 如果有, 那麼就認爲當時按鍵資訊可以傳遞到System Server, 此時可能System Server 邏輯異常.
* Power Key/ Volumn Up/Down Key
- 是否可以點亮,關閉屏幕(注意最好關燈測試,不然背光可能觀測不到)
- 可點亮關閉屏幕,說明KPD ->input manager->PowerManagerService->Power->LCD driver 正常;通常可以懷疑TPD, 以及SurfaceFlinger.
- 可顯示音量調整情況,進一步說明SurfaceFlinger 也正常,進一步懷疑TPD,或者直接的APP 無響應的情況。
- Status Bar 是否可以拉下,以便防止只是活動區卡住的情況, 可下拉,說明只是APP 卡住,或者lockscreen 無法解鎖的清理。
- 測試LockScreen 是否可以解鎖,
- 測試modem 是否已經關閉
- 可進入電話界面,說明是lockscreen 無法解鎖的情況
* Home Key/ Back Key/Search Key
- 確認當時是否只是APP hang 住的情況,避免將ANR 誤判爲hang 機
- 可確認Surfaceflinger, System Server 的運行情況
- 首先查看windows 的設備管理器裏面是否出現對應的設備
- 在命令行中輸入adb devices, 看是否可以打印設備信息,在輸入之前您最好先輸入adb kill-server 保證pc 上的adb client 沒有卡住
- 請確保您使用的PC 上已經安裝ADB,USB 端口本身正常
* 正常的ADB 連接情況如下圖
* 正常adb devices 查看結果如下圖
4.2 USB/ADB 連接分析
- 恭喜您adb 目前可以正常接入,可以進行USB/ADB Debug
- 確認adb 驅動是否安裝好,USB 的uid 和 vid 是否和driver 匹配。
- 可能機器的adbd 已經無法對接,嘗試adb kill-server 來確認
- 機器的adbd 拒絕連接,或者adbd 無法拉起。
- PC adb 版本過低
- 很可能adbd 已經卡住, adbd process status 爲 ‘D’
- 需要手機確認PC RSA 指紋
* 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 pid, sf pid,在ICS 上如果機器上層沒有重啓過,通常sf pid < 200, ss pid < 600,如果pid 比較大就說明上層重啓過。
** 是否還存在特別進程,如ipod, 表示在ipo 關機中,如aee_core_forworder, 表示在抓core dump,aee_dumpstate 表示aee 在抓取db 信息等。
- 審查storage 的使用情況,查看SD 卡和 data 分區使用情況,特別是如SD 卡已滿,或者data 分區寫滿等。
- 審查當前的memory 使用情況, 追查各個進程的memory 情況
- 審查當前system properties 情況
- 摸一摸手機,感覺有點熱,或者發燙的話,說明通常是CPU 利用率比較高
- 大體上審查當前CPU 的利用情況,不求精湛
總之,機器狀態基本檢測,目標就是通過簡單幾個命令直接偵測當前手機最爲可能的異常情況。包括關鍵進程基本狀態, CPU 利用率,memory 狀況,storage 狀況等。做出基本的預先分析,從而爲下一步的debug 打好基礎。
* 確認Input Driver-Input System 通路是否正常,即input driver 是否可以傳上正常的輸入
* 最常見的檢測方式是 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
比如我們追查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
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
(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, 機器死機就成必然.
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.
Java:
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:138)
at android.os.Looper.loop(Looper.java:150)
#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)
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)
當發現java backtrace 最後鑽入到異常native method時,抓取其native backtace,通過native backtrace 進一步追查。
如果在native backtrace 中,發現已經call 入binder driver, 那就是通過binder 進行IPC call, 這個時候就要知道binder 的對端process, 然後查閱它的binder thread 進程進一步釐清問題.
圖片deadlock-java-backtrace.png
圖片lock-in-native-thread.png
圖片lock-in-binder.png
圖片lock-in-binder-thread.png
WindowManagerService通過InputManager提供的接口開啓一個線程驅動InputReader不斷地從/dev/input /目錄下面的設備文件讀取事件,然後通過InputDispatcher分發給連接到WindowManagerService服務的客戶端。
"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)
"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 時,恰好運行到,造成烏龍的情況呢? 這個通常需要具體情況具體分析
--- Native Backtrace 處於R 狀態
--- Java Backtrace 處於 Suspend 狀態, Running 狀態
--- Backtrace 處於明確的非block 狀態
--- Double Backtrace 確認是否CPU 有運轉
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 直接退出
圖片: display-control-flow.png
** Surfaceflinger 是用戶空間中ramework下libraries 中負責顯示相關的一個模塊
** 當系統同時執行多個應用程序時,Surfaceflinger 負責管理顯示,主要包括顯示的疊加和顯示的渲染
** 其中顯示的疊加,MTK 架構按照一定的原則和算法,主要用到GPU 和 OVL 模塊的overlay 功能來做2D/3D 繪圖的顯示合成
** 基於DDP OVL 硬件做HW Overlay 功能
** aspecialized electronic circuit designed to accelerate the image processing, frame buffer rendering for output to a display.
** 基於Linux Frame buffer 模型的MTK Framebuffer 實作,主要來管理frame buffer。
** MTK Display Hardware Controller,包括OVL, DMA, Color Processor,BLS, DSI/DBI/DPI Controller
圖片: display-data-flow.jpg
** 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 顯示相關的畫面
* 確認FrameBuffer 數據和理想數據一致, 那麼說明android 上層的處理行爲都是正常的, 而問題就很可能出在LCM 的driver 或者 LCM 本身問題, 此時需要聯繫LCM 的driver 工程師以及 LCM vendor 來確認分析, 這裏不再詳細說明.
[SF-WD] detect SF maybe hang!!!
這樣的LOG, 並且會紀錄卡頓的時機,如果持續卡頓,毫無疑問, SurfaceFlinger 已經卡住,此時就要分析對應的backtrace 以及 LOG.
* SurfaceFlinger 的線程情況在不同的版本上變化比較大, 最直接的方式是審查它binder thread 情況, 以及Event Loop 的執行情況. binder thread 和 system server 情況一致, 不再詳細說明. Event Loop 在JB 以及, JB 以及KK 上都不相同, 這裏分別說明.
圖片: surfaceflinger-event-loop-beforce-jb.png
圖片: surfaceflinger-event-loop-jb.png
圖片: surfaceflinger-event-loop-kk.png
當您確認了對應的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 在客戶版本中沒有開啓。
當usb/adb 無法確認問題時或者已經無法連接時,我們需要利用UART/KDB 來進一步釐清問題。
確認UART 以及 UART Console 已經開啓。
ENG 版本默認開啓,User 版本默認關閉,參考相關FAQ。
在hang 機時,所有利用Uart Console 輸入 的命令,務必都帶 &, 轉入background 運行。
原則上在adb shell 中能夠輸入的命令,除無法adb 特有的pull/push 命令,在UART 中一樣都能夠輸入。
圖片: enter-kdb.png
*追查每一個process 的kernel backtrace
** ps; btp <pid> ; bta ; btc;
** md
** mm
** rd
** rm
6. JTAG Debug
如果uart 都無法吐出任何的資訊,此時可能機器HW & BUS 都有完全卡住,需要利用JTAG/CVD 來進行深入的Debug.
常見的分析有:
* 進程狀態以及 Backtrace.
* MMU & CPU register.
* Mmeory 數據以及狀態變化情況.
當問題涉及到HW 時,就需要大量的owner 進來一起分析,目前還沒有成熟的套路來處理。
前面已經提到,無場景分成兩類
* 已經重啓過的手機
死機現場已經不復存在, 但手機還沒有刷機, 可以從手機中抓取已經存在的資訊來分析.
* 僅僅一些LOG 或者其他的資訊
需要從這些LOG 或者 資訊中猜測, 當時手機的狀態, 以及可能死機的原因.
對於前者, 如同法醫解剖逝者遺體, 分析可能的死亡原因。而對於後者, 就相當於警察叔叔對一些曾年舊案的考證, 挖掘與分析, 難度可想而知.
爲了能夠更好更快的分析死機問題,通常我們都強烈建議客戶保存好現場, 如果一些特殊環境下無法保存現場, 那麼就要求測試工程師詳細的紀錄當時現場環境, 包括但不限於下面的資訊:
* 發現hang 機的時間
--- 如果是發現時,感覺機器早已經hang 機,也請說明
--- 如有截圖,拍照,可以從圖像上先獲取
--- 強調在正常使用到hang 機過程中的操作。
--- 環境狀態通常包括溫度,溼度,網絡信號狀況。
--- 復現流程的視頻
--- 復現的軟件版本: 版本號? USER/ENG Build ?
--- 外部設備情況:有插入SD 卡?耳機?SIM ?
--- 軟件開啓情況: 開啓藍牙? WIFI?數據服務?GPS?
--- 多少臺手機做過測試,多少臺手機可以復現。
--- 前後多少個版本可以復現,從哪個版本開始可以復現。
* 確認hang 機時間,和當時是否已經hang 機至關重要
* 如果當時kernel 已經異常,mobile log 無法抓取log, 那麼log 的價值大打折扣。
* 反過來如果當時mobile log 還在繼續,說明至少kernel 整體功能正常,先分析android 上層的情況。
* Log 中通常沒有明確的字眼說明已經卡住,如果RD 自己知道已經這樣的情況,那就不是問題了。只能結合多方面的情況來做整體的推理分析。
* 查看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 情況。
因爲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
* 如果客戶有提供/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.
@echo "Pull important information from hang phone, written by Yanghui Li"
adb devices
@echo "attach sdcard/mtklog"
adb pull /sdcard/mtklog mtklog/
adb pull /sdcard2/mtklog mtklog/sdcard2
adb pull /data/anr mtklog/anr
adb pull /data/rtt_dump* mtklog/sf_dump
adb pull /data/anr/sf_rtt mtklog/sf_rtt_1
adb pull /data/aee_exp mtklog/data_aee_exp
adb pull /data/mobilelog mtklog/data_mobilelog
adb pull /data/core mtklog/data_core
adb pull /data/tombstones mtklog/tombstones
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."
1. 2. 當機器重啓後,可以使用下面腳本抓取:
%Writed by Yanghui Li%
adb pull /sdcard/mtklog mtklog/
adb pull /sdcard2/mtklog mtklog/sdcard2
adb pull /data/anr mtklog/anr
adb pull /data/aee_exp mtklog/data_aee_exp
adb pull /data/mobilelog mtklog/data_mobilelog
adb pull /data/core mtklog/data_core
adb pull /data/tombstones mtklog/tombstones
adb pull /data/rtt_dump* mtklog/sf_rtt
adb pull /data/anr/sf_rtt mtklog/sf_rtt_1
應當說針對不同的場景, 死機和重啓意義不一樣.
對於普通用戶來說, 死機的危害性和重要性遠大於重啓。死機意味着機器在很長一段時間內機器都運行不正常,不能使用。危害性僅次於不能開機。重啓很多時候用戶都無法感知到,除非當時正在操作手機,並且通常很快就可以恢復。 所以在USER 版本中,我們要儘可能的把死機轉換成重啓。
對於開發者來說,重要的是解決問題,通常把死機轉換成重啓,爲了規避一些未知因素,通常抓取的資訊比較少,難以直接分析問題。而如果有死機的現場,分析起來就相對容易得多。
所以針對ENG 版本,我們建議不要把死機轉換成重啓。針對USER 版本,儘可能的把死機轉換成重啓。
死機轉換成重啓的通用手段就是Watchdog 機制, 通過watchdog 監測系統執行情況, 一旦發生watchdog timeout, 則做相應的異常處理。 通常對於嚴重的情況,就是重啓系統。
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 現象
- Watchdog 最爲關心的就是system server 的ServerThread.
- 閱讀ServerThread 是否被卡住,然後根據依賴關係抓出兇手。
- 如果ServerThread 正常,再閱讀WindowManager, WindowManagerPolicy, ActivityManager, PowerManager 等關鍵thread 是否運行正常。
- 當追查Java backtrace 進入Native code 時,就需要native trace 分析。
- Java Trace 和 Native Trace 的結合部位,關鍵在於Java Trace 上的sysTid, 兩者的sysTid 是標準的linux tid. 而Java Trace 上的tid=xxx, 是在DVM 中分配的tid, 在Native Trace 中不在存在。
- 當然您也可以根據兩者方法的調用關係來追查,不過如果對該領域不熟悉時,可能產生錯誤的匹配。
- 一般稍微標準的代碼,lock, mutex 之類不會隨意使用。可review 代碼, 然後加上當前的trace 分析。查看trace 中是否有拿着lock 在幹其他事情的thread, 抓出來, 如下圖:
前一頁對應的source code. android_view_InputQueue.cpp, 如下圖:
- 通過一路的trace 分析,此時我們已經知道ServerThread 最終卡在哪裏了。即因爲lock 的依賴關係卡在了WindowManager 的event consume 中。因爲sem_wait 涉及到semaphore 的處理,從trace 中將比較難以分析。
- 再次閱讀LOG,是否可以從LOG 發現更加有價值的線索。打開相應領域的LOG 開關,獲取更多的信息, 對於重點地方加入相關LOG,釐清流程, 把異常流程和正常流程比較,尋找關鍵點。
如下圖:
如下圖:
- 值得說明的是,在我們PC 上進行sem_destroy 測試,並不會使得sem_wait 卡住,而在android 會附帶有清零的動作。
- Input 中代碼兼容性問題,既然您把sem 都已經destory 了,也把event message 給destory 了,就意味者這個event 已經不能夠再被consume 了。更多的邏輯分析這裏不再一一深入撰寫了,有興趣者可以研讀代碼。
- 修正Window / input Manager 的同步問題,改動比較大,有一定的風險性。
- 提高InputTransport 中的兼容性,簡單易操作,不會有side-effect.
修改方法如下圖: