android traceview性能調試

Traceview和dmtracedump分析工具

Traceview是查看程序運行時log的圖形化查看工具,在代碼中用Debug類記錄跟蹤信息並生成log文件。用Traceview工具可以幫助調試程序和分析程序性能。

Traceview的界面佈局

如果你有一份Trace log文件(可以在程序中添加跟蹤代碼生成,也可以由ddms生成),那麼就可以用Traceview載入log文件,Traceview會在窗口中用兩個面板來形象化
的展示程序相關信息:
  • 時間線面板    ----    描述每個線程和方法何時起止
  • 分析面板        ----    提供每個方法的情況概要

時間線面板

下圖是時間軸面板的一個特寫。每一行顯示一個線程的運行情況,時間從左到右增加。每一個方法用一種顏色表示(從表示最長時間段的那個顏色開始,顏色會被循環重用,注:待考)。第一行下面的細線指示了,對選中方法的所有的調用的時間範圍(進入到退出),圖片所示的方法是LoadListener.nativeFinished(),這個方法在分析視圖中被選中。
Traceview timeline panel
圖一,Traceview的時間線面板

分析面板

 圖二顯示的是分析面板,方法中所有時間消耗的一個概要。該表同時顯示inclusive 時間和exclusive時間(用與總時間的百分比表示)。Exclusive時間是方法本身消耗的時間,Inclusive時間是方法本身消耗的時間,再加上方法中所調用的其他任何方法所消耗的時間之和。我們稱調用方法爲“parents”,被調用的方法稱爲“children”。當一個方法被選中時(單擊就會選中),它會展開顯示“parents”和“children”。“parents”用紫色背景顯示,“children”用黃色背景顯示。表的最後一列顯示對本方法的調用數量加上遞歸調用數量。最後一欄顯示對那個方法的調用總數中的調用數量(注:待考)由圖中,我們可以看到對LoadListener.nativeFinished()方法有14次調用;同時看時間線視圖,會發現這些調用中有一個佔用了異常長的時間。
Traceview profile panel.
圖二,Traceview的分析面板

Traceview文件格式

Tracing創建了輸出中兩個截然不同的部分:一個data文件,包含了跟蹤數據,另外還有一個key文件,提供二進制標識符到線程和方法名字的映射。當Tracing結束時,會將它們串聯爲單個.trace文件。

注意:之前版本的Traceview沒有自動串聯這些文件,如果你有舊的key和data文件需要繼續跟蹤,你可以手動串聯,用如下命令:
cat mytrace.key mytrace.data > mytrace.trace

data文件格式

data文件是二進制格式的,結構如下(所有值都以little-endian順序存儲)
注:具體內容就不翻譯了,不影響我們使用這個工具

key文件格式

key文件是分爲三部分的純文本文件,每一部分都用星號“*”開頭,如果你在行首看到了星號“*”字符,那麼恭喜你,你找到了一個新部分的開頭了。
注:文件格式的詳細內容不翻譯,不影響使用

創建跟蹤文件

要使用Traceview,你必須要生成包含你想要分析的跟蹤信息的log文件。

有兩種方法來生成跟蹤log:
  • 在代碼中,使用Debug類,調用這個類的方法來開始和結束跟蹤,並將跟蹤信息寫入到磁盤。這種方式很精確,因爲你可以在代碼中精確的指定在什麼位置開始和結束跟蹤數據。
  • 使用DDMS的方法分析特性。這種方法不那麼精確,因爲你沒有更改代碼,而只是通過DDMS指定什麼時候開始和結束log。儘管這種方式,不能精確控制log的地方,但是如果你無法接觸程序代碼,或者不需要第一種方式那麼精確時,這仍然是一種很有用的方法。

生成trace log須知:
  • 如果你使用Debug類,你的設備或者模擬器需要有SD卡,並且程序有寫入SD卡的權限。
  • 如果你使用DDMS,Android 1.5的設備是不支持的。
  • 如果你使用DDMS,Android2.1或之前的設備,必須有SD卡,並且程序有寫入SD卡的權限。
  • 如果你使用DDMS,Android2.2或之後的設備,不需要SD卡,trace log文件直接傳到了你的開發機上。
