1. 程式人生 > >「Android」adb除錯原始碼(針對dumpsys SurfceFlinger、trace.txt獲取)

「Android」adb除錯原始碼(針對dumpsys SurfceFlinger、trace.txt獲取)

首先對ADB作簡單的闡述,接下來對adb shell dumpsys SurfaceFlinger服務的dump資訊的檢視、以及ANR問題如何獲取trace檔案並簡單分析。

-×**************************************************************

目錄:

一、ADB

五、trace分析  

-×*************************************************************

 一、ADB

三、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服務

四、dump SurfaceFlinger的列印資訊分析

  SurfaceFlinger的dump資訊主要通過dumpAllLocked 函式來獲取。

  一般包含:1、layer的資訊,layer一般對應於一個surface;2、opengl的資訊。一般是跟gpu比較相關的引數,opengl是標準的介面;3、display。安卓支援三種類型的display,可以匯出display當前的顯示狀態,也就是各個surface(layer)在各個display的顯示屬性;4、surfaceflinger管理graphis buffer的資訊。主要是layer申請的幀資料記憶體;5、hwcomopser的如果實現dump介面也能知道hwcomposer的一些引數;6、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的大小;

  • crop代表裁剪區域,這點依然是對於桌布很明顯,因為桌布layer大小大於螢幕,必須涉及到需要裁剪一部分顯示在螢幕上,因此它的裁剪區域是crop=( 560, 0,2000,2560);
  • isOpaque代表是否是不透明的,只有完全不透明的layer這個值才是1,比如桌布,像狀態列和launcher他們都是0,代表不是完全不透明;
  • invalidate表示這個layer的資料是失效的,這個值絕大多數情況下都是0.因為我們看到的一般都是繪製好的有效的資料.一種情況下這值特別頻繁的多見為1,就是剛剛鎖屏(解鎖)時.因為突然鎖屏,會導致繪製的內容和要顯示的內容完全不同,導致layer的各種資料要重新計算,所以將layer置為失效;
  • alpha表示了這張layer的透明度,這個值跟isOpaque是有區別的.isOpaque表示了這個layer可以是透明的,也就是沒有顯示資料的地方,可以透明;而alpha表示透明度,也即是有資料的地方也可以因為透明度而收到影響產生透明的效果;
  • flag值含義豐富,它是眾多flag或出來的結果,會a影星layer的狀態;
  • 接下來的一組tr資料代表螢幕的旋轉和縮放程度.大多數的layer實際上是不需要旋轉和縮放的,因為他們定義的大小就是跟螢幕一致的,所以他們的這組資料是[1.00, 0.00][0.00, 1.00],實際上如果你使用這組資料來做矩陣變換的話,矩陣是不會發生變化的.需要旋轉的比較典型的場景是照相機.橫著拿相機時它的layer的變換矩陣是[-1.00, 0.00][-0.00, -1.00],也就是旋轉180°. 這個值的來源是上層呼叫setMatrix函式設定的.
  • client含義比較簡單,值的來源是建立layer時,對應的SurfaceSession中mNativeClient.這東西也是跟SurfaceSession一一對應的,也就是跟SurfaceFlinger連線時一一對應的.從這個值我們可以判斷,client值相同的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]]

 五、trace分析

trace.txt生成:當APP(包括系統APP和使用者APP)程序出現ANR、應用響應慢或WatchDog的監視沒有得到回饋時,系統會dump此時的top程序,程序中Thread的執行狀態就都dump到這個Trace檔案中了。

ANR一般有三種類型:

  1、KeyDispatchTimeout(5 seconds) --主要型別  按鍵或觸控事件在特定時間內無響應

  2、BroadcastTimeout(10 seconds)        BroadcastReceiver在特定時間內無法處理完成

  3、ServiceTimeout(20 seconds) --小概率型別  Service在特定的時間內無法處理完成

---------------------------------------------------------------

 1、adb shell 進入手機的/data/anr檔案目錄下面檢視生成的trace.txt檔案

  如果ls檢視檔案列表沒有許可權,可以先adb  root一下

 2、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被暫停