如何用logcat抓取執行在android手機上執行的qq的報錯log,logcat如何安裝使用? 匿名使用者 1級 2014-04-21 回答

安裝谷歌自帶的eclipse跟sdk的整合包,不需要安裝logcat的,自帶的有,如果QQ裡面的原始碼對qq報錯有logcat輸出的話也可以檢視的到。如果原始碼裡面沒有log輸出,也看不到的。

如何用logcat抓取執行在android手機上執行的qq的報錯log,logcat如何安裝使用? 為了夢想 1級 2014-04-21 回答

首先,讓我們看一看androidlog的格式。下面這段log是以所謂的long格式打印出來的。從前面logcat的介紹中可以知道,long格式會把時間,標籤等作為單獨的一行顯示。

[ 12-09 21:39:35。510 396: 416 i/activitymanager ]

start procnet。coollet。infzmreader:umengservice_v1 for service

net。coollet。infzmreader/com。umeng。message。

umengservice:pid=21745 uid=10039 gids={50039, 3003, 1015,1028}

[ 12-09 21:39:35。518 21745:21745i/dalvikvm ]

turning on jni app bug workarounds fortarget sdk version 8。。。

[ 12-09 21:39:35。611 21745:21745d/agooservice ]

oncreate()

我們以第一行為例:12-09 是日期,21:39:35。510是時間396是程序號,416是執行緒號;i代表log優先順序,activitymanager是log標籤。

在應用開發中,這些資訊的作用可能不是很大。但是在系統開發中,這些都是很重要的輔助資訊。開發工程師分析的log很多都是由測試工程師抓取的,所以可能有些log根本就不是當時出錯的log。如果出現這種情況,無論你怎麼分析都不太可能得出正確的結論。如何能最大限度的避免這種情況呢?筆者就要求測試工程師報bug時必須填上bug發生的時間。這樣結合log裡的時間戳資訊就能大致判斷是否是發生錯誤時的log。而且根據測試工程師提供的bug發生時間點,開發工程師可以在長長的log資訊中快速的定位錯誤的位置,縮小分析的範圍。

同時我們也要注意,時間資訊在log分析中可能被錯誤的使用。例如:在分析多執行緒相關的問題時,我們有時需要根據兩段不同執行緒中log語句執行的先後順序來判斷錯誤發生的原因,但是我們不能以兩段log在log檔案中出現的先後做為判斷的條件,這是因為在小段時間內兩個執行緒輸出log的先後是隨機的,log列印的先後順序並不完全等同於執行的順序。那麼我們是否能以log的時間戳來判斷呢?同樣是不可以,因為這個時間戳實際上是系統列印輸出log時的時間,並不是呼叫log函式時的時間。遇到這種情況唯一的辦法是在輸出log前,呼叫系統時間函式獲取當時時間,然後再透過log資訊列印輸出。這樣雖然麻煩一點,但是隻有這樣取得的時間才是可靠的,才能做為我們判斷的依據。

另外一種誤用log中時間戳的情況是用它來分析程式的效能。一個有多年工作經驗的工程師拿著他的效能分析結果給筆者看,但是筆者對這份和實際情況相差很遠的報告表示懷疑,於是詢問這位工程師是如何得出結論的。他的回答讓筆者很驚訝,他計算所採用的資料就是log資訊前面的時間戳。前面我們已經講過,log前面時間戳和呼叫log函式的時間並不相同,這是由於系統緩衝log資訊引起的,而且這兩個時間的時間差並不固定。所以用log資訊前附帶的時間戳來計算兩段log間程式碼的效能會有比較大的誤差。正確的方法還是上面提到的:在程式中獲取系統時間然後列印輸出,利用我們列印的時間來計算所花費的時間。

瞭解了時間,我們再談談程序id和執行緒id,它們也是分析log時很重要的依據。我們看到的log檔案,不同程序的log資訊實際上是混雜在一起輸出的,這給我們分析log帶來了很大的麻煩。有時即使是一個函式內的兩條相鄰的log,也會出現不同程序的log交替輸出的情況,也就是a程序的第一條log後面跟著的是b程序的第二條log,對於這樣的組合如果不細心分析,就很容易得出錯誤的結論。這時一定要仔細看log前面的程序id,把相同id的log放到一起看。

