這幾天一直在忙着調試 crash 的問題。周末兩天都在加班。 周日更是從早上8:00 到晚上 12:50 一直沒離開過辦公室. 加上這個項目對我們整個開發組以及 EM 都很重要,不容有失,這不禁讓我想起了微軟 NT 開發組開發 NT 的情形,所以有了這個標題.
這次是在 android 上,但不是 arm,而是 x86 atom。我們的程序是從 windows 上移植到 android 上的, 一個 C++ 寫的底層庫作為 service,UI 是 java 寫的。 因為是在 android 2.2 上,java 也是唯一的寫 UI 的選擇。 java 通過 aidl/jni 與底層 service/c++ 代碼交互。這樣的架構讓調試很悲劇。
之前一直抱怨 xcode 調試多么不給力,而現在在 android 上的調試已經原始到通過 log 分析程序的執行。arm 上的調試還能用 gdb 勉強為之,而 x86 下那簡直就是坑爹。好不容易把符號神馬的搞定,gdb 遠程調試到設備上,卻發現程序不 crash 了。
logcat 和 tombstone 顯示的 crash 調用棧是這樣的:
06370 INF/DEBUG ( 1670): signal 11 (SIGSEGV), fault addr deadbaad 06371 INF/DEBUG ( 1670): eax 00000000 ebx 801614f4 ecx 00000004 edx 00001000 06372 INF/DEBUG ( 1670): esi b3afa000 edi bfca7b18 06373 INF/DEBUG ( 1670): xcs 00000073 xds 0000007b xes 0000007b xfs 00000000 xss 0000007b 06374 INF/DEBUG ( 1670): eip 8011c768 ebp bfca7b28 esp bfca7af0 flags 00010286 06375 INF/DEBUG ( 1670): #00 06376 INF/DEBUG ( 1670): eip: 8011c768 /system/lib/libc.so (abort) 06377 INF/DEBUG ( 1670): #01 06378 INF/DEBUG ( 1670): eip: 8010ed4b /system/lib/libc.so (dlfree) 06379 INF/DEBUG ( 1670): #02 06380 INF/DEBUG ( 1670): eip: 80111503 /system/lib/libc.so (free) 06381 INF/DEBUG ( 1670): #03 06382 INF/DEBUG ( 1670): eip: 80200a1d /system/lib/libstdc++.so (_ZdlPv) 06383 INF/DEBUG ( 1670): #04 06384 INF/DEBUG ( 1670): eip: 86138519 /data/data/cip.impservice/lib/libimpjni2.so (_ZSt12__stl_deletePv) 06385 INF/DEBUG ( 1670): #05 06386 INF/DEBUG ( 1670): eip: 8613853d /data/data/cip.impservice/lib/libimpjni2.so (_ZNSt11__new_alloc10deallocateEPvj) 06387 INF/DEBUG ( 1670): #06 06388 INF/DEBUG ( 1670): eip: 8613856e /data/data/cip.impservice/lib/libimpjni2.so (_ZNSaIcE10deallocateEPcj) 06389 INF/DEBUG ( 1670): #07 06390 INF/DEBUG ( 1670): eip: 861385b1 /data/data/cip.impservice/lib/libimpjni2.so (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06391 INF/DEBUG ( 1670): stack: 06392 INF/DEBUG ( 1670): #00 06393 INF/DEBUG ( 1670): bfca7af0 00000002 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06394 INF/DEBUG ( 1670): bfca7af4 bfca7b18 [stack] (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06395 INF/DEBUG ( 1670): bfca7af8 00000000 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06396 INF/DEBUG ( 1670): bfca7afc 00000000 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06397 INF/DEBUG ( 1670): bfca7b00 00000000 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06398 INF/DEBUG ( 1670): bfca7b04 00000000 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06399 INF/DEBUG ( 1670): bfca7b08 84a60900 /system/lib/libstlport.so (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06400 INF/DEBUG ( 1670): bfca7b0c 00000010 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06401 INF/DEBUG ( 1670): bfca7b10 0a1f8300 [heap] (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06402 INF/DEBUG ( 1670): bfca7b14 00000001 (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06403 INF/DEBUG ( 1670): bfca7b18 fffffbdf (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06404 INF/DEBUG ( 1670): bfca7b1c 801614f4 /system/lib/libc.so (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06405 INF/DEBUG ( 1670): bfca7b20 0a1f6c98 [heap] (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06406 INF/DEBUG ( 1670): bfca7b24 bfca8410 [stack] (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06407 INF/DEBUG ( 1670): bfca7b28 bfca7b68 [stack] (_ZNSt4priv17_STLP_alloc_proxyIPccSaIcEE10deallocateES1_j) 06408 INF/DEBUG ( 1670): #01 06409 INF/DEBUG ( 1670): bfca7b2c 8010ed4b /system/lib/libc.so (dlfree) 06410 INF/DEBUG ( 1670): bfca7b30 80163118 (dlfree) 06411 INF/DEBUG ( 1670): bfca7b34 00000000 (dlfree) 06412 INF/DEBUG ( 1670): bfca7b38 8010f3ab /system/lib/libc.so (dlmalloc) 06413 INF/DEBUG ( 1670): bfca7b3c 801614f4 /system/lib/libc.so (dlmalloc) 06414 INF/DEBUG ( 1670): bfca7b40 00000066 (dlmalloc) 06415 INF/DEBUG ( 1670): bfca7b44 bfca8410 [stack] (dlmalloc) 06416 INF/DEBUG ( 1670): bfca7b48 0a1f6c90 [heap] (dlmalloc) 06417 INF/DEBUG ( 1670): bfca7b4c 801114d2 /system/lib/libc.so (malloc) 06418 INF/DEBUG ( 1670): bfca7b50 0000002f (malloc) 06419 INF/DEBUG ( 1670): bfca7b54 80201d98 /system/lib/libstdc++.so 06420 INF/DEBUG ( 1670): bfca7b58 bfca7b68 [stack] 06421 INF/DEBUG ( 1670): bfca7b5c 801614f4 /system/lib/libc.so 06422 INF/DEBUG ( 1670): bfca7b60 0a1f6caf [heap] 06423 INF/DEBUG ( 1670): bfca7b64 bfca8410 [stack] 06424 INF/DEBUG ( 1670): bfca7b68 bfca7b78 [stack] 06425 INF/DEBUG ( 1670): ...... ......
每次都幾乎看不到我們的代碼,好不容易看到一個用戶代碼的地址,通過 addr2line, 也是 STL 里的 _alloc.h 之類。這其實是一個提示,但一開始我們並沒有在意。我們還是堅忍的打着 log,試圖逐一的排查 crash, 好不容易向前推進了一些讓程序跑得更遠一點,再跑一次卻發現又回到了起點, crash 又在前面的代碼中出現鳥,我勒個去~
不穩定的 crash 讓我有些沮喪,同時也讓我產生了懷疑,這到底是不是我們的代碼的問題? 看看每次 crash 的調用棧,都有 STL 牽涉其中,又聯想起我們用的 stlport 不是線程安全的 (編譯時使用了 _NOTHREADS 宏), 是不是應該去掉 _NOTHREADS 編譯試試? 但遇到的新問題是,我們使用的動態鏈接的系統自帶的 STL,雖然可以替換掉系統的,但勢必影響其他的程序。這是不能接受的。 馬上想到我們可以編譯一個靜態庫,連接到我們的程序中。說干就干,拿到 stlport 的代碼,折騰一番,也就編譯好了。這里還有一個小插曲, android 的頭文件說它對 #include_next 支持的並不好,但最終我們還是用了 gcc 的這個 feature 才讓代碼編譯通過。
但測試的結果卻讓人沮喪,我再次確認了的確是鏈接到了自己編譯的靜態版本,悲催的發現這次嘗試可恥的失敗鳥~ 這時已是周一凌晨 0:50 了,而我已經在辦公室呆了 16個小時沒有離開。
周一早上9:40到達公司。一計不成,又生一計,這次我注意到調用棧頂端的 lib 的 free 函數,既然每次都 crash 在這里,那我能不能讓它不調用這個函數。聯想起之前看過的 stlport 文檔 (可 以讓 stlport 使用 new/delete 而不是 malloc/free 來分配/釋放內存, 無獨有偶,目前我們做的這個項目的前身(另一個項目組開發的)編譯的時候就加了 stlport 的這個宏(我們在移植代碼的時候由於匆忙,急於讓代碼通過編譯,沒能加上這個宏),這更堅定了我做這個嘗試的決心。於是給十幾個模塊的 mk 文件都加上了這幾個 stlport 相關的宏(看 stlport 文檔關於分配內存的宏),rebuild all。 哈利露亞~ 幸福來得那么突然, 程序真的跑起來了,不再 crash!
http://www.stlport.com/doc/configure.html),的確有一個宏 _ STLP_USE_NEWALLOC (總結這次調試的經驗教訓:
1. 開始時調試進展緩慢,問題出在編譯/調試環境很不給力。
我們需要把代碼放到另一個 site 的唯一的 build machine 上讓同事幫忙編譯,這個過程讓調試過程變得很漫長。 所以我讓同事給我在 build machine 上單獨弄了一個環境,這樣我就可以隨意的修改/調試代碼了
2. 對問題不敏感,雖然每次都看到 stl/free 出現在調用棧中,直到在錯誤的方向上迷失才意識到我們可能走錯路了
3. 對項目本身不夠了解,我沒能意識到我們其實有一個能用的版本(雖然我們重構了很多),的確應該早些查看別人是怎么做到的。 這里有一個客觀原因是我之前一直不在這個項目。
4. Android 的調試環境太坑爹了。既然符號神馬的都齊活了,在調研棧里卻只顯示地址和莫名其妙的函數名(還是 decorated), 尼瑪就不能把代碼行也加進去啊!有木有!Android 開發小組在忙於追趕/超越蘋果的時候,明顯對開發者照顧不夠。 當然,也有可能是我被微軟給慣壞了.