首先對ADB做簡單的闡述,接下來對adb shell dumpsys SurfaceFlinger服務的dump信息的查看、以及ANR問題如何獲取trace文件並簡單分析。java
-×**************************************************************linux
目錄:android
1、ADBweb
shell
數據庫
緩存
網絡
app
composer
5、trace分析
-×*************************************************************
1、ADB
(1)
3、dumpsys使用
(1)adb shell 進入shell
(2)dumpsys -l 查看全部正在運行的服務名
service list 查看這些服務名稱調用了哪一個服務
下面列舉了其中一些服務名:
服務名 | 類名 | 功能 |
---|---|---|
activity | ActivityManagerService | AMS相關信息 |
package | PackageManagerService | PMS相關信息 |
window | WindowManagerService | WMS相關信息 |
input | InputManagerService | IMS相關信息 |
power | PowerManagerService | PMS相關信息 |
batterystats | BatterystatsService | 電池統計信息 |
battery | BatteryService | 電池信息 |
alarm | AlarmManagerService | 鬧鐘信息 |
dropbox | DropboxManagerService | 調試相關 |
procstats | ProcessStatsService | 進程統計 |
cpuinfo | CpuBinder | CPU |
meminfo | MemBinder | 內存 |
gfxinfo | GraphicsBinder | 圖像 |
dbinfo | DbBinder | 數據庫 |
服務名 | 功能 |
---|---|
SurfaceFlinger | 圖像相關 |
appops | app使用狀況 |
permission | 權限 |
processinfo | 進程服務 |
batteryproperties | 電池相關 |
audio | 查看聲音信息 |
netstats | 查看網絡統計信息 |
diskstats | 查看空間free狀態 |
jobscheduler | 查看任務計劃 |
wifi | wifi信息 |
diskstats | 磁盤狀況 |
usagestats | 用戶使用狀況 |
devicestoragemonitor | 設備信息 |
(3)dumpsys <service>
打印具體某一項服務(service就是前面表格中的服務名)
dumpsys cpuinfo //打印一段時間進程的CPU使用百分比排行榜 dumpsys meminfo -h //查看dump內存的幫助信息 dumpsys package <packagename> //查看指定包的信息 dumpsys SurfaceFLinger //查看SF服務
4、dump SurfaceFlinger的打印信息分析
SurfaceFlinger的dump信息主要經過dumpAllLocked 函數來獲取。
通常包含:
一、layer的信息,layer通常對應於一個surface;
二、opengl的信息。通常是跟gpu比較相關的參數,opengl是標準的接口;
三、display。安卓支持三種類型的display,能夠導出display當前的顯示狀態,也就是各個surface(layer)在各個display的顯示屬性;
四、surfaceflinger管理graphis buffer的信息。主要是layer申請的幀數據內存;
五、hwcomopser的若是實現dump接口也能知道hwcomposer的一些參數;
六、gralloc的內存分配信息。若是gralloc有實現dump接口的話;
(1)特殊宏的打開
Build configuration: [sf] [libui] [libgui]
(2)打印目前正在使用的Sync機制
Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]
(3)打印Layer
Visible layers (count = 9)
count的值來源於layersSortedByZ中layer的數量,接下來就進入各個layer的dump。
例如:
> 0xb3f92000指向當前layer對象的值,括號中是當前layer的名稱,id是建立layer時產生的序列號:
+ Layer 0xb3f92000 (com.sec.android.app.launcher/com.android.launcher2.Launcher) id=87
> 區域信息,兩段是兩個Region的dump,每一個region可能包含多個區域,因此這裏count也可能不等於1,
前兩行的值來源於activeTransparentRegion,表示的是這個layer裏面透明區域的大小,
後兩行值來源於visibleRegion,表示可見區域的大小:
Region transparentRegion (this=0xb3f92164, count=1) [ 0, 0, 0, 0] Region visibleRegion (this=0xb3f92008, count=1) [ 0, 0, 1440, 2560]
> 基本信息:
layerStack= 0, z= 21010, pos=(0,0), size=(1440,2560), crop=(0, 0,1440,2560), isOpaque=0, invalidate=0, alpha=0xff, flags=0x00000000, tr=[1.00, 0.00][0.00, 1.00] client=0xb11160c0
對應的dumpAllLock中的源碼:
result.appendFormat( "layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), crop=(%4d,%4d,%4d,%4d), " "isOpaque=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n" " client=%p\n", s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h, s.active.crop.left, s.active.crop.top, s.active.crop.right, s.active.crop.bottom, isOpaque(s), contentDirty, s.alpha, s.flags, s.transform[0][0], s.transform[0][1], s.transform[1][0], s.transform[1][1], client.get());
layerStack表示這個layer是保存在哪一個layerstack中(不一樣的display是有不一樣的layerstack的);
z表示Z軸座標,z值越大,layer越靠上;
pos的值是layer左上角的位置,這個值比較特殊的是ImageWallpaper這個layer的pos值,由於ImageWallpaper的大小大於屏幕大小,因此ImageWallpaper的pos值在屏幕的外面(note4是pos=(-560,0)).
size天然是layer的大小;
(4)打印Displays信息
首先會打印當前display的數量,數量基於mDisplays的大小,這個容器在SurfaceFlinger初始化時會生成數據,後面根據收到不一樣的消息在handleTransactionLocked函數中也會調整.
正常狀況下是1,也就是隻有一個display(Built-in Screen),當設備鏈接了HDMI或者使用了屏幕共享等功能時,會有額外的display加入。
Displays (2 entries) //這個是鏈接了HDMI後的數據 + DisplayDevice: HDMI Screen type=1, hwcId=1, layerStack=6, (1920x1080), ANativeWindow=0xb4d94d08, orient= 0 (type=00000000), flips=1173, isSecure=1, secureVis=0, powerMode=2, activeConfig=0, numLayers=1 v:[0,0,1920,1080], f:[0,0,1920,1080], s:[0,0,1920,1080],transform:[[1.000,0.000,-0.000][0.000,1.000,-0.000][0.000,0.000,1.000]] mAbandoned=0 -BufferQueue mMaxAcquiredBufferCount=2, mDequeueBufferCannotBlock=0, default-size=[1920x1080], default-format=1, transform-hint=00, FIFO(0)={} [00:0xb6418c80] state=FREE , 0xb43ed880 [1920x1080:1920, 1] [01:0xb43cb300] state=FREE , 0xb640d970 [1920x1080:1920, 1] >[02:0xb43cb280] state=ACQUIRED, 0xb43ed830 [1920x1080:1920, 1] + DisplayDevice: Built-in Screen //DisplayDevice是設備的名字,這個能夠調用接口設置,可是比較常見的值一般有:Built-in Screen,HDMI Screen,Virtual Screen,wfdservice等等 type=0, hwcId=0, layerStack=0, (1080x1920), ANativeWindow=0xb4d94608, orient= 0 (type=00000000), flips=3140, isSecure=1, secureVis=0, powerMode=2, activeConfig=0, numLayers=2 v:[0,0,1080,1920], f:[0,0,1080,1920], s:[0,0,1080,1920],transform:[[1.000,0.000,-0.000][0.000,1.000,-0.000][0.000,0.000,1.000]]
5、trace分析
trace.txt生成:當APP(包括系統APP和用戶APP)進程出現ANR、應用響應慢或WatchDog的監視沒有獲得回饋時,系統會dump此時的top進程,進程中Thread的運行狀態就都dump到這個Trace文件中了。
ANR通常有三種類型:
一、KeyDispatchTimeout(5 seconds) --主要類型 按鍵或觸摸事件在特定時間內無響應
二、BroadcastTimeout(10 seconds) BroadcastReceiver在特定時間內沒法處理完成
三、ServiceTimeout(20 seconds) --小几率類型 Service在特定的時間內沒法處理完成
---------------------------------------------------------------
一、adb shell 進入手機的/data/anr文件目錄下面查看生成的trace.txt文件
若是ls查看文件列表沒有權限,能夠先adb root一下
二、adb pull /data/ ./ 將該文件導出,而後分析
---------------------------------------------------------------
log打印了ANR的基本信息(adb shell top查看cg進程,adb logcat -v process |grep PID查看日誌),
能夠分析CPU使用率得知ANR的簡單狀況;若是CPU使用率很高,接近100%,多是在進行大規模的計算更多是陷入死循環;若是CUP使用率很低,說明主線程被阻塞了,而且當IOwait很高,多是主線程在等待I/O操做的完成。
對於ANR只是分析Log很難知道問題所在,咱們還須要經過Trace文件分析stack調用狀況,在log中顯示的pid在traces文件中與之對應,而後經過查看堆棧調用信息分析ANR的代碼:
(此處trace的分析參考 https://blog.csdn.net/qq_25804863/article/details/49111005 )
----- pid 17027 at 2017-06-22 10:37:39 ----- // ANR出現的進程pid和時間 Cmd line: org.code:MessengerService // ANR出現的進程名(或者進程包名) Build fingerprint: 'Homecare/qucii8976v3_64:6.0.1/pansen06141150:eng/test-keys' // 下面記錄系統版本,內存等狀態信息 ABI: 'arm64' Build type: optimized Zygote loaded classes=6576 post zygote classes=13 Intern table: 13780 strong; 17 weak JNI: CheckJNI is on; globals=283 (plus 158 weak) Libraries: /system/lib64/libandroid.so /system/lib64/libcompiler_rt.so /system/lib64/libjavacrypto.so /system/lib64/libjnigraphics.so /system/lib64/libmedia_jni.so /system/lib64/libwebviewchromium_loader.so libjavacore.so (7) Heap: 29% free, 8MB/12MB; 75731 objects Dumping cumulative Gc timings Total number of allocations 75731 Total bytes allocated 8MB Total bytes freed 0B Free memory 3MB Free memory until GC 3MB Free memory until OOME 183MB Total memory 12MB Max memory 192MB Zygote space size 3MB Total mutator paused time: 0 Total time waiting for GC to complete: 0 Total GC count: 0 Total GC time: 0 Total blocking GC count: 0 Total blocking GC time: 0 suspend all histogram: Sum: 76us 99% C.I. 0.100us-28us Avg: 7.600us Max: 28us DALVIK THREADS (15): // Signal Catcher是記錄traces信息的線程 // Signal Catche(線程名)、(daemon)表示守護進程、prio(線程優先級,默認是5)、tid(線程惟一標識ID)、Runnable(線程當前狀態) "Signal Catcher" daemon prio=5 tid=3 Runnable //線程組名稱、suspendCount、debugSuspendCount、線程的Java對象地址、線程的Native對象地址 | group="system" sCount=0 dsCount=0 obj=0x12d8f0a0 self=0x5598ae55d0 //sysTid是線程號(主線程的線程號和進程號相同) | sysTid=17033 nice=0 cgrp=default sched=0/0 handle=0x7fb2350450 | state=R schedstat=( 4348125 172343 3 ) utm=0 stm=0 core=1 HZ=100 | stack=0x7fb2254000-0x7fb2256000 stackSize=1013KB | held mutexes= "mutator lock"(shared held) native: #00 pc 0000000000489e28 /system/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, char const*, art::ArtMethod*, void*)+236) native: #01 pc 0000000000458fe8 /system/lib64/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&) const+220) native: #02 pc 0000000000465bc8 /system/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+688) native: #03 pc 0000000000466ae0 /system/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*)+276) native: #04 pc 000000000046719c /system/lib64/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+188) native: #05 pc 0000000000467a84 /system/lib64/libart.so (art::ThreadList::DumpForSigQuit(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+492) native: #06 pc 0000000000431194 /system/lib64/libart.so (art::Runtime::DumpForSigQuit(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+96) native: #07 pc 000000000043e604 /system/lib64/libart.so (art::SignalCatcher::HandleSigQuit()+1256) native: #08 pc 000000000043f214 /system/lib64/libart.so (art::SignalCatcher::Run(void*)+452) native: #09 pc 0000000000068714 /system/lib64/libc.so (__pthread_start(void*)+52) native: #10 pc 000000000001c604 /system/lib64/libc.so (__start_thread+16) (no managed stack frames) //main(線程名)、prio(線程優先級,默認是5)、tid(線程惟一標識ID)、Sleeping(線程當前狀態) "main" prio=5 tid=1 Sleeping | group="main" sCount=1 dsCount=0 obj=0x73132d10 self=0x5598a5f5e0 //sysTid是線程號(主線程的線程號和進程號相同) | sysTid=17027 nice=0 cgrp=default sched=0/0 handle=0x7fb6db6fe8 | state=S schedstat=( 420582038 5862546 143 ) utm=24 stm=18 core=6 HZ=100 | stack=0x7fefba3000-0x7fefba5000 stackSize=8MB | held mutexes= // java 堆棧調用信息(這裏可查看致使ANR的代碼調用流程)(分析ANR最重要的信息) at java.lang.Thread.sleep!(Native method) - sleeping on <0x0c60f3c7> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:1031) - locked <0x0c60f3c7> (a java.lang.Object) // 鎖住對象0x0c60f3c7 at java.lang.Thread.sleep(Thread.java:985) at android.os.SystemClock.sleep(SystemClock.java:120) at org.code.ipc.MessengerService.onCreate(MessengerService.java:63) //致使ANR的代碼 at android.app.ActivityThread.handleCreateService(ActivityThread.java:2877) at android.app.ActivityThread.access$1900(ActivityThread.java:150) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke!(Native method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Traces中顯示的線程狀態都是C代碼定義的.咱們能夠經過查看線程狀態對應的信息分析ANR問題
如: TimedWaiting對應的線程狀態是TIMED_WAITING
kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout 執行了無超時參數的wait函數
kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep() 執行了帶有超時參數的sleep函數
ZOMBIE 線程死亡,終止運行RUNNING/RUNNABLE 線程可運行或正在運行TIMED_WAIT 執行了帶有超時參數的wait、sleep或join函數MONITOR 線程阻塞,等待獲取對象鎖WAIT 執行了無超時參數的wait函數INITIALIZING 新建,正在初始化,爲其分配資源STARTING 新建,正在啓動NATIVE 正在執行JNI本地函數VMWAIT 正在等待VM資源SUSPENDED 線程暫停,一般是因爲GC或debug被暫停