不同程序的log有這樣的問題,不同的執行緒輸出的log當然也存在著相同的問題。logcat加上-vthread就能打印出執行緒id。但是有一點也要引起注意,就是android的執行緒id和我們平時所講的linux執行緒id並不完全等同。首先,在android系統中,c++層使用的linux獲取執行緒id的函式gettid()是不能得到執行緒id的,呼叫gettid()實際上返回的是程序id。作為替代,我們可以呼叫pthread_self()得到一個唯一的值來標示當前的native執行緒。android也提供了一個函式androidgetthreaid()來獲取執行緒id,這個函式實際上就是在呼叫pthread_self函式。但是在java層執行緒id又是另外一個值,java層的執行緒id是透過呼叫thread的getid方法得到的,這個方法的返回值實際上來自android在每個程序的java層中維護的一個全域性變數,所以這個值和c++層所獲得的值並不相同。這也是我們分析log時要注意的問題,如果是java層執行緒id,一般值會比較小,幾百左右;如果是c++層的執行緒,值會比較大。在前裡面的log樣本中,就能很容易的看出,第一條log是jave層輸出的log,第二條是native層輸出的。明白了這些,我們在分析log時就不要看見兩段log前面的執行緒id不相同就得出是兩個不同執行緒log的簡單結論,還要注意jave層和native層的區別,這樣才能防止被誤導。

androidlog的優先順序在列印輸出時會被轉換成v,i,d,w,e等簡單的字元標記。在做系統log分析時,我們很難把一個log檔案從頭看到尾,都是利用搜索工具來查找出錯的標記。比如搜尋“e/”來看看有沒有指示錯誤的log。所以如果參與系統開發的每個工程師都能遵守android定義的優先順序含義來輸出log,這會讓我們繁重的log分析工作變得相對輕鬆些。

android比較常見的嚴重問題有兩大類,一是程式發生崩潰;二是產生了anr。程式崩潰和anr既可能發生在java層,也可能發生在native層。如果問題發生在java層,出錯的原因一般比較容易定位。如果是native層的問題,在很多情況下,解決問題就不是那麼的容易了。我們先看一個java層的崩潰例子:

i/activitymanager( 396): start proccom。test。crash for activity com。test。crash/。mainactivity:

pid=1760 uid=10065 gids={50065, 1028}

d/androidruntime( 1760): shutting downvm

w/dalvikvm( 1760): threadid=1: threadexiting with uncaught exception(group=0x40c38930)

e/androidruntime( 1760): fatalexception: main

e/androidruntime( 1760):java。lang。runtimeexception: unable to start activitycomponentinfo

{com。test。crash/com。test。crash。mainactivity}:java。lang。nullpointerexception

e/androidruntime( 1760): atandroid。app。activitythread。performlaunchactivity(activitythread。java:2180)

e/androidruntime( 1760): atandroid。app。activitythread。handlelaunchactivity(activitythread。java:2230)

e/androidruntime( 1760): atandroid。app。activitythread。access$600(activitythread。java:141)

e/androidruntime( 1760): atandroid。app。activitythread$h。handlemessage(activitythread。java:1234)

e/androidruntime( 1760): atandroid。os。handler。dispatchmessage(handler。java:99)

e/androidruntime( 1760): atandroid。os。looper。loop(looper。java:137)

e/androidruntime( 1760): atandroid。app。activitythread。main(activitythread。java:5050)

e/androidruntime( 1760): atjava。lang。reflect。method。invokenative(nativemethod)

e/androidruntime( 1760): atjava。lang。reflect。method。invoke(method。java:511)

e/androidruntime( 1760): atcom。android。internal。os。zygoteinit$methodandargscaller。run

(zygoteinit。java:793)

e/androidruntime( 1760): atcom。android。internal。os。zygoteinit。main(zygoteinit。java:560)

e/androidruntime( 1760): atdalvik。system。nativestart。main(nativemethod)

e/androidruntime( 1760): caused by:java。lang。nullpointerexception

e/androidruntime( 1760): atcom。test。crash。mainactivity。setviewtext(mainactivity。java:29)

e/androidruntime( 1760): atcom。test。crash。mainactivity。oncreate(mainactivity。java:17)

e/androidruntime( 1760): atandroid。app。activity。performcreate(activity。java:5104)

