『壹』 如何定位android NDK開發中遇到的錯誤
NDK編譯生成的.so文件作為程序的一部分,在運行發生異常時同樣會造成程序崩潰。不同於java代碼異常造成的程序崩潰,在NDK的異常發生時,程序在Android設備上都會立即退出,即通常所說的閃退,而不會彈出「程序xxx無響應,是否立即關閉」之類的提示框。
NDK是使用C/C++來進行開發的,熟悉C/C++的程序員都知道,指針和內存管理是最重要也是最容易出問題的地方,稍有不慎就會遇到諸如內存無效訪問、無效對象、內存泄露、堆棧溢出等常見的問題,最後都是同一個結果:程序崩潰。例如我們常說的空指針錯誤,就是當一個內存指針被置為空(NULL)之後再次對其進行訪問;另外一個經常出現的錯誤是,在程序的某個位置釋放了某個內存空間,而後在程序的其他位置試圖訪問該內存地址,這就會產生一個無效地址錯誤。常見的錯誤類型如下:
初始化錯誤
訪問錯誤
數組索引訪問越界
指針對象訪問越界
訪問空指針對象
訪問無效指針對象
迭代器訪問越界
內存泄露
參數錯誤
堆棧溢出
類型轉換錯誤
數字除0錯誤
利用Android NDK開發本地應用的時候,幾乎所有的程序員都遇到過程序崩潰的問題,但它的崩潰會在logcat中列印一堆看起來類似天書的堆棧信息,讓人舉足無措。單靠添加一行行的列印信息來定位錯誤代碼做在的行數,無疑是一件令人崩潰的事情。在網上搜索「Android NDK崩潰」,可以搜索到很多文章來介紹如何通過Android提供的工具來查找和定位NDK的錯誤,但大都晦澀難懂。下面以一個實際的例子來說明,首先生成一個錯誤,然後演示如何通過兩種不同的方法,來定位錯誤的函數名和代碼行。
首先,看我們在hello-jni程序的代碼中做了什麼(有關如何創建或導入工程,此處略),看下圖:在JNI_OnLoad()的函數中,即so載入時,調用willCrash()函數,而在willCrash()函數中,std::string的這種賦值方法會產生一個空指針錯誤。這樣,在hello-jni程序載入時就會閃退。我們記一下這兩個行數:在61行調用了willCrash()函數;在69行發生了崩潰。
下面來看看發生崩潰(閃退)時系統列印的logcat日誌:
[plain] view plain
************************************************
Buildfingerprint:'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'
pid:32607,tid:32607,name:xample.hellojni>>>com.example.hellojni<<<
signal11(SIGSEGV),code1(SEGV_MAPERR),faultaddr00000000
backtrace:
#00pc00023438/system/lib/libc.so
#01pc00004de8/data/app-lib/com.example.hellojni-2/libhello-jni.so
#02pc000056c8/data/app-lib/com.example.hellojni-2/libhello-jni.so
#03pc00004fb4/data/app-lib/com.example.hellojni-2/libhello-jni.so
#04pc00004f58/data/app-lib/com.example.hellojni-2/libhello-jni.so
#05pc000505b9/system/lib/libdvm.so
#06pc00068005/system/lib/libdvm.so
#07pc000278a0/system/lib/libdvm.so
#08pc0002b7fc/system/lib/libdvm.so
#09pc00060fe1/system/lib/libdvm.so
#10pc0006100b/system/lib/libdvm.so
#11pc0006c6eb/system/lib/libdvm.so
#12pc00067a1f/system/lib/libdvm.so
#13pc000278a0/system/lib/libdvm.so
#14pc0002b7fc/system/lib/libdvm.so
#15pc00061307/system/lib/libdvm.so
#16pc0006912d/system/lib/libdvm.so
#17pc000278a0/system/lib/libdvm.so
#18pc0002b7fc/system/lib/libdvm.so
#19pc00060fe1/system/lib/libdvm.so
#20pc00049ff9/system/lib/libdvm.so
#21pc0004d419/system/lib/libandroid_runtime.so
#22pc0004e1bd/system/lib/libandroid_runtime.so
#23pc00001d37/system/bin/app_process
#24pc0001bd98/system/lib/libc.so
#25pc00001904/system/bin/app_process
stack:
beb12340012153f8
beb1234400054290
beb1234800000035
beb1234cbeb123c0[stack]
……
如果你看過logcat列印的NDK錯誤時的日誌就會知道,我省略了後面很多的內容,很多人看到這么多密密麻麻的日誌就已經頭暈腦脹了,即使是很多資深的Android開發者,在面對NDK日誌時也大都默默的選擇了無視。
「符號化」NDK錯誤信息的方法
其實,只要你細心的查看,再配合Google提供的工具,完全可以快速的准確定位出錯的代碼位置,這個工作我們稱之為「符號化」。需要注意的是,如果要對NDK錯誤進行符號化的工作,需要保留編譯過程中產生的包含符號表的so文件,這些文件一般保存在$PROJECT_PATH/obj/local/目錄下。
第一種方法:ndk-stack
這個命令行工具包含在NDK工具的安裝目錄,和ndk-build和其他一些常用的NDK命令放在一起,比如在我的電腦上,其位置是/android-ndk-r9d/ndk-stack。根據Google官方文檔,NDK從r6版本開始提供ndk-stack命令,如果你用的之前的版本,建議還是盡快升級至最新的版本。使用ndk –stack命令也有兩種方式
使用ndk-stack實時分析日誌
在運行程序的同時,使用adb獲取logcat日誌,並通過管道符輸出給ndk-stack,同時需要指定包含符號表的so文件位置;如果你的程序包含了多種CPU架構,在這里需求根據錯誤發生時的手機CPU類型,選擇不同的CPU架構目錄,如:
[plain] view plain
adbshelllogcat|ndk-stack-sym$PROJECT_PATH/obj/local/armeabi
當崩潰發生時,會得到如下的信息:
[plain] view plain
**********Crashmp:**********
Buildfingerprint:'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'
pid:32607,tid:32607,name:xample.hellojni>>>com.example.hellojni<<<
signal11(SIGSEGV),code1(SEGV_MAPERR),faultaddr00000000
Stackframe#00pc00023438/system/lib/libc.so(strlen+72)
Stackframe#01pc00004de8/data/app-lib/com.example.hellojni-2/libhello-jni.so(std::char_traits<char>::length(charconst*)+20):Routinestd::char_traits<char>::length(charconst*)at/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/char_traits.h:229
Stackframe#02pc000056c8/data/app-lib/com.example.hellojni-2/libhello-jni.so(std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(charconst*,std::allocator<char>const&)+44):Routinebasic_stringat/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/_string.c:639
Stackframe#03pc00004fb4/data/app-lib/com.example.hellojni-2/libhello-jni.so(willCrash()+68):RoutinewillCrash()at/home/testin/hello-jni/jni/hello-jni.cpp:69
Stackframe#04pc00004f58/data/app-lib/com.example.hellojni-2/libhello-jni.so(JNI_OnLoad+20):RoutineJNI_OnLoadat/home/testin/hello-jni/jni/hello-jni.cpp:61
Stackframe#05pc000505b9/system/lib/libdvm.so(dvmLoadNativeCode(charconst*,Object*,char**)+516)
Stackframe#06pc00068005/system/lib/libdvm.so
Stackframe#07pc000278a0/system/lib/libdvm.so
Stackframe#08pc0002b7fc/system/lib/libdvm.so(dvmInterpret(Thread*,Methodconst*,JValue*)+180)
Stackframe#09pc00060fe1/system/lib/libdvm.so(dvmCallMethodV(Thread*,Methodconst*,Object*,bool,JValue*,std::__va_list)+272)
……(後面略)
我們重點看一下#03和#04,這兩行都是在我們自己生成的libhello-jni.so中的報錯信息,那麼會發現如下關鍵信息:
[plain] view plain
#03(willCrash()+68):RoutinewillCrash()at/home/testin/hello-jni/jni/hello-jni.cpp:69
#04(JNI_OnLoad+20):RoutineJNI_OnLoadat/home/testin/hello-jni/jni/hello-jni.cpp:61
回想一下我們的代碼,在JNI_OnLoad()函數中(第61行),我們調用了willCrash()函數;在willCrash()函數中(第69行),我們製造了一個錯誤。這些信息都被准確無誤的提取了出來!是不是非常簡單?
先獲取日誌,再使用ndk-stack分析
這種方法其實和上面的方法沒有什麼大的區別,僅僅是logcat日誌獲取的方式不同。可以在程序運行的過程中將logcat日誌保存到一個文件,甚至可以在崩潰發生時,快速的將logcat日誌保存起來,然後再進行分析,比上面的方法稍微靈活一點,而且日誌可以留待以後繼續分析。
[plain] view plain
adbshelllogcat>1.log
ndk-stack-sym$PROJECT_PATH/obj/local/armeabi–mp1.log
第二種方法:使用addr2line和objmp命令
這個方法適用於那些,不滿足於上述ndk-stack的簡單用法,而喜歡刨根問底的程序員們,這兩個方法可以揭示ndk-stack命令的工作原理是什麼,盡管用起來稍微麻煩一點,但是可以滿足一下程序員的好奇心。
先簡單說一下這兩個命令,在絕大部分的linux發行版本中都能找到他們,如果你的操作系統是linux,而你測試手機使用的是Intel x86系列,那麼你使用系統中自帶的命令就可以了。然而,如果僅僅是這樣,那麼絕大多數人要絕望了,因為恰恰大部分開發者使用的是Windows,而手機很有可能是armeabi系列。
別急,在NDK中自帶了適用於各個操作系統和CPU架構的工具鏈,其中就包含了這兩個命令,只不過名字稍有變化,你可以在NDK目錄的toolchains目錄下找到他們。以我的Mac電腦為例,如果我要找的是適用於armeabi架構的工具,那麼他們分別為arm-linux-androideabi-addr2line和arm-linux-androideabi-objmp;位置在下面目錄中,後續介紹中將省略此位置:
[plain] view plain
/Developer/android_sdk/android-ndk-r9d/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/
假設你的電腦是windows,CPU架構為mips,那麼你要的工具可能包含在這個目錄中:
[plain] view plain
D:android-ndk-r9d oolchainsmipsel-linux-android-4.8prebuiltwindows-x86_64in
『貳』 Android P 系統穩定性問題分析方法總結
Android系統最開始是為手機設計的,在機頂盒,電視,帶屏音箱等大屏上運行後,晶元廠家做些適配,產品廠家也會做系統客制化,有時候還要適配第三方應用..等待
這種適配容易引人系統的穩定性問題,系統穩定性對於用戶體驗至關重要,很多問題也都比較類似,android系統對系統性能,穩定性分析工具也比較多,下面根據工作中遇到的問題做個總結。
從表現來看有: 死機重啟, 自動關機, 無法開機,凍屏,黑屏以及閃退, 無響應等情況;
從技術層面來劃分無外乎兩大類: 長時間無法執行完成(Timeout) 以及異常崩潰(crash). 主要分類如下:
ANR(Application Not responding),是指普通app進程超過一定時間沒有執行完,系統會彈出應用無響應對話框. 如果
該進程運行在system進程, 更准確的來說,應該是(System Not Responding, SNR)
ANR產生的原因可能是各種各樣的,但常見的原因可以分為:
1.logcat日誌
2.trace文件(保存在/data/anr/traces.txt)
從logcat里可以看到死鎖的列印
從traces.txt可以看到線程的函數調用棧
10-16 00:50:10 820 907 E ActivityManager: ANR in com.android.systemui, time=130090695
10-16 00:50:10 820 907 E ActivityManager: Reason: Broadcast of Intent { act=android.intent.action.TIME_TICK flg=0x50000114 (has extras) }
10-16 00:50:10 820 907 E ActivityManager: Load: 30.4 / 22.34 / 19.94
10-16 00:50:10 820 907 E ActivityManager: Android time :[2015-10-16 00:50:05.76] [130191,266]
10-16 00:50:10 820 907 E ActivityManager: CPU usage from 6753ms to -4ms ago:
10-16 00:50:10 820 907 E ActivityManager: 47% 320/netd: 3.1% user + 44% kernel / faults: 14886 minor 3 major
10-16 00:50:10 820 907 E ActivityManager: 15% 10007/com.sohu.sohuvideo: 2.8% user + 12% kernel / faults: 1144 minor
10-16 00:50:10 820 907 E ActivityManager: 13% 10654/hif_thread: 0% user + 13% kernel
10-16 00:50:10 820 907 E ActivityManager: 11% 175/mmcqd/0: 0% user + 11% kernel
10-16 00:50:10 820 907 E ActivityManager: 5.1% 12165/app_process: 1.6% user + 3.5% kernel / faults: 9703 minor 540 major
10-16 00:50:10 820 907 E ActivityManager: 3.3% 29533/com.android.systemui: 2.6% user + 0.7% kernel / faults: 8402 minor 343 major
......
10-16 00:50:10 820 907 E ActivityManager: +0% 12832/cat: 0% user + 0% kernel
10-16 00:50:10 820 907 E ActivityManager: +0% 13211/zygote64: 0% user + 0% kernel
10-16 00:50:10 820 907 E ActivityManager: 87% TOTAL: 3% user + 18% kernel + 64% iowait + 0.5% softirq
發生ANR的時間 00:50:10 ,可以從這個時間點之前的日誌中,還原ANR出現時系統的運行狀態
發生ANR的進程 com.android.system.ui
發生ANR的原因 Reason關鍵字表明了ANR的原因是處理TIME_TICK廣播消息超時
CPU負載 Load關鍵字表明了最近1分鍾、5分鍾、15分鍾內的CPU負載分別是30.4、22.3、19.94.CPU最近1分鍾的負載最具參考價值,因為ANR的超時限制基本都是1分鍾以內, 這可以近似的理解為CPU最近1分鍾平均有30.4個任務要處理,這個負載值是比較高的
CPU使用統計時間段 CPU usage from XX to XX ago關鍵字表明了這是在ANR發生之前一段時間內的CPU統計,類似的還有CPU usage from XX to XX after關鍵字,表明是ANR發生之後一段時間內的CPU統計
各進程的CPU使用率
以com.android.systemui進程的CPU使用率為例,它包含以下信息:
總的CPU使用率: 3.3%,其中systemui進程在用戶態的CPU使用率是2.6%,在內核態的使用率是0.7%
缺頁次數fault:8402 minor表示高速緩存中的缺頁次數,343 major表示內存的缺頁次數。minor可以理解為進程在做內存訪問,major可以理解為進程在做IO操作。 當前minor和major值都是比較高的,從側面反映了發生ANR之前,systemui進程有有較多的內存訪問操作,引發的IO次數也會較多
CPU使用匯總 TOTAL關鍵字表明了CPU使用的匯總,87%是總的CPU使用率,其中有一項iowait表明CPU在等待IO的時間,佔到64%,說明發生ANR以前,有大量的IO操作。app_process、 system_server, com.android.systemui這幾個進程的major值都比較大,說明這些進程的IO操作較為頻繁,從而拉升了整個iowait的時間
traces.txt 如下
----- pid 29533 at 2015-10-16 00:48:29 -----
Cmd line: com.android.systemui
DALVIK THREADS (54):
"main" prio=5 tid=1 Blocked
| group="main" sCount=1 dsCount=0 obj=0x75bd5818 self=0x7f8549a000
| sysTid=29533 nice=0 cgrp=bg_non_interactive sched=0/0 handle=0x7f894bbe58
| state=S schedstat=( 289080040422 93461978317 904874 ) utm=20599 stm=8309 core=0 HZ=100
| stack=0x7fdffda000-0x7fdffdc000 stackSize=8MB
| held mutexes=
at com.mediatek.anrappmanager.MessageLogger.println(SourceFile:77)
Android系統中,有硬體WatchDog用於定時檢測關鍵硬體是否正常工作,類似地,在framework層有一個軟體WatchDog用於定期檢測關鍵系統服務是否發生死鎖事件。
watchdog 每過30s 檢測一次, 如果要監控的線程30s 後沒有響應,系統會mp出此進程堆棧,如果超過60s 沒有相應,會觸發watchdog,並重啟系統
10:57:23.718 579 1308 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in monitor com.android.server.am.ActivityManagerService on foreground thread (android.fg), Blocked in handler on main thread (main), Blocked in handler on ActivityManager (ActivityManager)
10:57:23.725 579 1308 W Watchdog: android.fg annotated stack trace:
10:57:23.726 579 1308 W Watchdog: at com.android.server.am.ActivityManagerService.monitor(ActivityManagerService.java:26271)
10:57:23.727 579 1308 W Watchdog: - waiting to lock <0x0bb47e39> (a com.android.server.am.ActivityManagerService)
10:57:23.727 579 1308 W Watchdog: at com.android.server.Watchdog DeliveryTracker.alarmTimedOut(AlarmManagerService.java:4151)
10:57:23.733 579 1308 W Watchdog: - waiting to lock <0x00aaee38> (a java.lang.Object)
......
10:57:23.736 579 1308 W Watchdog: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:838)
10:57:23.739 579 1308 W Watchdog: ActivityManager annotated stack trace:
10:57:23.740 579 1308 W Watchdog: at com.android.server.am.ActivityStack$ActivityStackHandler.handleMessage(ActivityStack.java:405)
10:57:23.740 579 1308 W Watchdog: - waiting to lock <0x0bb47e39> (a com.android.server.am.ActivityManagerService)
10:57:23.740 579 1308 W Watchdog: at android.os.Handler.dispatchMessage(Handler.java:106)
10:57:23.741 579 1308 W Watchdog: *** GOODBYE!
分析:
提示 ActivityManagerService的android.fg,main,ActivityManager 線程Block了,但logcat里只能看到
android.fg等待0x0bb47e39 鎖,main 等待0x00aaee38鎖,ActivityManager等待0x0bb47e39鎖,無法進一步分析,需要看traces.txt
Cmd line: system_server
......
"main" prio=5 tid=1 Blocked
當出現應用閃退,可以從兩個方面查看:
1、是否應用崩潰:
可以通過logcat –s AndroidRuntime DEBUG過濾日誌,查看應用奔潰的具體堆棧信息。
其中AndroidRuntime的TAG列印java層信息,DEBUG的TAG列印native層的信息。
2、是否被lowmemorykiller殺掉:
可以通過 logcat –s lowmemorykiller 過濾日誌,注意adj 0是代表前台進程。例如:
03-08 04:16:58.084 310 310 I lowmemorykiller: Killing'com.google.android.tvlauncher' (2520), uid 10007, adj 0
發生這種情況,需要mpsys meminfo 查看當前內存狀態,是否有進程內存泄漏,導致系統內存不夠,出現前台進程被殺,造成閃退。
測試過程中,經常遇到屏幕閃爍的現象,需要排除是OSD層閃爍,還是video層閃爍。
1、先通過android原生方法:screencap截圖, screenrecord 錄制視頻,這里都是截取的OSD層,查看是否有閃屏現象。
2、OSD沒有問題,就需要從更底層的顯示模塊分析,一般需要晶元廠家提供debug手段,不同晶元廠家方案不一樣。
3, 有時候輸出不穩定,hdmi/mipi信號干擾,輸出頻率異常等也會導致閃屏,這種情況需要硬體協助分析。
如果OSD層也閃爍,則需從系統和應用層面分析。如曾遇到在開機向導界面,有個應用不斷被喚起,導致走開機向導時出現連續閃灰屏的現象。
黑屏分UI黑屏,視頻播放黑屏但UI正常等,2種場景
1、screencap截屏,排查OSD層圖形是否正常,
2、如果OSD圖形正常,需要排查顯示輸出模塊是否異常。
3、電視機裡面屏顯是單獨控制,如果屏參配置錯誤會導致整改黑屏。
OSD異常,需要排查頂層activity是否黑屏,window是否有異常等.
1,排查視頻圖層或者window是否創建成功。
2,排查解碼是否有異常,不同的應用youtube,netflix,iptv解碼方式不一樣,需要具體問題具體分析。
如下,ActivityManager因為空對象引用而掛掉,導致system_server重啟
*** [FATAL EXCEPTION IN SYSTEM PROCESS: ActivityHanager [
^ava.lang.NullPointerException: Attempt to invoke virtual method 'void co®.android.internal.os.KernelSingleUidTimeReader.iBarkDataAsStale(boolean)' on a null object reference
at com.android.internal.os.BatteryStatsIiaplSConstants.(BatteryStatslnpl.java:13355)
at com.android.internal.os.BatteryStatsInplSConstants.upddteConstants(BatteryStatsImpl.java:13330)
at com.android.internal-o-batteryStatslMpl$Constants-onChange(BatteryStatsInpl-java:13316)
at android.database.Contentobserver.onChange(ContentObserver.java:145)
解決方法:修復空指針
DEBUG : pid: 296, tid: 1721, name: Binder:296_4 >>> /system/bin/surfaceflinger <<<
DEBUG : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr ------
DEBUG : Abort message: 'status.cpp:149] Failed HIDL return status not checked: Status(EXTRANSACTIONFAILED):
DEBUG : r0 00000000 rl 000006b9
DEBUG : C4 00000128 r5 000006b9
r2 00000006 r3 a5c5d620
r6 a235d60c r7 0000010c
DEAD_OB3ECT:
DEBUG : r8 00000019 r9 0000015d
DEBUG : ip a6ablbec sp a235d5f8
rlO a568f090 rll a620dce9
Ir a5be901d pc a5be0da2
/system/lib/libc.so (abort+62)
/system/lib/libbase.so (android::base::DefaultAborter(char const )+6)
backtrace:
/system/lib/libsurfaceflinger.so
/system/lib/libsurfaceflinger.so
/system/lib/libsurfaceflinger.so
/system/lib/libsurfaceflinger.so
/system/lib/libbase.so (android::base::LogMessage::~LogMessage()+502)
/system/lib/libhidlbase.so (android::hardware::details::return_status::~return_status()+184)
(android::Hwc2::impl::Composer::getActiveConfig(unsigned long long, unsigned int )+56)
(HWC2::Display::getActiveConfig(std::_1::shared_ptr<HWC2::Display::Config const>*) const+38)
(android::HWComposer::getActiveConfig(int) const+64)
(android::SurfaceFlinger::resyncToHardwareVsync(bool)+64)
可以根據backtrace來進行定位異常崩潰的地方。Android P上, backtrace使用Java上下文來顯示,省去使用addr2line來轉換的一個過程,方便調試分析問題。但是實際場景中,
有些native進程崩潰只有pc地址,而無函數信息,或者需要定位到具體的某個文件某個函數,則可藉助堆棧分析工具addr2line。
addr2line:根據堆棧定位具體函數和文件
addr2line -e libsurfaceflinger.so -f 00071a09
addr2line -e libsurfaceflinger.so -f 00071a09
_
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:1229
需注意兩點:
1、需用帶debug信息的LINK目錄裡面的so庫,機頂盒上的so庫是無法定位的:
out/target/proct/xx/obj/SHARED_LIBRARIES/libsurfaceflinger_intermediates/LINKED/libsurfaceflinger.so
或者:out/target/proct/xx/symbols/system/lib/libsurfaceflinger.so
2、定位的文件,必現和機器上出現問題的版本一致,否則定位不準確
debuggerd:列印當前進程實時堆棧:debuggerd –b pid
主要可以分為以下3類
1)Data abort
Unable to handle kernel NULL pointer dereference at virtual address...
Unable to handle kernel paging request at virtual address...
Unhandled fault...at...
Unhandled prefetch abort...at...
2)BUG/BUG_ON
Oops - BUG...
例如:
Out of memory and no killable processes...
rbus timeout...
...
PS:WARN_ON只mp stacks,kernel還是正常
3)bad mode
Oops - bad mode...
日誌列印:
〃錯誤類型原因
[214.962667] 08:14:19.315 (2)-0488 Unable to handle kernel paging request at virtual address 6b6b6cl7
[214.973889] 08:14:19.326 (2)-0488 addr:6b6b6c17 pgd = d0824000
[214.980132] [6b6b6c17J •pgd=O000eO0e
〃Oopsttl誤碼序號
[214.983865] 08:14:19.336 (2)-0488 Internal error: Oops: 805 [#1] PREEMPT SMP ARM
[214.9914S3] Moles linked in: 8192eu ufsd(PO) jnl(O) fusion(O)
〃發生也錯誤的CPU序號
(215.001878] 08:14:19.354 (2)-0488 CPU: 2 PID: 488 Comm: system_server Tainted: P 4.4.3+ #113
(2)-0488 Hardware name: rtd284x
[215.011865] 08:14:19.364
〃當前PC指針 98:14:19.377 (2)-0488 PC is at mutex_unlo<k+0xc/0x38
(21S.024846] 08:14:19.383 (2)-0488 LR is at storage_pm_event+0xb4/0xe8
(21S.031026]
//Registers 08:14:19.390 (2)-0488 :[<ceb78ffc>] Ir : [<C0542034>] psr: 200f0013
I 215.037644] sp : ccf79e38 ip : eceoeeee fp : 9b34648c
I 215.037644]
08:14:19.404 (2)-0488 rlO: 00000080 r9 :Cl8b3864 r8 : oeeeeeoe
215.051370]
215.058692] 08:14:19.411 (2)-0488 P7 : C1293a98 P6 :C1293940 r5 : C1293940 r4 :C1293a80
21S.067345]
[ 215.076014] 08:14:19.420 (2)-0488 r3 : 00000033 r2 :00000000 ri : 000^000 re :6b6b6c07
[ 215.085307]
08:14:19.428 (2)-0488 Flags: nzCv IRQs on FIQs on Mode SVC 32 ISA ARM Segment user
08:14:19.438 (2)-0488 Control: 10c5383d Table: 1082406a DAC: 00000055
//Process.不 ,定是該process的錯誤,只是發生錯誤時,剛好在運行該process
[215.093168]
//Stacks 08:14:19.446 (2)-0488 Process syste«i_server (pid: 488, stack limit = 0xccf78218)
(21S.101827] 08:14:19.454 (2)-0488 Stack: 0xccf79e38 (Oxccf79d7。 to 0xccf7a08Q) - par(0xcf796d4)
---[ end trace 45d55384id6a0974 ]--- Kernel panic not syncing: Fatal exception
[217.359794] 08:14:21.712 (0)-0488
解決方案: kernel異常一般找晶元原廠協助分析。
系統卡頓時,一般先分三步走:
1、查看當前系統的CPU,IO等參數,輸入top、iotop命令: (如:iotop -s io -m 9)
如果有異常飆高的進程,kill掉後會發現系統恢復正常。
之前項目上遇到過某些U盤IO性能比較差,媒體中心又在後台掃描媒體問題,導致系統各種卡頓,io wait時間比較長。
2、系統進程卡住,觸發Watchdog:ps –A |grep system_server,一般而言,system_server正常的進程號是200多,如果發現進程號變成幾千,則可能出現重啟,結合tombstone和 /data/anr下的trace文件分析重啟原因
3、當前應用出現卡頓,造成ANR。輸入logcat | grep ANR,如果有ANR列印,再去/data/anr下面查看相應進程的traces文件
有時在應用裡面操作卡頓,按鍵響應延遲,但是卻沒有生成ANR,此時如果退出該應用(如果無法退出,在抓取足夠信息的情況下,可以串口直接kill掉卡頓的應用),則一切正常,可能是應用自身實現問題,或者調用了其它介面導致(例如曾遇到應用調用了中間件、mediaplayer某些介面導致操作嚴重卡頓,按鍵響應延遲),這種情況則需應用和相應介面的實現者去排查。
系統完全卡死,一般分三種情況
1,串口無響應,大概率kernel panic,
2,串口日誌狂輸出,把系統堵塞, 優化日誌輸出,關注關閉後壓測。
3,Input系統完全堵塞,導致任何輸入都無響應。
『叄』 android 怎麼編譯so文件
android NDK編譯多個so文件
android編譯系統的makefile文件Android.mk寫法如下
(1)Android.mk文件首先需要指定LOCAL_PATH變數,用於查找源文件。由於一般情況下
Android.mk和需要編譯的源文件在同一目錄下,所以定義成如下形式:
LOCAL_PATH:=$(call my-dir)
上面的語句的意思是將LOCAL_PATH變數定義成本文件所在目錄路徑。
(2)Android.mk中可以定義多個編譯模塊,每個編譯模塊都是以include $(CLEAR_VARS)開始
以include $(BUILD_XXX)結束。
include $(CLEAR_VARS)
CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE為你清除除LOCAL_PATH以外的所有LOCAL_XXX變數,
如LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_SHARED_LIBRARIES,LOCAL_STATIC_LIBRARIES等。
include $(BUILD_STATIC_LIBRARY)表示編譯成靜態庫
include $(BUILD_SHARED_LIBRARY)表示編譯成動態庫。
include $(BUILD_EXECUTABLE)表示編譯成可執行程序
(3)舉例如下(frameworks/base/libs/audioflinger/Android.mk):
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) 模塊一
ifeq ($(AUDIO_POLICY_TEST),true)
ENABLE_AUDIO_DUMP := true
endif
LOCAL_SRC_FILES:= \
AudioHardwareGeneric.cpp \
AudioHardwareStub.cpp \
AudioHardwareInterface.cpp
ifeq ($(ENABLE_AUDIO_DUMP),true)
LOCAL_SRC_FILES += AudioDumpInterface.cpp
LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP
endif
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
libmedia \
libhardware_legacy
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
LOCAL_CFLAGS += -DGENERIC_AUDIO
endif
LOCAL_MODULE:= libaudiointerface
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_SRC_FILES += A2dpAudioInterface.cpp
LOCAL_SHARED_LIBRARIES += liba2dp
LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
LOCAL_C_INCLUDES += $(call include-path-for, bluez)
endif
include $(BUILD_STATIC_LIBRARY) 模塊一編譯成靜態庫
include $(CLEAR_VARS) 模塊二
LOCAL_SRC_FILES:= \
AudioPolicyManagerBase.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libmedia
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_LDLIBS += -ldl
else
LOCAL_SHARED_LIBRARIES += libdl
endif
LOCAL_MODULE:= libaudiopolicybase
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_CFLAGS += -DWITH_A2DP
endif
ifeq ($(AUDIO_POLICY_TEST),true)
LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
endif
include $(BUILD_STATIC_LIBRARY) 模塊二編譯成靜態庫
include $(CLEAR_VARS) 模塊三
LOCAL_SRC_FILES:= \
AudioFlinger.cpp \
AudioMixer.cpp.arm \
AudioResampler.cpp.arm \
AudioResamplerSinc.cpp.arm \
AudioResamplerCubic.cpp.arm \
AudioPolicyService.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libbinder \
libmedia \
libhardware_legacy
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
LOCAL_CFLAGS += -DGENERIC_AUDIO
else
LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
endif
ifeq ($(TARGET_SIMULATOR),true)
LOCAL_LDLIBS += -ldl
else
LOCAL_SHARED_LIBRARIES += libdl
endif
LOCAL_MODULE:= libaudioflinger
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
LOCAL_SHARED_LIBRARIES += liba2dp
endif
ifeq ($(AUDIO_POLICY_TEST),true)
LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
endif
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(HOST_OS),linux)
LOCAL_LDLIBS += -lrt -lpthread
endif
endif
ifeq ($(BOARD_USE_LVMX),true)
LOCAL_CFLAGS += -DLVMX
LOCAL_C_INCLUDES += vendor/nxp
LOCAL_STATIC_LIBRARIES += liblifevibes
LOCAL_SHARED_LIBRARIES += liblvmxservice
# LOCAL_SHARED_LIBRARIES += liblvmxipc
endif
include $(BUILD_SHARED_LIBRARY) 模塊三編譯成動態庫
(4)編譯一個應用程序(APK)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory-->直譯(建立在java子目錄中的所有Java文件)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build-->直譯(創建APK的名稱)
LOCAL_PACKAGE_NAME := LocalPackage
# Tell it to build an APK-->直譯(告訴它來建立一個APK)
include $(BUILD_PACKAGE)
(5)編譯一個依賴於靜態Java庫(static.jar)的應用程序
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# List of static libraries to include in the package
LOCAL_STATIC_JAVA_LIBRARIES := static-library
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
# Tell it to build an APK
include $(BUILD_PACKAGE)
(6)編譯一個需要用平台的key簽名的應用程序
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_CERTIFICATE := platform
# Tell it to build an APK
include $(BUILD_PACKAGE)
(7)編譯一個需要用特定key前面的應用程序
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Name of the APK to build
LOCAL_PACKAGE_NAME := LocalPackage
LOCAL_CERTIFICATE := vendor/example/certs/app
# Tell it to build an APK
include $(BUILD_PACKAGE)
(8)添加一個預編譯應用程序
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Mole name should match apk name to be installed.
LOCAL_MODULE := LocalMoleName
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)
(9)添加一個靜態JAVA庫
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Build all java files in the java subdirectory
LOCAL_SRC_FILES := $(call all-subdir-java-files)
# Any libraries that this library depends on
LOCAL_JAVA_LIBRARIES := android.test.runner
# The name of the jar file to create
LOCAL_MODULE := sample
# Build a static jar file.
include $(BUILD_STATIC_JAVA_LIBRARY)
(10)Android.mk的編譯模塊中間可以定義相關的編譯內容,也就是指定相關的變數如下:
LOCAL_AAPT_FLAGS
LOCAL_ACP_UNAVAILABLE
LOCAL_ADDITIONAL_JAVA_DIR
LOCAL_AIDL_INCLUDES
LOCAL_ALLOW_UNDEFINED_SYMBOLS
LOCAL_ARM_MODE
LOCAL_ASFLAGS
LOCAL_ASSET_DIR
LOCAL_ASSET_FILES 在Android.mk文件中編譯應用程序(BUILD_PACKAGE)時設置此變數,表示資源文件,
通常會定義成LOCAL_ASSET_FILES += $(call find-subdir-assets)
LOCAL_BUILT_MODULE_STEM
LOCAL_C_INCLUDES 額外的C/C++編譯頭文件路徑,用LOCAL_PATH表示本文件所在目錄
舉例如下:
LOCAL_C_INCLUDES += extlibs/zlib-1.2.3
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_CC 指定C編譯器
LOCAL_CERTIFICATE 簽名認證
LOCAL_CFLAGS 為C/C++編譯器定義額外的標志(如宏定義),舉例:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1
LOCAL_CLASSPATH
LOCAL_COMPRESS_MODULE_SYMBOLS
LOCAL_COPY_HEADERS install應用程序時需要復制的頭文件,必須同時定義LOCAL_COPY_HEADERS_TO
LOCAL_COPY_HEADERS_TO install應用程序時復制頭文件的目的路徑
LOCAL_CPP_EXTENSION 如果你的C++文件不是以cpp為文件後綴,你可以通過LOCAL_CPP_EXTENSION指定C++文件後綴名
如:LOCAL_CPP_EXTENSION := .cc
注意統一模塊中C++文件後綴必須保持一致。
LOCAL_CPPFLAGS 傳遞額外的標志給C++編譯器,如:LOCAL_CPPFLAGS += -ffriend-injection
LOCAL_CXX 指定C++編譯器
LOCAL_DX_FLAGS
LOCAL_EXPORT_PACKAGE_RESOURCES
LOCAL_FORCE_STATIC_EXECUTABLE 如果編譯的可執行程序要進行靜態鏈接(執行時不依賴於任何動態庫),則設置LOCAL_FORCE_STATIC_EXECUTABLE:=true
目前只有libc有靜態庫形式,這個只有文件系統中/sbin目錄下的應用程序會用到,這個目錄下的應用程序在運行時通常
文件系統的其它部分還沒有載入,所以必須進行靜態鏈接。
LOCAL_GENERATED_SOURCES
LOCAL_INSTRUMENTATION_FOR
LOCAL_INSTRUMENTATION_FOR_PACKAGE_NAME
LOCAL_INTERMEDIATE_SOURCES
LOCAL_INTERMEDIATE_TARGETS
LOCAL_IS_HOST_MODULE
LOCAL_JAR_MANIFEST
LOCAL_JARJAR_RULES
LOCAL_JAVA_LIBRARIES 編譯java應用程序和庫的時候指定包含的java類庫,目前有core和framework兩種
多數情況下定義成:LOCAL_JAVA_LIBRARIES := core framework
注意LOCAL_JAVA_LIBRARIES不是必須的,而且編譯APK時不允許定義(系統會自動添加)
LOCAL_JAVA_RESOURCE_DIRS
LOCAL_JAVA_RESOURCE_FILES
LOCAL_JNI_SHARED_LIBRARIES
LOCAL_LDFLAGS 傳遞額外的參數給連接器(務必注意參數的順序)
LOCAL_LDLIBS 為可執行程序或者庫的編譯指定額外的庫,指定庫以"-lxxx"格式,舉例:
LOCAL_LDLIBS += -lcurses -lpthread
LOCAL_LDLIBS += -Wl,-z,origin
LOCAL_MODULE 生成的模塊的名稱(注意應用程序名稱用LOCAL_PACKAGE_NAME而不是LOCAL_MODULE)
LOCAL_MODULE_PATH 生成模塊的路徑
LOCAL_MODULE_STEM
LOCAL_MODULE_TAGS 生成模塊的標記
LOCAL_NO_DEFAULT_COMPILER_FLAGS
LOCAL_NO_EMMA_COMPILE
LOCAL_NO_EMMA_INSTRUMENT
LOCAL_NO_STANDARD_LIBRARIES
LOCAL_OVERRIDES_PACKAGES
LOCAL_PACKAGE_NAME APK應用程序的名稱
LOCAL_POST_PROCESS_COMMAND
LOCAL_PREBUILT_EXECUTABLES 預編譯including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)時所用,指定需要復制的可執行文件
LOCAL_PREBUILT_JAVA_LIBRARIES
LOCAL_PREBUILT_LIBS 預編譯including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)時所用, 指定需要復制的庫.
LOCAL_PREBUILT_OBJ_FILES
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
LOCAL_PRELINK_MODULE 是否需要預連接處理(默認需要,用來做動態庫優化)
LOCAL_REQUIRED_MODULES 指定模塊運行所依賴的模塊(模塊安裝時將會同步安裝它所依賴的模塊)
LOCAL_RESOURCE_DIR
LOCAL_SDK_VERSION
LOCAL_SHARED_LIBRARIES 可鏈接動態庫
LOCAL_SRC_FILES 編譯源文件
LOCAL_STATIC_JAVA_LIBRARIES
LOCAL_STATIC_LIBRARIES 可鏈接靜態庫
LOCAL_UNINSTALLABLE_MODULE
LOCAL_UNSTRIPPED_PATH
LOCAL_WHOLE_STATIC_LIBRARIES 指定模塊所需要載入的完整靜態庫(這些精通庫在鏈接是不允許鏈接器刪除其中無用的代碼)
LOCAL_YACCFLAGS
OVERRIDE_BUILT_MODULE_PATH
『肆』 android軟體開發的架構
Android以Java為編程語言,使介面到功能,都有層出不窮的變化,其中Activity等同於J2ME的MIDlet,一個 Activity 類(class)負責如世御創建視窗(window),一個活動中的Activity就是在 foreground(前景)模式,背景運行的程序叫做Service。兩者之間通過由和AIDL連結,達到復數程序同時運行的效果。如果運行中的 Activity 全部畫面被其他 Activity 取代時,該 Activity 便被停止(stopped),甚至被系統清除(kill)。
View等同於J2ME的Displayable,程序人員可以通過 View 類與「XML layout」檔將UI放置在視窗上,Android 1.5的版本可以利用 View 打造出所謂的 Widgets,其實Widget只是View的一種,所以可以使用xml來設計layout,HTC的Android Hero手機即含有大量的widget。至於ViewGroup 是各種layout 的基礎抽象類(abstract class),ViewGroup之內還可以有ViewGroup。View的構造函數不需要在Activity中調用,但是Displayable的是必須的,在Activity 中,要通過()來從XML 中取得View,Android的View類的顯示很大程度上是從XML中讀取的。View 與事件(event)息息相關,兩者之間通過Listener 結合在一起,每一個渣岩View都可以注冊一個event listener,例如:當View要處理用戶觸碰(touch)的事件時,就要向Android框架注冊View.。另外還有Image等同於J2ME的BitMap。 在模擬器上運行模擬是虛擬設備(AVD),我們需要配置來運行我們的Android應用程序。步驟1、開放的AVD管理步驟2、新的按鈕,點擊添加新設備,並配置您的設備設置。步驟3、會有一個結果窗口顯示所有已配置你上一屏幕選擇。步驟4、按「確定」,你將會看到你的設備列在有你可以關閉此窗口。步驟5、運行你的Android應用程序項目從Eclipse,如果只有一個AVD配置,它會自動部署的應用程序也會出現一個窗口,選擇你的圖片。 模擬器將開始。在設備上運行
Android應用程序可以直接部署在Android設備上,這幾個配置所需要的。步驟1、在調試模式的設置可以設置應用程序:Android的<應用程序>元真可調試屬性。ADT 8這是默認的。步驟2、您的設備上啟用USB調試:Android 3.2或以上轉至設置>應用程序>開發和啟用USB調試。在Android 4更新,這是開發商選擇設置>。註:在Android 4.2更新,開發者選項是默認隱藏。可以,去設定>android的版本號。返回先前屏幕找到開發商選擇。步返敗驟3、安裝USB驅動程序為您的設備,計算機識別你的設備。步驟4、一旦設置和您的設備通過USB連接,從Eclipse菜單欄安裝您的應用程序在設備上選擇運行>運行(或運行>調試)。 操作系統與應用程序的溝通橋梁,並用分為兩層:函數層(Library)和虛擬機(Virtual Machine)。 Bionic是 Android 改良libc的版本。Android 同時包含了Webkit,所謂的Webkit 就是Apple Safari瀏覽器背後的引擎。Surface flinger 是就2D或3D的內容顯示到屏幕上。Android使用工具鏈(Toolchain)為Google自製的Bionic Libc。
Android採用OpenCORE作為基礎多媒體框架。OpenCORE可分7大塊:PVPlayer、PVAuthor、Codec、PacketVideo Multimedia Framework(PVMF)、Operating SystemLibrary(OSCL)、Common、OpenMAX。
Android 使用skia 為核心圖形引擎,搭配OpenGL/ES。skia與Linux Cairo功能相當,但相較於Linux Cairo, skia 功能還只是陽春型的。2005年Skia公司被Google收購,2007年初,Skia GL源碼被公開,Skia 也是Google Chrome 的圖形引擎。
Android的多媒體資料庫採用SQLite資料庫系統。資料庫又分為共用資料庫及私用資料庫。用戶可通過類(Column)取得共用資料庫。
Android的中間層多以Java 實現,並且採用特殊的Dalvik虛擬機(Dalvik Virtual Machine)。Dalvik虛擬機是一種「暫存器型態」(Register Based)的Java虛擬機,變數皆存放於暫存器中,虛擬機的指令相對減少。
Dalvik虛擬機可以有多個實例(instance), 每個Android應用程序都用一個自屬的Dalvik虛擬機來運行,讓系統在運行程序時可達到優化。Dalvik虛擬機並非運行Java位元組碼(Bytecode),而是運行一種稱為.dex格式的文件。 Android 的 HAL(硬體抽像層)是能以封閉源碼形式提供硬體驅動模塊。HAL 的目的是為了把 Android framework 與 Linux kernel 隔開,讓 Android 不至過度依賴 Linux kernel,以達成 kernel independent 的概念,也讓 Android framework 的開發能在不考慮驅動程序實現的前提下進行發展。
HAL stub 是一種代理人(proxy)的概念,stub 是以 *.so 檔的形式存在。Stub 向 HAL「提供」操作函數(operations),並由 Android runtime 向 HAL 取得 stub 的 operations,再 callback 這些操作函數。HAL 里包含了許多的 stub(代理人)。Runtime 只要說明「類型」,即 mole ID,就可以取得操作函數。 Android 是運行於 Linux kernel之上,但並不是GNU/Linux。因為在一般GNU/Linux 里支持的功能,Android 大都沒有支持,包括Cairo、X11、Alsa、FFmpeg、GTK、Pango及Glibc等都被移除掉了。Android又以bionic 取代Glibc、以Skia 取代Cairo、再以opencore 取代FFmpeg 等等。Android 為了達到商業應用,必須移除被GNU GPL授權證所約束的部份,例如Android將驅動程序移到 userspace,使得Linux driver 與 Linux kernel徹底分開。bionic/libc/kernel/ 並非標準的kernel header files。Android 的 kernel header 是利用工具由 Linux kernel header 所產生的,這樣做是為了保留常數、數據結構與宏。
Android 的 Linux kernel控制包括安全(Security),存儲器管理(Memory Managemeat),程序管理(Process Management),網路堆棧(Network Stack),驅動程序模型(Driver Model)等。下載Android源碼之前,先要安裝其構建工具Repo來初始化源碼。Repo 是 Android 用來輔助Git工作的一個工具。
『伍』 標准C程序如何移植到android平台需要什麼編譯參數,我用的是arm-eabi-gcc工具鏈
首先 你編譯的程序 用cat命令看 目錄應該是/lib/libc.so.6 而Android 是 /system/lib/libc.so
本來已經有C庫了 但是 我這里沒有用~ 你可以在 你的PC安裝機子上找到 交叉編譯arm-eabi-gcc或 arm-linux-gcc安裝目錄下的arm/4.3.2/..../libc/lib/libc2.8.so好像 是這個 有一個是libc.so.6 這個是一個連接 不是文件 查看屬性 看下連接的文件應該是 那個libc2.8.so (放心它是arm版的)拷貝這個文件到 安卓/system/lib/去 然後
用RE文件 為它添加一個 連接 /system/lib/libc.so.6 就可以了
這樣 標准庫是被移植去了 但是 你的應用程序 有可能使用的是 /lib/libc.so.6 不是 /system/lib/libc.so.6
先測試不行的話 就把 /system/lib 連接為/lib