本文關注如何用Debug類生成跟蹤數據,用DDMS來生成跟蹤數據的話,可參考這一節:Using the Dalvik Debug Monitor Server

爲生成跟蹤數據,首先引入Debug類,之後調用幾個startMethodTracing()方法中的一個,調用時,指定系統生成的log文件的基本名。調用stopMethodTracing()方法來結束log。這兩個方法啓動和結束方法跟蹤,這種方法跟蹤可以跨越整個虛擬機。例如,你可以在Activity的onCreate()方法中調用startMethodTracing()方法,在Activity的onDestroy()中調用stopMethodTracing()方法。

   // start tracing to "/sdcard/calc.trace"
   Debug.startMethodTracing("calc");
   // ...
   // stop tracing
   Debug.stopMethodTracing();
程序調用startMethodTracing()方法,系統會創建名爲.trace的log文件。這個文件包含二進制的方法跟蹤數據和線程方法名稱映射表。

之後系統開始緩存生成的跟蹤數據,直到程序調用stopMethodTracing()方法,此時,就會將緩存的數據寫入到輸出文件。如果在調用stopMethodTracing()方法之前,系統到達了最大緩衝尺寸,系統會停止跟蹤併發送一個消息到控制檯。

當啓用分析時,解釋性代碼會運行得更慢一些,不要試圖從分析結果中生成絕對時序(例如:函數X耗時2.5秒),這些時間只有相對於其他的分析輸出纔有用,所以你可以通過時間比較,判斷所做更改是使代碼變得更快了,還是更慢了。

當使用android模擬器時,在創建AVD時,必須指定SD卡,因爲tracing文件要寫入到SD卡中,同時程序必須有寫入SD卡的權限。

tracing文件的格式,本節的前面已討論過(注:當然,我沒有全部翻譯)。

複製跟蹤文件到主機

程序已經運行並且系統已經在設備或模擬器上生成.trace文件後,你需要將log文件複製到你的開發主機上,可以使用adb pull命令來複制文件。下面這個例子,從模擬器的默認位置複製calc.trace文件到模擬器主機的/temp目錄下:

adb pull /sdcard/calc.trace /tmp

用Traceview查看trace文件

運行traceview來查看trace文件,輸入traceview 。例如,運行Traceview查看上節複製的例子文件:

traceview /tmp/calc
注意:如果你要查看那些使用了Proguard的程序(Release模式編譯)生成的log文件,一些方法和成員名稱可能會被混淆。你可以使用Proguard的mapping.txt來計算出原始的未混淆的名稱。關於該文件的更多信息,請查看Proguard文檔。

使用dmtracdedump

dmtracdedump是讓你從跟蹤日誌文件中,生成圖形化調用棧的工具。該工具使用Graphviz Dot utility來生成圖形化輸出,所以你在運行dmtracdedump之前,必須安裝Graphviz。


dmtracdedump能夠將調用棧數據生成爲樹形圖,每個調用表示爲一個節點。它使用箭頭表示調用流程(從parent節點到child節點)。下圖是一個輸出示例。



圖三,dmtracedump截圖

每個節點,dmtracedump顯示  callname (, ,)這幾樣信息,其中:
  •    ----    調用的參考編號,trace log中用到的。
  •    ----    Inclusive消耗時間(方法,包括所有child方法消耗的毫秒數)
  •    ----    Exclusive消耗時間(方法,不包括任何child方法消耗的毫秒數)
  •    ----    調用數

dmtracedump 的用法:

dmtracedump [-ho] [-s sortable] [-d trace-base-name] [-g outfile] <</span>trace-base-name>
dmtracedump從 .data和 .key文件中載入trace log數據。下表列出了dmtracedump的選項。
Option Description
-d  Diff with this trace name
-g  Generate output to
-h Turn on HTML output
-o Dump the trace file instead of profiling
-d  URL base to the location of the sortable javascript file
-t  Minimum threshold for including child nodes in the graph (child's inclusive time as a percentage of parent inclusive time). If this option is not used, the default threshold is 20%.
注:上面這個原始表明顯是有誤的,第一項和第五項相同,描述卻不相同,第五項應爲-s。下面是我運行這個工具,所提示的用法:
cly@topgun /cygdrive/g/android-sdk-windows/tools
$ dmtracedump.exe
Copyright (C) 2006 The Android Open Source Project

usage: G:\android-sdk-windows\tools\dmtracedump.exe [-ho] [-s sortable] [-d trace-file-name] [-g outfile] trace-file-name
  -d trace-file-name  - Diff with this trace
  -g outfile          - Write graph to 'outfile'
  -k                  - When writing a graph, keep the intermediate DOT file
  -h                  - Turn on HTML output
  -o                  - Dump the dmtrace file instead of profiling
  -s                  - URL base to where the sortable javascript file
  -t threshold        - Threshold percentage for including nodes in the graph


Traceview的已知問題

線程
Traceview log沒有處理線程,會導致下面兩個問題:
  1. 如果在分析期間,線程退出,那麼該線程名字不會出現。
  2. VM重用線程ID。如果一個線程退出,又起了另一個線程,它們的ID可能相同。

注:我的ADT版本是21.1,使用了下traceview.bat,發現這個工具已經不能單獨使用了,而用一個綜合工具 Android Device Monitor代替了
cly@topgun /cygdrive/c/Users/cly/Desktop
$ traceview.bat ./ddms61735.trace
The standalone version of traceview is deprecated.
Please use Android Device Monitor (tools/monitor) instead. 
trace file './ddms61735.trace' not found

使用monitor
cly@topgun /cygdrive/c/Users/cly/Desktop
$ monitor.bat
會開啓一個界面,選擇File菜單,然後Open file,打開你的trace文件即可,雙擊這個trace的標籤頁,即可最大化顯示,截圖如下。

界面截圖如下:,



圖四,新版本的traceview視圖

在Android系統中,Java層和c++/c層都可以創建線程。不過Java層創建的線程最終都由c++/c層來實現。使用ddms這個工具可以查看虛擬機實例進程(除zygote本身和由native code創建的進程,比如vold外)所包含的線程大致信息。比如線程名字,id號,狀態,user time和system time等。
大家可以看到一般一個虛擬機實例進程都包含有如下7個通用的線程,下面大致講一下這些線程的作用和創建流程。
1. main
這個就是主線程了。具體流程待細述。
2. HeapWorker
一個異步的工作線程,處理那些需要在單獨線程裏面做的避免同步問題的堆操作。其源代碼在dalvik/vm/alloc/HeapWorker.*部分。
3. Signal Catcher
這個線程是用來捕獲linux信號和做一些後續處理的。比如說,當一個SIGQUIT (Ctrl-\)信號到達後,這個線程就會掛起虛擬機,並且將所有線程的狀態信息輸出到log。其源代碼在dalvik/vm/SignalCatcher.*部分。
4. JDWP
這個線程是用來實現Java Debug Wire Protocol的。如果命令行調試器的參數爲"suspend=y",這樣會暫停虛擬機。這個估計和eclipse的調試和ddms等調試工具相關。其源代碼在dalvik/vm/jdwp/*部分。
5. Stdio Converter
這個線程從標準輸出和標準錯誤輸出讀取信息並將它們轉換爲log信息。其源代碼在dalvik/vm/StdioConverter.*部分。
6. Compiler
Android's Jit獨立於目標平臺的部分。其源代碼在dalvik/vm/compiler/Compiler.*和dalvik/vm/interp/Jit.*等部分。
7. Binder Thread #%d
使用binder進行通訊時用到的線程。其源代碼在frameworks/base/libs/binder/*等部分。
以下的線程屬於system_server和應用程序專有線程,視具體應用的需求而定。
8. system_server專有
android.server.ServerThread
ActivityManager
ProcessStats
PackageManager
FileObserver
AccountManagerService
SyncHandlerThread
UEventObserver
PowerManagerService
AlarmManager
WindowManager
InputDeviceReader
WindowManagerPolicy
InputDispatcher
ConnectivityThread
WifiService
WifiWatchdogThread
LocationManagerService
AudioService
GpsEventThread
GpsNetworkThread
android.hardware.SensorManager$SensorThread
watchdog
Wallpaper
com.android.server.MountListener
9. misc
其他部分線程由java層的api提供,Thread等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章