e/androidruntime( 1760): atandroid。app。instrumentation。callactivityoncreate(instrumentation。java:1080)

e/androidruntime( 1760): atandroid。app。activitythread。performlaunchactivity(activitythread。java:2144)

e/androidruntime( 1760): 。。。 11more

i/process ( 1760): sending signal。pid: 1760 sig: 9

w/activitymanager( 396): force finishing activitycom。test。crash/。mainactivity

jave層的程式碼發生crash問題時,系統往往會打印出很詳細的出錯資訊。比如上面這個例子,不但給出了出錯的原因,還有出錯的檔案和行數。根據這些資訊,我們會很容易的定位問題所在。native層的crash雖然也有棧log資訊輸出,但是就不那麼容易看懂了。下面我們再看一個native層crash的例子:

f/libc ( 2102): fatal signal 11 (sigsegv) at 0x00000000 (code=1), thread2102 (testapp)

d/dalvikvm(26630):gc_for_alloc freed 604k, 11% free 11980k/13368k, paused 36ms, total36ms

i/dalvikvm-heap(26630):grow heap (frag case) to 11。831mb for 102416-byteallocation

d/dalvikvm(26630):gc_for_alloc freed 1k, 11% free 12078k/13472k, paused 34ms, total34ms

i/debug ( 127):*** *** *** *** *** *** *** *** *** *** *** *** *** *** ******

i/debug ( 127):build fingerprint:

‘android/full_maguro/maguro:4。2。2/jdq39/eng。liuchao。20130619。201255:userdebug/test-keys’

i/debug ( 127):revision: ‘9’

i/debug ( 127):pid: 2102, tid: 2102, name: testapp >>>。/testapp <<<

i/debug ( 127):signal 11 (sigsegv), code 1 (segv_maperr), fault addr00000000

i/debug ( 127): r0 00000020 r173696874 r2 400ff520 r300000000

i/debug ( 127): r4 400ff469 r5beb4ab24 r6 00000001 r7beb4ab2c

i/debug ( 127): r8 00000000 r900000000 sl 00000000 fpbeb4ab1c

i/debug ( 127): ip 4009b5dc spbeb4aae8 lr 400ff46f pc400ff45e cpsr 60000030

i/debug ( 127): d0 000000004108dae8 d1 4108ced84108cec8

i/debug ( 127): d2 4108cef84108cee8 d3 4108cf184108cf08

i/debug ( 127): d4 4108c5a84108c598 d5 4108ca084108c5b8

i/debug ( 127): d6 4108ce684108ce58 d7 4108ce884108ce78

i/debug ( 127): d8 0000000000000000 d9 0000000000000000

i/debug ( 127): d10 0000000000000000 d110000000000000000

i/debug ( 127): d120000000000000000 d130000000000000000

i/debug ( 127): d14 0000000000000000 d150000000000000000

i/debug ( 127): d16 c1dcf7c087fec8b4 d173f50624dd2f1a9fc

i/debug ( 127): d18 41c7b1ac89800000 d190000000000000000

i/debug ( 127): d20 0000000000000000 d210000000000000000

i/debug ( 127): d22 0000000000000000 d230000000000000000

i/debug ( 127): d24 0000000000000000 d250000000000000000

i/debug ( 127): d26 0000000000000000 d270000000000000000

i/debug ( 127): d28 0000000000000000 d290000000000000000

i/debug ( 127): d30 0000000000000000 d310000000000000000

i/debug ( 127): scr 00000010

i/debug ( 127):

i/debug ( 127):backtrace:

i/debug ( 127): #00 pc0000045e /system/bin/testapp

i/debug ( 127): #01 pc0000046b /system/bin/testapp

i/debug ( 127): #02 pc0001271f /system/lib/libc。so (__libc_init+38)

i/debug ( 127): #03 pc00000400 /system/bin/testapp

i/debug ( 127):

i/debug ( 127):stack:

i/debug ( 127): beb4aaa8 000000c8

i/debug ( 127): beb4aaac 00000000

i/debug ( 127): beb4aab0 00000000

i/debug ( 127): beb4aab4 401cbee0 /system/bin/linker

i/debug ( 127): beb4aab8 00001000

i/debug ( 127): beb4aabc 4020191d /system/lib/libc。so (__libc_fini)

