|
Android log系統php |
|
|
lighthtml |
2011/11/20java |
Android 系統log抓取,實現原理分析linux |
本文檔主要是供Android開發人員使用,特別是Framework開發。由於Framework中95%以上的問題都是靠分析log解決的,因此開發人員必須對android整個log系統十分清楚。什麼問題抓什麼log, 使用什麼工具抓Log,如何分析log, 如何在代碼中添加log.android
關於ddms是如何工做的和ddms的詳細功能,見下面android sdk中文檔詳細介紹:shell
F:\02 Android\01_SDK\Gingerbread2.3\docs-2.3_r01-linux\guide\developing\tools\ddms.html緩存
Ddms 工具中打印log的幾個菜單以下:性能優化
Device -> Show process status…app
Device -> Dump device state…eclipse
Device -> Dump app state…
Device -> Dump radio state…
Device -> Run logcat…
一、 Show process status…菜單
等效於在adb shell下執行adb shell ps –x 命令,該命令打印出進程的詳細信息,以下:
USER PID PPID VSIZE RSS WCHAN PC NAME
root 1 0 268 180 c009b74c 0000875c S /init (u:2, s:371)
root 2 0 0 0 c004e72c 00000000 S kthreadd (u:0, s:1)
root 3 2 0 0 c003fdc8 00000000 S ksoftirqd/0 (u:0, s:0)
root 4 2 0 0 c004b2c4 00000000 S events/0 (u:0, s:39)
root 5 2 0 0 c004b2c4 00000000 S khelper (u:0, s:0)
root 6 2 0 0 c004b2c4 00000000 S suspend (u:0, s:0)
USER 用戶名,即用戶所在組
PID 進程ID(Process ID)
PPID 父進程的進程ID(Parent Process id)
VSZ 進程所使用的虛擬內存的大小(Virtual Size)
RSS 進程使用的駐留集大小或者是實際內存的大小,Kbytes字節。
WCHAN 進程正在睡眠的內核函數名稱;該函數的名稱是從/root/system.map文件中得到的
Exploring Processes
You can see the output of ps -x for a specific VM by selecting Device > Show process status... in the menu bar.
二、 Dump device state…菜單
等效於執行 /system/bin/dumpstate /proc/self/fd/0
等效於執行adb shell dumpstate命令
To run dumpstate from Dalvik, select Device > Dump device state... in the menu bar.
三、 Dump app state…菜單
等效於執行adb shell dumpsys命令
輸出android服務狀態信息,即輸出服務中dump函數的log.
四、 Dump radio state…菜單
等效於執行adb shell cat /data/logs/radio命令
Examine Radio State
By default, radio state is not output during a standard logcat (it is a lot of information). To see radio information, either click Device > Dump radio state... or run logcat as described in Logging Radio Information.
五、 Run logcat…菜單
等效於執行adb logcat
To run dumpsys (logcat) from Dalvik, select Device > Run logcat... in the menu bar.
總結:除了Run logcat…菜單是實時輸出設備中的log外,其餘菜單都是輸出設備中緩存的log
Android sdk文檔中對adb的介紹見下:
F:\02 Android\01_SDK\Gingerbread2.3\docs-2.3_r01-linux\guide\developing\tools\adb.html
在adb 1.0.26版本共有2條命令打印log,以下:
adb logcat [ <filter-spec> ] - View device log
adb bugreport - return all information from the device
that should be included in a bug report.
Adb logcat經常使用命令
logcat -c 清除已有log信息
logcat -b main 顯示主緩衝區的log
logcat -b radio 顯示無線緩衝區的log
logcat -b events 顯示事件緩衝區的log
logcat -f [filename] 將log保存到指定的文件中,例如 logcat -b radio -f /data/radio.log
比較經常使用的是顯示時間:logcat -v time &
logcat -g 查看緩衝區的大小
logcat -g main
logcat -g radio
logcat -g events
logcat打印/dev/log設備下的三個文件 radio, events, main數據
logcat默認是輸出main緩衝區的log
控制日誌輸出格式
日誌信息包括了許多元數據域包括標籤和優先級。能夠修改日誌的輸出格式,因此能夠顯示出特定的元數據域。能夠經過 -v 選項獲得格式化輸出日誌的相關信息.
brief — Display priority/tag and PID of originating process (the default format).
process — Display PID only.
tag — Display the priority/tag only.
thread — Display process:thread and priority/tag only.
raw — Display the raw log message, with no other metadata fields.
time — Display the date, invocation time, priority/tag, and PID of the originating process.
long — Display all metadata fields and separate messages with a blank lines.
當啓動了logcat ,你能夠經過-v 選項來指定輸出格式:
[adb] logcat [-v <format>]
此外,adb shell cmd, cmd爲/system/bin目錄下抓log的可執行程序,如dumpsys , dumpstate等
Adb下的log命令:
Adb logcat
Adb bugreport
Adb shell dumpsys
Adb shell dumpstate
Adb shell dmesg //導出當前緩存的kernel log
Adb shell kmsgcat //實時查看kernel log
其餘查看系統當前信息命令
Adb shell ps //查看系統進程信息,能夠加不少有用信息
Adb shell pm //查看package相關信息
Adb shell am //啓動apk應用
Adb shell setprop //設置系統屬性
Adb shell getprop //查看全部系統屬性
Adb shell reboot
Adb shell kill //經過進程ID殺死指定的進程
Adb shell top //查看當前運行進程信息
Adb shell vmstat //查看虛擬機信息
Adb shell bootanimation //播放開機動畫
Adb shell df //查看分區信息
Adb shell monkey //跑自動化測試用例
1、手機dropbox默認路徑:
/data/system/dropbox/
實現機制:
log文件什麼場景產生?
Log文件分析:
2、手機anr日誌默認路徑:
/data/anr/
實現機制:
log文件什麼場景產生?
Log文件分析:
4、tombstones路徑:
/data/tombstones
log文件什麼場景產生?
Log文件分析:
如:
adb logcat -v time -r 1024 -n 16 -f /sdcard/bugreports/applogcat-log
1、Logcat
LogCat是在文件system/core/logcat/logcat.cpp中實現的。
從Logger設備驅動的實現知道,Log的讀取是阻塞的操做,亦即,有數據可用,讀出數據;不然,讀操做會被BLOCK,相應的讀進程也會被掛起等待。下面看應用程序LogCat中如何實現讀的,這可能須要不斷回頭與寫操做和驅動實現結合來看。
看具體實現以前,先看一個logcat中定義的重要的結構體log_device_t。其中的重要的成員在後面用到的時候再具體解釋。
Android的Logcat命令詳解的命令參數-b <buffer>知道,logcat是能夠經過參數來指定對哪一個buffer(main/radio/event)進行操做的。Logcat的b參數解析的地方,是經過傳遞進來的參數(main/radio/event)來建立了一個上面的結構變量,而這些結構經過log_device_t.next連接起來
由於logcat可能會同時操做多個Buffer,而read()會阻塞讀取進程,對其餘Buffer的讀取就不能進行,因此這裏用select()來判斷可讀取的Buffer。
2、Bugreport
I:\00_AndriodSource\android-gingerbread-src\frameworks\base\cmds\bugreport
#include <cutils/properties.h>
#include <cutils/sockets.h>
int main(int argc, char *argv[]) {
char buffer[65536];
int i, s;
/* start the dumpstate service */
property_set("ctl.start", "dumpstate");//啓動dumpstate服務
/* socket will not be available until service starts */
for (i = 0; i < 10; i++) {
s = socket_local_client("dumpstate",
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if (s >= 0)
break;
/* try again in 1 second */
sleep(1);
}
if (s < 0) {
fprintf(stderr, "Failed to connect to dumpstate service\n");
exit(1);
}
while (1) {
int length = read(s, buffer, sizeof(buffer));
if (length <= 0)
break;
fwrite(buffer, 1, length, stdout);
}
close(s);
return 0;
}
原理:啓動dumpstate服務,經過socket鏈接dumpstate服務,而後從socket中不斷讀取dumpstate側的log打印出來
經過代碼分析和實際對比分析,發現bugreport輸出的log和dumpstate輸出的log徹底一致。
3、Dumpstate
I:\00_AndriodSource\android-gingerbread-src\frameworks\base\cmds\dumpstate
輸出的信息包括:
一、 版本信息
二、 系統狀態信息:CPU 內存 進程 系統屬性 等
三、 Logcat信息
四、 Dumpsys輸出的因此services信息
五、 ANR log信息
六、 Dmesg kernel log信息
會運行下面命令輸出log
run_command("CPU INFO", 10, "top", "-n", "1", "-d", "1", "-m", "30", "-t", NULL);
run_command("PROCRANK", 20, "procrank", NULL);
run_command("SYSTEM LOG", 20, "logcat", "-v", "time", "-d", "*:v", NULL);
run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "time", "-d", "*:v", NULL);
run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "time", "-d", "*:v", NULL);
run_command("NETWORK INTERFACES", 10, "netcfg", NULL);
run_command("KERNEL LOG", 20, "dmesg", NULL);
run_command("VOLD DUMP", 10, "vdc", "dump", NULL);
run_command("SECURE CONTAINERS", 10, "vdc", "asec", "list", NULL);
run_command("PROCESSES", 10, "ps", "-P", NULL);
run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL);
run_command("LIBRANK", 10, "librank", NULL);
run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL);
run_command("LAST RADIO LOG", 10, "parse_radio_log", "/proc/last_radio_log", NULL);
run_command("DUMPSYS", 60, "dumpsys", NULL);
4、Dumpsys
I:\00_AndriodSource\android-gingerbread-src\frameworks\base\cmds\dumpsys
const size_t N = services.size();
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
aout << " " << services[i] << endl;
}
}
}
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
if (N > 1) {
aout << "------------------------------------------------------------"
"-------------------" << endl;
aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
}
int err = service->dump(STDOUT_FILENO, args);//調用每一個service中的dump()方法輸出log
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err)
<< ") " << services[i] << endl;
}
} else {
aerr << "Can't find service: " << services[i] << endl;
}
}
輸出log信息代碼如上,結合實際log分析,dumpsys輸出系統服務dump信息
一、 經過設置系統屬性打開log開關
若有以下打印log的代碼:
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Redirect requested but no Location "
+ "specified.");
}
咱們只須要經過設置系統屬性就能夠打印出這個log,而不用修改代碼。能夠經過setprop命令或修改build.prop來達到目的。具體見Log.java中的介紹:
/**
* Checks to see whether or not a log for the specified tag is loggable at the specified level.
*
* The default level of any tag is set to INFO. This means that any level above and including
* INFO will be logged. Before you make any calls to a logging method you should check to see
* if your tag should be logged. You can change the default level by setting a system property:
* 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'
* Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will
* turn off all logging for your tag. You can also create a local.prop file that with the
* following in it:
* 'log.tag.<YOUR_LOG_TAG>=<LEVEL>'
* and place that in /data/local.prop.
*
* @param tag The tag to check.
* @param level The level to check.
* @return Whether or not that this is allowed to be logged.
* @throws IllegalArgumentException is thrown if the tag.length() > 23.
*/
public static native boolean isLoggable(String tag, int level);
1. 查看當前堆棧
1) 功能:在程序中加入代碼,使能夠在logcat中看到打印出的當前函數調用關係
2) 方法:
new Exception(「print trace」).printStackTrace();
2. MethodTracing
1) 功能:用於熱點分析和性能優化,分析每一個函數佔用的CPU時間,調用次數,函數調用關係等
2) 方法:
a) 在程序代碼中加入追蹤開關
import android.os.Debug;
……
android.os.Debug.startMethodTracing(「/data/tmp/test」); // 先建/data/tmp目錄
…… // 被追蹤的程序段
android.os.Debug.stopMethodTracing();
b) 編譯,運行後,設備端生成/data/tmp/test.trace文件
c) 把trace文件複製到PC端
$ adb pull /data/tmp/test.trace ./
d) 使用android自帶工具分析trace文件
$ $ANDROID_SRC/out/host/linux-x86/bin/traceview test.trace
此時可看到各個函數被調用的次數CPU佔用率等信息
e) 使用android自帶工具分析生成調用關係類圖
$ apt-get install graphviz # 安裝圖片相關軟件
$ANDROID_SRC/out/host/linux-x86/bin/dmtracedump -g test.png test.trace
此時目錄下生成類圖test.png
3) 注意
trace文件生成與libdvm模塊DEBUG版本相沖突,因此此方法只適用於對非DEBUG版本模擬器的調試,不然在分析trace文件時會報錯
3. HProf (Heap Profile)
1) 功能:
用於java層面的內存分析,顯示詳細的內存佔用信息,指出可疑的內存泄漏對象
2) 方法:
a) 在代碼中加入dump動做
import android.os.Debug;
import java.io.IOException;
……
try {
android.os.Debug.dumpHprofData(「/data/tmp/input.hprof」); // 先建/data/tmp目錄
} catch (IOException ioe) {
}
b) 把hprof文件複製到PC端
$ adb pull /data/tmp/input.hprof ./
c) 使用命令hprof-conv把hprof轉成MAT識別的標準的hprof
$ $ANDROID_SRC/out/host/linux-x86/bin/hprof-conv input.hprof output.hprof
d) 使用MAT工具看hprof信息
下載MAT工具:http://www.eclipse.org/mat/downloads.php
用工具打開output.hprof
3) 注意:此工具只能顯示java層面的,而不能顯示C層的內存佔用信息
4. SamplingProfile (android 2.0上版本使用)
1) 功能
每隔N毫秒對當前正在運行的函數取樣,並輸出到log中
2) 在代碼中加入取樣設定
import dalvik.system.SamplingProfiler
……
SamplingProfile sp = SamplingProfiler.getInstance();
sp.start(n); // n爲設定每秒採樣次數
sp.logSnapshot(sp.snapshot());
……
sp.shutDown();
它會啓一個線程監測,在logcat中打印信息
5. 用發系統信號的方式取當前堆棧狀況和內存信息
1) 原理
dalvik虛擬機對SIGQUIT和SIGUSR1信號進行處理(dalvik/vm/SignalCatcher.c),分別完成取當前堆棧和取當前內存狀況的功能
2) 用法
a) $ chmod 777 /data/anr -R # 把anr目錄權限設爲可寫
$ rm /data/anr/traces.txt # 刪除以前的trace信息
$ ps # 找到進程號
$ kill -3 進程號 # 發送SIGQUIT信號給該進程,此時生成trace信息
$ cat /data/anr/traces.txt
功能實現:遍歷thread list(dalvik/vm/Thread.c:dvmDumpAllThreadEx()),並打印當前函數調用關係(dalvik/vm/interp/Stack.c:dumpFrames())
b) $ chmod 777 /data/misc -R
$ ps # 找到進程號
$ kill -10 進程號 # 發送SIGQUIT信事信號給該進程,此時生成hprof信息
$ ls /data/misc/*.hprof
此時生成hprf文件,如何使用此文件,見第二部分(HProf)
注意:hprof文件都很大,注意用完立刻刪除,以避免佔滿存儲器
6. logcat及原理
1) android.util.Log利用println的標準java輸出詞句,並加前綴I/V/D….
2) dalvik利用管道加線程的方式,先利用dup2把stdout和stderr重定向到管理中(vm/StdioConverter.c:dvmstdioConverterStartup),而後再啓動一個線程從管道另外一端讀出內容(dalvik/vm/StdioConverter.c:stdioconverterThreadStart()),使用LOG公共工具(system/core/liblog/logd_write.c: __android_log_print())輸出到/dev/log/*中去
LogCat是在文件system/core/logcat/logcat.cpp中實現的
7. monkey
1) monkey是一個android自帶的命令行工具。它向系統發送僞隨機的用戶事件流,實現對正在開發的應用程序進行壓力測試。
2) 方法
在設備端打開setting界面
$ adb shell
# monkey -p com.android.settings -v 500
此時能夠看到界面不斷被切換
8. 其它小工具
具體見android.os.Debug中提供的工具
1) 取毫微秒級的時間,用於計算時間
threadCpuTimeNanos()
2) 統計兩點間的內存分配狀況
startAllocCounting()
stopAllocCounting()
getGlobalAllocCount()
get…..
3) 打印當前已load的class
getLoadedClassCount()
printLoadedClasses() 它須要打開NDEBUG功能才能打開system/core/中Log功能