i/debug ( 127): beb4aac0 4020191d /system/lib/libc。so (__libc_fini)

i/debug ( 127): beb4aac4 40100eac /system/bin/testapp

i/debug ( 127): beb4aac8 00000000

i/debug ( 127): beb4aacc 400ff469 /system/bin/testapp

i/debug ( 127): beb4aad0 beb4ab24 [stack]

i/debug ( 127): beb4aad4 00000001

i/debug ( 127): beb4aad8 beb4ab2c [stack]

i/debug ( 127): beb4aadc 00000000

i/debug ( 127): beb4aae0 df0027ad

i/debug ( 127): beb4aae4 00000000

i/debug ( 127): #00 beb4aae8 00000000

i/debug ( 127): ……。。 ……。。

i/debug ( 127): #01 beb4aae8 00000000

i/debug ( 127): beb4aaec 401e9721 /system/lib/libc。so (__libc_init+40)

i/debug ( 127): #02 beb4aaf0 beb4ab08 [stack]

i/debug ( 127): beb4aaf4 00000000

i/debug ( 127): beb4aaf8 00000000

i/debug ( 127): beb4aafc 00000000

i/debug ( 127): beb4ab00 00000000

i/debug ( 127): beb4ab04 400ff404 /system/bin/testapp

i/debug ( 127):

這個log就不那麼容易懂了,但是還是能從中看出很多資訊,讓我們一起來學習如何分析這種log。首先看下面這行:

pid: 2102, tid: 2102,name: testapp >>>。/testapp <<<

從這一行我們可以知道crash程序的pid和tid,前文我們已經提到過,android呼叫gettid函式得到的實際是程序id號,所以這裡的pid和tid相同。知道程序號後我們可以往前翻翻log,看看該程序最後一次列印的log是什麼,這樣能縮小一點範圍。

接下來內容是程序名和啟動引數。再接下來的一行比較重要了,它告訴了我們從系統角度看,出錯的原因:

signal 11 (sigsegv), code 1(segv_maperr), fault addr 00000000

signal11是linux定義的訊號之一,含義是invalidmemory reference,無效的記憶體引用。加上後面的“faultaddr 00000000”我們基本可以判定這是一個空指標導致的crash。當然這是筆者為了講解而特地製造的一個crash的例子,比較容易判斷,大部分實際的例子可能就沒有那麼容易了。

再接下來的log打印出了cpu的所有暫存器的資訊和堆疊的資訊,這裡面最重要的是從堆疊中得到的backtrace資訊:

i/debug ( 127):backtrace:

i/debug ( 127): #00 pc0000045e /system/bin/testapp

i/debug ( 127): #01 pc0000046b /system/bin/testapp

i/debug ( 127): #02 pc0001271f /system/lib/libc。so (__libc_init+38)

i/debug ( 127): #03 pc00000400 /system/bin/testapp

因為實際的執行系統裡沒有符號資訊,所以打印出的log裡看不出檔名和行數。這就需要我們藉助編譯時留下的符號資訊表來翻譯了。android提供了一個工具可以來做這種翻譯工作:arm-eabi-addr2line,位於prebuilts/gcc/linux-x86/arm/arm-eabi-4。6/bin目錄下。用法很簡單:

#。/arm-eabi-addr2line -f -eout/target/product/hammerhead/symbols/system/bin/testapp0x0000045e

引數-f表示列印函式名;引數-e表示帶符號表的模組路徑;最後是要轉換的地址。這條命令在筆者的編譯環境中得到的結果是:

memcpy /home/rd/compile/android-4。4_r1。2/bionic/libc/include/string。h:108

剩餘三個地址翻譯如下:

main /home/rd/compile/android-4。4_r1。2/packages/apps/testapp/app_main。cpp:38

out_vformat /home/rd/compile/android-4。4_r1。2/bionic/libc/bionic/libc_logging。cpp:361

_start libgcc2。c:0

利用這些資訊我們很快就能定位問題了。不過這樣手動一條一條的翻譯比較麻煩,筆者使用的是從網上找到的一個指令碼,可以一次翻譯所有的行,有需要的讀者可以在網上找一找。

瞭解瞭如何分析普通的log檔案,下面讓我們再看看如何分析anr的log檔案。