在分析Android Native Error這一類問題的時候汁胆,如果能抓到異常進(jìn)程的coredump文件,那么對分析該問題是事半功倍的遵班,但是由于在抓取coredump文件的時候屠升,需要消耗很多的內(nèi)存和CPU資源,并且保存的文件也都很大狭郑,所以用戶最終使用的版本都是默認(rèn)關(guān)閉的腹暖,即使在內(nèi)部研發(fā)階段,也只是某些特定的測試項里面才會打開翰萨,例如針對系統(tǒng)穩(wěn)定性的monkey測試脏答,所以有的時候穩(wěn)定性問題其實不是很難分析,難的是獲取有效的Log亩鬼,抓取到了coredump文件殖告,同時有這個固件對應(yīng)的symbole的話就可以使用GDB這一個調(diào)試?yán)鱽矸治鰡栴}了.
Coredump文件
coredump文件可以理解為是進(jìn)程某個時刻的內(nèi)存和寄存器快照,最終用ELF文件把這些內(nèi)容包裝一下雳锋,就可以使用GDB等工具來分析了黄绩,Kernel默認(rèn)是支持Coredump的,但是在Android上面還有幾個重要的因素影響到是否會抓取coredump.
Linux當(dāng)中每個進(jìn)程可以使用的資源是有限制的,可以通過查看/proc/$PID/limits這個文件來查看玷过,例如
進(jìn)程rlimit
從這個節(jié)點的信息可以看到爽丹,這個進(jìn)程允許打開的文件個數(shù)是1024,而它的core file size是0辛蚊,所以當(dāng)前這個進(jìn)程是即使收到了相關(guān)的信號粤蝎,它也是無法抓取coredump的,所以一般要修改進(jìn)程的rlimit.
/proc/sys/kernel/core_pattern設(shè)置coredump文件的保存路徑,例如 echo " /data/corefile/core-%e-%p" > /proc/sys/kernel/core_pattern
另外還可能要執(zhí)行 echo 1 > /proc/sys/fs/suid_dumpable.
進(jìn)程只有在接收到某些特定的信號時袋马,才會去抓coredump初澎,比如SIGSEGV、SIGABRT飞蛹、SIGBUS等等,同時要注意在抓取某個進(jìn)程的coredump文件的時候谤狡,不能發(fā)送SIGKILL信號給該進(jìn)程灸眼,SIGKILL會終止抓取動作,導(dǎo)致抓出來的coredump文件不完整墓懂,無法分析.
GDB
- GDB在線調(diào)試環(huán)境
GDB焰宣,GNU Project Debugger,大名鼎鼎的調(diào)試?yán)鞑蹲校瑢τ谖覀兂绦騿T來說匕积,即使沒用過但應(yīng)該也不陌生吧,GDB它可以在線調(diào)試榜跌,也可以離線調(diào)試coredump等內(nèi)存轉(zhuǎn)儲文件闪唆,在穩(wěn)定性日常工作中,我們主要用它來離線分析coredump文件.
- adb shell gdbserver remote:1234 --attach 4321
1234是手機端的端口钓葫,4321是你要debug的進(jìn)程PID.- adb forward tcp:1234 tcp:1234
設(shè)置adb tcp端口轉(zhuǎn)發(fā)悄蕾,前一個tcp:1234是指PC端的端口,后一個是Target础浮,也就是手機端的.- aarch64-linux-android-gdb
aarch64-linux-android-gdb是針對ARM64的gdb客戶端帆调,相應(yīng)的對于以AARCH32來執(zhí)行的進(jìn)程,需要選擇相應(yīng)的gdb客戶端.- 在GDB命令行里面執(zhí)行以下命令:
(gdb) set solib-absolute-prefix out/target/product/general/symbols/
(gdb) set solib-search-path out/target/product/general/symbols/
(gdb) target remote :1234
更多的信息請見搭建Android GDB在線調(diào)試環(huán)境
- GDB + Eclipse 離線調(diào)試
工欲善其事必先利其器豆同,分析NE問題可以使用命令行形式的GDB工具番刊,如果你熟悉GDB的各種命令,那么命令行的方式可以讓你得心應(yīng)手影锈,另外也還可以使用GDB + Eclipse打造一個可視化的調(diào)試環(huán)境芹务,雖然功能沒有命令行強大,但是對我們分析簡單的問題足矣,下面介紹如何搭建環(huán)境:
1鸭廷、打開ADT之后枣抱,依次點擊Run → Debug Configration,然后選擇C/C++ Postmortem Debugger
2、點擊左上角的 "+"符號靴姿,新建一個配置沃但,并隨機取一個名字磁滚,例如“android_gdb”, C/C++ Appliacation選擇你的Coredump文件對應(yīng)的可執(zhí)行文件佛吓,例如SurfaceFlinger,可以選擇/symbols/system/bin/surfaceflinger,但是由zygote派生出來的進(jìn)程要選擇/symbols/system/bin/app_process64, 同時 Post Mortem file type選擇 Core file垂攘,點擊Browse定位到Coredump文件.
3维雇、切換到Debugger選項卡,GDB debugger選擇對應(yīng)平臺的gdb可執(zhí)行文件晒他,GDB command file對應(yīng)的文件是你想在打開coredump文件之后想要執(zhí)行的gdb命令吱型,我的gdbinit文件內(nèi)容是: set solib-search-path /media/xxxx/SSD/tmp/Log/0622/symbols/system/lib64 設(shè)置GDB的lib庫查找路徑,這樣GDB就可以把帶符號信息的so庫加載進(jìn)去了
4、點擊Debug按鈕之后會出現(xiàn)完整的debug視圖
- GDB腳本
GDB腳本 gdb支持兩種腳本:python腳本和命令腳本陨仅,在命令腳本中我們可以自定義命令津滞,其形式類似于:
??define commandName
???statement
???......
??end
其中 statement可以是任何有效的GDB命令铝侵,此外自定義命令還支持最多10個輸入?yún)?shù):$arg0,$arg1 …… $arg9触徐,并且還用$argc來標(biāo)明一共傳入了多少參數(shù)咪鲜,另外腳本也提供了if else等條件判斷語句和while循環(huán)語句,可以直接在命令行里面編輯gdb腳本撞鹉,也可以寫到一個單獨的文件里面疟丙,然后使用source命令加載進(jìn)來.
- GDB調(diào)試coredump示例
在monkey測試過程中,發(fā)現(xiàn)有一臺機器卡屏了鸟雏,通過log分析到可能是system_server進(jìn)程的ART虛擬機在抓取trace或者gc時候享郊,調(diào)用SuspendAll的時候超時了,這種情況以前也遇到過孝鹊,也是抓coredump文件來分析的炊琉,所以這一次我們也是直接發(fā)送了kill -11信號給system_server進(jìn)程,然后抓到coredump文件.拿到了coredump文件之后又活,還需要這個固件對應(yīng)的symbole文件分析.
[Linux@Linux w]$ls
core-system_server-3060 symbols symbols.zip
[Linux@Linux w]$aarch64-linux-android-gdb ./symbols/system/bin/app_process64 ./core-system_server-3060
GNU gdb (GDB) 7.7
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=aarch64-elf-linux".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://source.android.com/source/report-bugs.html>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./symbols/system/bin/app_process64...done.
[New LWP 3060]
[New LWP 3065]
[New LWP 3173]
[New LWP 3067]
[New LWP 3066]
......
......
[New LWP 3954]
[New LWP 3086]
[New LWP 3128]
warning: Could not load shared library symbols for 194 libraries, e.g. /system/bin/linker64.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000007962221cac in ?? ()
(gdb) set solib-search-path ./symbols/system/lib64/
Reading symbols from /media/linux/SSD/tmp/Log/DoNotRemove/symbols/system/lib64/libcutils.so...done.
Loaded symbols for /media/linux/SSD/tmp/Log/DoNotRemove/symbols/system/lib64/libcutils.so
Reading symbols from /media/xxxx/SSD/tmp/Log/DoNotRemove/symbols/system/lib64/libutils.so...done.
Loaded symbols for /media/linux/SSD/tmp/Log/DoNotRemove/symbols/system/lib64/libutils.so
Reading symbols from /media/xxxx/SSD/tmp/Log/DoNotRemove/symbols/system/lib64/liblog.so...done.
Loaded symbols for /media/linux/SSD/tmp/Log/DoNotRemove/symbols/system/lib64/liblog.so
......
(gdb) bt
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
#1 0x000000795f0c63dc in futex (uaddr=0x795f6fa910, op=0, val=17669, val3=0, timeout=<optimized out>, uaddr2=<optimized out>) at art/runtime/base/mutex-inl.h:45
#2 art::ConditionVariable::WaitHoldingLocks (this=<optimized out>, self=<optimized out>) at art/runtime/base/mutex.cc:848
#3 0x000000795f3272e8 in TransitionFromSuspendedToRunnable (this=<optimized out>) at art/runtime/thread-inl.h:209
#4 ScopedThreadStateChange (self=<optimized out>, new_thread_state=art::kRunnable, this=<optimized out>) at art/runtime/scoped_thread_state_change.h:51
#5 ScopedObjectAccessUnchecked (this=<optimized out>, env=<optimized out>) at art/runtime/scoped_thread_state_change.h:224
#6 ScopedObjectAccess (this=<optimized out>, env=<optimized out>) at art/runtime/scoped_thread_state_change.h:255
#7 art::JNI::NewStringUTF (env=<optimized out>, utf=<optimized out>) at art/runtime/jni_internal.cc:1646
#8 0x0000007961acce64 in NewStringUTF (bytes=<optimized out>, this=0x795f63e180) at libnativehelper/include/nativehelper/jni.h:842
#9 android::android_content_AssetManager_getArrayStringResource (env=0x795f63e180, clazz=<optimized out>, arrayResId=<optimized out>) at frameworks/base/core/jni/android_util_AssetManager.cpp:1977
#10 0x00000000748f498c in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)
因為這個機器是虛擬機在suspend all的時候卡住的温自,分析代碼,這里卡住的話一般是因為某些線程沒有及時響應(yīng)suspend flag皇钞,而不響應(yīng)的話一般是這個線程的狀態(tài)是mRunnable狀態(tài)悼泌,注意這里指的是ART的線程狀態(tài)不是Linux的R狀態(tài),這兩個之間還是有區(qū)別的夹界,那我們的思路就是要從coredump文件找出是哪個線程還在mRunnable狀態(tài)馆里,因為所有的Java線程對應(yīng)的art::Thread對象都在ThreadList的list_域變量里面,所以我們只要把這個list_對象內(nèi)容打印出來可柿,就可以找到是哪個Java線程是mRunnable狀態(tài).
// The actual list of all threads.
std::list<Thread*> list_ GUARDED_BY(Locks::thread_list_lock_);
而要打印這個list_的內(nèi)容的話鸠踪,需要從上下文里面找到ThreadList對象,這個可以通過Runtime的全局變量推導(dǎo)出來复斥,也可以找到哪個線程的調(diào)用堆棧上下文里面有這個ThreadList對象的营密,然后找出來,我們這里選用第二種方式目锭,因為ThreadList::SuspendAllInternal的方法恰好就有this參數(shù)评汰,通過this就很容易找到ThreadList對象,在虛擬機中調(diào)用這個的地方只有SignalCatcher 或者HeapTaskDaemon線程痢虹,他們一個負(fù)責(zé)打印trace被去,一個負(fù)責(zé)執(zhí)行g(shù)c task,所以先從現(xiàn)場或者log里面找到這兩個線程的pid奖唯,然后通過gdb來查看他們當(dāng)前的堆棧. 我們找到這兩個線程的pid分別為3065和3070.
(gdb) info threads
Id Target Id Frame
180 LWP 3128 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
179 LWP 3086 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
178 LWP 3954 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
177 LWP 3218 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
......
127 LWP 3167 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
---Type <return> to continue, or q <return> to quit---
因為GDB對線程重新編了號惨缆,所以我們要找到3065和3070對應(yīng)的編號,而且我們看到在GDB里面有輸出
“---Type <return> to continue, or q <return> to quit---”這樣的內(nèi)容,這個是因為GDB默認(rèn)對于輸出內(nèi)容很長的做了截斷坯墨,可以通過set pagination off來改變這種行為.
(gdb) set pagination off
(gdb) info threads
......
9 LWP 3070 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
......
2 LWP 3065 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
(gdb) t 2
[Switching to thread 2 (LWP 3065)]
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
41 bionic/libc/arch-arm64/bionic/syscall.S: 沒有那個文件或目錄.
(gdb) bt
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
#1 0x000000795f0c63dc in futex (uaddr=0x795f6fa910, op=0, val=17669, val3=0, timeout=<optimized out>, uaddr2=<optimized out>) at art/runtime/base/mutex-inl.h:45
#2 art::ConditionVariable::WaitHoldingLocks (this=<optimized out>, self=<optimized out>) at art/runtime/base/mutex.cc:848
#3 0x000000795f120234 in TransitionFromSuspendedToRunnable (this=<optimized out>) at art/runtime/thread-inl.h:209
#4 ScopedThreadStateChange (new_thread_state=art::kRunnable, this=<optimized out>, self=<optimized out>) at art/runtime/scoped_thread_state_change.h:51
#5 ScopedObjectAccessUnchecked (this=<optimized out>, self=<optimized out>) at art/runtime/scoped_thread_state_change.h:231
#6 ScopedObjectAccess (self=<optimized out>, this=<optimized out>) at art/runtime/scoped_thread_state_change.h:261
#7 art::ClassLinker::DumpForSigQuit (this=<optimized out>, os=...) at art/runtime/class_linker.cc:7752
#8 0x000000795f415950 in art::Runtime::DumpForSigQuit (this=0x795f6ec000, os=...) at art/runtime/runtime.cc:1401
#9 0x000000795f41c27c in art::SignalCatcher::HandleSigQuit (this=<optimized out>) at art/runtime/signal_catcher.cc:145
#10 0x000000795f41ad3c in art::SignalCatcher::Run (arg=<optimized out>) at art/runtime/signal_catcher.cc:214
#11 0x000000796226e0f0 in __pthread_start (arg=<optimized out>) at bionic/libc/bionic/pthread_create.cpp:198
#12 0x0000007962223944 in __start_thread (fn=0x62, arg=0x795f6fa910) at bionic/libc/bionic/clone.cpp:41
#13 0x0000000000000000 in ?? ()
(gdb) t 9
[Switching to thread 9 (LWP 3070)]
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
41 in bionic/libc/arch-arm64/bionic/syscall.S
(gdb) bt
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
#1 0x000000795f43bb2c in futex (val3=0, uaddr=<optimized out>, op=<optimized out>, val=<optimized out>, timeout=<optimized out>, uaddr2=<optimized out>) at art/runtime/base/mutex-inl.h:45
#2 art::ThreadList::SuspendAllInternal (this=<optimized out>, self=<optimized out>, ignore1=<optimized out>, ignore2=<optimized out>, debug_suspend=<optimized out>) at art/runtime/thread_list.cc:586
#3 0x000000795f43c198 in art::ThreadList::SuspendAll (this=0x795f6fb000, cause=0x795f55e996 "ScopedPause", long_suspend=<optimized out>) at art/runtime/thread_list.cc:476
#4 0x000000795f1c6d4c in art::gc::collector::MarkSweep::RunPhases (this=<optimized out>) at art/runtime/gc/collector/mark_sweep.cc:153
#5 0x000000795f1bf490 in art::gc::collector::GarbageCollector::Run (this=0x795f687500, gc_cause=art::gc::kGcCauseBackground, clear_soft_references=false) at art/runtime/gc/collector/garbage_collector.cc:87
#6 0x000000795f1ef0a4 in art::gc::Heap::CollectGarbageInternal (this=<optimized out>, gc_type=<optimized out>, gc_cause=<optimized out>, clear_soft_references=<optimized out>) at art/runtime/gc/heap.cc:2719
#7 0x000000795f1f65dc in art::gc::Heap::ConcurrentGC (this=0x795f64b700, self=<optimized out>, force_full=true) at art/runtime/gc/heap.cc:3722
#8 0x000000795f1fd668 in art::gc::Heap::ConcurrentGCTask::Run (this=<optimized out>, self=0x0) at art/runtime/gc/heap.cc:3685
#9 0x000000795f21f2c4 in art::gc::TaskProcessor::RunAllTasks (this=<optimized out>, self=<optimized out>) at art/runtime/gc/task_processor.cc:124
#10 0x0000000072739114 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb)
從上面gdb命令的執(zhí)行結(jié)果來看寂汇,ThreadList對象的地址是0x795f6fb000,那么可以通過它找到保存了所有Thread對象的list_地址.
#3 0x000000795f43c198 in art::ThreadList::SuspendAll (this=0x795f6fb000, cause=0x795f55e996 "ScopedPause", long_suspend=<optimized out>) at art/runtime/thread_list.cc:476.
(gdb) set print pretty on
(gdb) f 3
#3 0x000000795f43c198 in art::ThreadList::SuspendAll (this=0x795f6fb000, cause=0x795f55e996 "ScopedPause", long_suspend=<optimized out>) at art/runtime/thread_list.cc:476
476 in art/runtime/thread_list.cc
(gdb) p *this
$2 = {
static kMaxThreadId = 65535,
static kInvalidThreadId = 0,
static kMainThreadId = 1,
allocated_ids_ = {
<std::__1::__bitset<1024, 65535>> = {
static __bits_per_word = 64,
__first_ = {18446744073709551615, 18446744073709551615, 38654705663, 0 <repeats 1021 times>}
},
members of std::__1::bitset<65535>:
static __n_words = 1024
},
list_ = {
<std::__1::__list_imp<art::Thread*, std::__1::allocator<art::Thread*> >> = {
__end_ = {
__prev_ = 0x792df94ee0,
__next_ = 0x795f6fa9a0
},
__size_alloc_ = {
<std::__1::__libcpp_compressed_pair_imp<unsigned long, std::__1::allocator<std::__1::__list_node<art::Thread*, void*> >, 2>> = {
<std::__1::allocator<std::__1::__list_node<art::Thread*, void*> >> = {<No data fields>},
members of std::__1::__libcpp_compressed_pair_imp<unsigned long, std::__1::allocator<std::__1::__list_node<art::Thread*, void*> >, 2>:
__first_ = 161
}, <No data fields>}
}, <No data fields>},
suspend_all_count_ = 1,
debug_suspend_all_count_ = 0,
......
}
因為list_是一個很長的列表,所以這里先自定義一個GDB命令捣染,用來自動打印每個Thread對象內(nèi)容
(gdb) def dump_all_threads_state
Type commands for definition of "dump_all_threads_state".
End with a line saying just "end".
> set $current = list_.__end_.__next_
> while $current != 0
> p * $current.__value_
> set $current = $current.__next_
> end
>end
(gdb) dump_all_threads_state
$3 = {
static kStackOverflowImplicitCheckSize = 8192,
static kMaxCheckpoints = 3,
static kMaxSuspendBarriers = 3,
static is_started_ = true,
static pthread_key_self_ = -2147483634,
static resume_cond_ = 0x795f6fa900,
static is_sensitive_thread_hook_ = 0x7961a76f20 <android::runtime_isSensitiveThread()>,
static jit_sensitive_thread_ = 0x0,
tls32_ = {
state_and_flags = {
as_struct = {
flags = 1,
state = 89
},
as_atomic_int = {
<std::__1::atomic<int>> = {
<std::__1::__atomic_base<int, true>> = {
<std::__1::__atomic_base<int, false>> = {
__a_ = 5832705
}, <No data fields>}, <No data fields>}, <No data fields>},
as_int = 5832705
},
suspend_count = 1,
debug_suspend_count = 0,
thin_lock_thread_id = 1,
tid = 3060,
daemon = 0,
throwing_OutOfMemoryError = 0,
no_thread_suspension = 0,
thread_exit_check_count = 0,
handling_signal_ = 0,
suspended_at_suspend_check = 0,
ready_for_debug_invoke = 0,
debug_method_entry_ = 0,
is_gc_marking = 0,
weak_ref_access_enabled = 1,
disable_thread_flip_count = 0
},
tls64_ = {
trace_clock_base = 0,
stats = {
allocated_objects = 0,
allocated_bytes = 0,
freed_objects = 0,
freed_bytes = 0,
gc_for_alloc_count = 0,
class_init_count = 2682,
class_init_time_ns = 1043034049
}
},
tlsPtr_ = {
card_table = 0x795ad01070 "",
exception = 0x0,
stack_end = 0x7ffbc34000 "",
managed_stack = {
top_quick_frame_ = 0x7ffc42d940,
link_ = 0x7ffc42e050,
top_shadow_frame_ = 0x0
},
suspend_trigger = 0x0,
jni_env = 0x795f63e180,
tmp_jni_env = 0x0,
self = 0x0,
opeer = 0x762523e8,
jpeer = 0x0,
stack_begin = 0x7ffbc32000 "",
stack_size = 8388608,
stack_trace_sample = 0x0,
wait_next = 0x0,
monitor_enter_object = 0x0,
top_handle_scope = 0x7ffc42d948,
class_loader_override = 0x10070a,
long_jump_context = 0x795f687c80,
instrumentation_stack = 0x795f716e90,
debug_invoke_req = 0x0,
single_step_control = 0x0,
stacked_shadow_frame_record = 0x0,
deoptimization_context_stack = 0x0,
frame_id_to_shadow_frame = 0x0,
name = 0x795f6fa980,
pthread_self = 521358133912,
last_no_thread_suspension_cause = 0x0,
checkpoint_functions = {0x0, 0x0, 0x0},
active_suspend_barriers = {0x0, 0x0, 0x0},
jni_entrypoints = {
pDlsymLookup = 0x795f0b04d0 <art_jni_dlsym_lookup_stub>
},
quick_entrypoints = {
pAllocArray = 0x795f0b4420 <art_quick_alloc_array_rosalloc>,
pAllocArrayResolved = 0x795f0b44e0 <art_quick_alloc_array_resolved_rosalloc>,
pAllocArrayWithAccessCheck = 0x795f0b45a0 <art_quick_alloc_array_with_access_check_rosalloc>,
pAllocObject = 0x795f0b9cc0 <art_quick_alloc_object_rosalloc>,
pAllocObjectResolved = 0x795f0b41e0 <art_quick_alloc_object_resolved_rosalloc>,
pAllocObjectInitialized = 0x795f0b42a0 <art_quick_alloc_object_initialized_rosalloc>,
pAllocObjectWithAccessCheck = 0x795f0b4360 <art_quick_alloc_object_with_access_check_rosalloc>,
pCheckAndAllocArray = 0x795f0b4660 <art_quick_check_and_alloc_array_rosalloc>,
pCheckAndAllocArrayWithAccessCheck = 0x795f0b4720 <art_quick_check_and_alloc_array_with_access_check_rosalloc>,
pAllocStringFromBytes = 0x795f0b47e0 <art_quick_alloc_string_from_bytes_rosalloc>,
pAllocStringFromChars = 0x795f0b48f0 <art_quick_alloc_string_from_chars_rosalloc>,
pAllocStringFromString = 0x795f0b49b0 <art_quick_alloc_string_from_string_rosalloc>,
pInstanceofNonTrivial = 0x795f516374 <artIsAssignableFromCode(art::mirror::Class*, art::mirror::Class*)>,
pCheckCast = 0x795f0b17f0 <art_quick_check_cast>,
pInitializeStaticStorage = 0x795f0b1a40 <art_quick_initialize_static_storage>,
pInitializeTypeAndVerifyAccess = 0x795f0b1bc0 <art_quick_initialize_type_and_verify_access>,
pInitializeType = 0x795f0b1b00 <art_quick_initialize_type>,
pResolveString = 0x795f0b2e80 <art_quick_resolve_string>,
pSet8Instance = 0x795f0b2a00 <art_quick_set8_instance>,
pSet8Static = 0x795f0b2700 <art_quick_set8_static>,
pSet16Instance = 0x795f0b2ac0 <art_quick_set16_instance>,
pSet16Static = 0x795f0b27c0 <art_quick_set16_static>,
pSet32Instance = 0x795f0b2b80 <art_quick_set32_instance>,
pSet32Static = 0x795f0b2880 <art_quick_set32_static>,
pSet64Instance = 0x795f0b2c40 <art_quick_set64_instance>,
pSet64Static = 0x795f0b2dc0 <art_quick_set64_static>,
pSetObjInstance = 0x795f0b2d00 <art_quick_set_obj_instance>,
pSetObjStatic = 0x795f0b2940 <art_quick_set_obj_static>,
pGetByteInstance = 0x795f0b2280 <art_quick_get_byte_instance>,
pGetBooleanInstance = 0x795f0b21c0 <art_quick_get_boolean_instance>,
pGetByteStatic = 0x795f0b1d40 <art_quick_get_byte_static>,
pGetBooleanStatic = 0x795f0b1c80 <art_quick_get_boolean_static>,
pGetShortInstance = 0x795f0b2400 <art_quick_get_short_instance>,
pGetCharInstance = 0x795f0b2340 <art_quick_get_char_instance>,
pGetShortStatic = 0x795f0b1ec0 <art_quick_get_short_static>,
pGetCharStatic = 0x795f0b1e00 <art_quick_get_char_static>,
pGet32Instance = 0x795f0b24c0 <art_quick_get32_instance>,
pGet32Static = 0x795f0b1f80 <art_quick_get32_static>,
pGet64Instance = 0x795f0b2580 <art_quick_get64_instance>,
pGet64Static = 0x795f0b2040 <art_quick_get64_static>,
pGetObjInstance = 0x795f0b2640 <art_quick_get_obj_instance>,
pGetObjStatic = 0x795f0b2100 <art_quick_get_obj_static>,
pAputObjectWithNullAndBoundCheck = 0x795f0b1870 <art_quick_aput_obj_with_null_and_bound_check>,
pAputObjectWithBoundCheck = 0x795f0b1880 <art_quick_aput_obj_with_bound_check>,
pAputObject = 0x795f0b18a0 <art_quick_aput_obj>,
pHandleFillArrayData = 0x795f0b1980 <art_quick_handle_fill_data>,
pJniMethodStart = 0x795f523c2c <art::JniMethodStart(art::Thread*)>,
pJniMethodStartSynchronized = 0x795f523db0 <art::JniMethodStartSynchronized(_jobject*, art::Thread*)>,
pJniMethodEnd = 0x795f523dec <art::JniMethodEnd(unsigned int, art::Thread*)>,
pJniMethodEndSynchronized = 0x795f5240d4 <art::JniMethodEndSynchronized(unsigned int, _jobject*, art::Thread*)>,
pJniMethodEndWithReference = 0x795f5242f4 <art::JniMethodEndWithReference(_jobject*, unsigned int, art::Thread*)>,
pJniMethodEndWithReferenceSynchronized = 0x795f5243a4 <art::JniMethodEndWithReferenceSynchronized(_jobject*, unsigned int, _jobject*, art::Thread*)>,
pQuickGenericJniTrampoline = 0x795f0ba500 <art_quick_generic_jni_trampoline>,
pLockObject = 0x795f0b1430 <art_quick_lock_object>,
pUnlockObject = 0x795f0b1610 <art_quick_unlock_object>,
pCmpgDouble = 0x0,
pCmpgFloat = 0x0,
pCmplDouble = 0x0,
pCmplFloat = 0x0,
pCos = 0x7961075168 <cos>,
pSin = 0x7961079e78 <sin>,
pAcos = 0x796106b978 <acos>,
pAsin = 0x796106c128 <asin>,
pAtan = 0x7961074400 <atan>,
pAtan2 = 0x796106c55c <atan2>,
pCbrt = 0x7961074844 <cbrt>,
pCosh = 0x796106cbb4 <cosh>,
pExp = 0x796106cd98 <exp>,
pExpm1 = 0x7961077cdc <expm1>,
pHypot = 0x796106d688 <hypot>,
pLog = 0x7961071960 <log>,
pLog10 = 0x79610712c0 <log10>,
pNextAfter = 0x79610792c8 <nextafter>,
pSinh = 0x79610730f0 <sinh>,
pTan = 0x796107a72c <tan>,
pTanh = 0x796107aefc <tanh>,
pFmod = 0x796106d204 <fmod>,
pL2d = 0x0,
pFmodf = 0x796106d4f4 <fmodf>,
pL2f = 0x0,
pD2iz = 0x0,
pF2iz = 0x0,
pIdivmod = 0x0,
pD2l = 0x0,
pF2l = 0x0,
pLdiv = 0x0,
pLmod = 0x0,
pLmul = 0x0,
pShlLong = 0x0,
pShrLong = 0x0,
pUshrLong = 0x0,
pIndexOf = 0x795f0ba930 <art_quick_indexof>,
pStringCompareTo = 0x795f0baa00 <art_quick_string_compareto>,
pMemcpy = 0x79622208c8 <memcpy>,
pQuickImtConflictTrampoline = 0x795f0ba290 <art_quick_imt_conflict_trampoline>,
pQuickResolutionTrampoline = 0x795f0ba3c0 <art_quick_resolution_trampoline>,
pQuickToInterpreterBridge = 0x795f0ba650 <art_quick_to_interpreter_bridge>,
pInvokeDirectTrampolineWithAccessCheck = 0x795f0b0a70 <art_quick_invoke_direct_trampoline_with_access_check>,
pInvokeInterfaceTrampolineWithAccessCheck = 0x795f0b0870 <art_quick_invoke_interface_trampoline_with_access_check>,
pInvokeStaticTrampolineWithAccessCheck = 0x795f0b0970 <art_quick_invoke_static_trampoline_with_access_check>,
pInvokeSuperTrampolineWithAccessCheck = 0x795f0b0b70 <art_quick_invoke_super_trampoline_with_access_check>,
pInvokeVirtualTrampolineWithAccessCheck = 0x795f0b0c70 <art_quick_invoke_virtual_trampoline_with_access_check>,
pTestSuspend = 0x795f0ba090 <art_quick_test_suspend>,
pDeliverException = 0x795f0b0660 <art_quick_deliver_exception>,
pThrowArrayBounds = 0x795f0b0760 <art_quick_throw_array_bounds>,
pThrowDivZero = 0x795f0b0710 <art_quick_throw_div_zero>,
pThrowNoSuchMethod = 0x795f0b0810 <art_quick_throw_no_such_method>,
pThrowNullPointer = 0x795f0b06c0 <art_quick_throw_null_pointer_exception>,
pThrowStackOverflow = 0x795f0b07c0 <art_quick_throw_stack_overflow>,
pDeoptimize = 0x795f0ba8d0 <art_quick_deoptimize_from_compiled_code>,
pA64Load = 0x795f4253b8 <art::UnimplementedEntryPoint()>,
pA64Store = 0x795f4253b8 <art::UnimplementedEntryPoint()>,
pNewEmptyString = 0x70e8c810,
pNewStringFromBytes_B = 0x70e8c848,
pNewStringFromBytes_BI = 0x70e8c880,
pNewStringFromBytes_BII = 0x70e8c8b8,
pNewStringFromBytes_BIII = 0x70e8c8f0,
pNewStringFromBytes_BIIString = 0x70e8c928,
pNewStringFromBytes_BString = 0x70e8c998,
pNewStringFromBytes_BIICharset = 0x70e8c960,
pNewStringFromBytes_BCharset = 0x70e8c9d0,
pNewStringFromChars_C = 0x70e8ca40,
pNewStringFromChars_CII = 0x70e8ca78,
pNewStringFromChars_IIC = 0x70e8ca08,
pNewStringFromCodePoints = 0x70e8cab0,
pNewStringFromString = 0x70e8cae8,
pNewStringFromStringBuffer = 0x70e8cb20,
pNewStringFromStringBuilder = 0x70e8cb58,
pReadBarrierJni = 0x795f523c28 <art::ReadBarrierJni(art::mirror::CompressedReference<art::mirror::Object>*, art::Thread*)>,
pReadBarrierMark = 0x795f5235c4 <artReadBarrierMark(art::mirror::Object*)>,
pReadBarrierSlow = 0x795f5236e8 <artReadBarrierSlow(art::mirror::Object*, art::mirror::Object*, uint32_t)>,
pReadBarrierForRootSlow = 0x795f5236f0 <artReadBarrierForRootSlow(art::GcRoot<art::mirror::Object>*)>
},
thread_local_objects = 0,
thread_local_start = 0x0,
thread_local_pos = 0x0,
thread_local_end = 0x0,
mterp_current_ibase = 0x795f0a0280 <artMterpAsmInstructionStart>,
mterp_default_ibase = 0x795f0a0280 <artMterpAsmInstructionStart>,
mterp_alt_ibase = 0x795f0a8280 <artMterpAsmSisterStart>,
rosalloc_runs = {0x795f5fcf08 <art::gc::allocator::RosAlloc::dedicated_full_run_storage_>, 0x13754000, 0x149d7000, 0x14e1f000, 0x14595000, 0x1457c000, 0x13e65000, 0x14186000, 0x14828000, 0x12f71000, 0x13f18000, 0x795f5fcf08 <art::gc::allocator::RosAlloc::dedicated_full_run_storage_>, 0x795f5fcf08 <art::gc::allocator::RosAlloc::dedicated_full_run_storage_>, 0x13c29000, 0x795f5fcf08 <art::gc::allocator::RosAlloc::dedicated_full_run_storage_>, 0x795f5fcf08 <art::gc::allocator::RosAlloc::dedicated_full_run_storage_>},
thread_local_alloc_stack_top = 0x795a52b8b8,
thread_local_alloc_stack_end = 0x795a52ba00,
held_mutexes = {0x0 <repeats 63 times>},
nested_signal_state = 0x795f67f300,
flip_function = 0x0,
method_verifier = 0x0,
thread_local_mark_stack = 0x0
},
wait_mutex_ = 0x795f719080,
wait_cond_ = 0x795f6fa960,
wait_monitor_ = 0x0,
interrupted_ = false,
debug_disallow_read_barrier_ = 0 '\000'
}
...... //此處省略N個Thread對象的打印
Cannot access memory at address 0xa1
(gdb)
從上面打印出來的N個Thread對象的內(nèi)容來看健无,我們很容易找到處于kRunnable狀態(tài)的線程,它的pid為3093液斜,因為它的state = 67累贤,也就是kRunnable.
enum ThreadState {
// Thread.State JDWP state
kTerminated = 66, // TERMINATED TS_ZOMBIE Thread.run has returned, but Thread* still around
kRunnable, // RUNNABLE TS_RUNNING runnable
kTimedWaiting, // TIMED_WAITING TS_WAIT in Object.wait() with a timeout
kSleeping, // TIMED_WAITING TS_SLEEPING in Thread.sleep()
kBlocked, // BLOCKED TS_MONITOR blocked on a monitor
kWaiting, // WAITING TS_WAIT in Object.wait()
kWaitingForGcToComplete, // WAITING TS_WAIT blocked waiting for GC
kWaitingForCheckPointsToRun, // WAITING TS_WAIT GC waiting for checkpoints to run
kWaitingPerformingGc, // WAITING TS_WAIT performing GC
kWaitingForDebuggerSend, // WAITING TS_WAIT blocked waiting for events to be sent
kWaitingForDebuggerToAttach, // WAITING TS_WAIT blocked waiting for debugger to attach
kWaitingInMainDebuggerLoop, // WAITING TS_WAIT blocking/reading/processing debugger events
kWaitingForDebuggerSuspension, // WAITING TS_WAIT waiting for debugger suspend all
kWaitingForJniOnLoad, // WAITING TS_WAIT waiting for execution of dlopen and JNI on load code
kWaitingForSignalCatcherOutput, // WAITING TS_WAIT waiting for signal catcher IO to complete
kWaitingInMainSignalCatcherLoop, // WAITING TS_WAIT blocking/reading/processing signals
kWaitingForDeoptimization, // WAITING TS_WAIT waiting for deoptimization suspend all
kWaitingForMethodTracingStart, // WAITING TS_WAIT waiting for method tracing to start
kWaitingForVisitObjects, // WAITING TS_WAIT waiting for visiting objects
kWaitingForGetObjectsAllocated, // WAITING TS_WAIT waiting for getting the number of allocated objects
kWaitingWeakGcRootRead, // WAITING TS_WAIT waiting on the GC to read a weak root
kWaitingForGcThreadFlip, // WAITING TS_WAIT waiting on the GC thread flip (CC collector) to finish
kStarting, // NEW TS_WAIT native thread started, not yet ready to run managed code
kNative, // RUNNABLE TS_RUNNING running in a JNI native method
kSuspended, // RUNNABLE TS_RUNNING suspended by GC or debugger
};
tls32_ = {
state_and_flags = {
as_struct = {
flags = 5,
state = 67
},
as_atomic_int = {
<std::__1::atomic<int>> = {
<std::__1::__atomic_base<int, true>> = {
<std::__1::__atomic_base<int, false>> = {
__a_ = 4390917
}, <No data fields>}, <No data fields>}, <No data fields>},
as_int = 4390917
},
suspend_count = 1,
debug_suspend_count = 0,
thin_lock_thread_id = 20,
tid = 3093,
daemon = 0,
throwing_OutOfMemoryError = 0,
no_thread_suspension = 0,
thread_exit_check_count = 0,
handling_signal_ = 0,
suspended_at_suspend_check = 0,
ready_for_debug_invoke = 0,
debug_method_entry_ = 0,
is_gc_marking = 0,
weak_ref_access_enabled = 1,
disable_thread_flip_count = 0
}
(gdb) t 176
[Switching to thread 176 (LWP 3093)]
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
41 bionic/libc/arch-arm64/bionic/syscall.S: 沒有那個文件或目錄.
(gdb) bt
#0 syscall () at bionic/libc/arch-arm64/bionic/syscall.S:41
#1 0x000000796226eb84 in __futex (op=<optimized out>, value=<optimized out>, timeout=0x0, bitset=-1, ftx=<optimized out>) at bionic/libc/private/bionic_futex.h:48
#2 __futex_wait_ex (value=<optimized out>, ftx=<optimized out>, shared=<optimized out>, use_realtime_clock=<optimized out>, abs_timeout=<optimized out>) at bionic/libc/private/bionic_futex.h:70
#3 __pthread_normal_mutex_lock (abs_timeout_or_null=<optimized out>, mutex=<optimized out>, shared=<optimized out>, use_realtime_clock=<optimized out>) at bionic/libc/bionic/pthread_mutex.cpp:327
#4 __pthread_mutex_lock_with_timeout (mutex=<optimized out>, use_realtime_clock=<optimized out>, abs_timeout_or_null=<optimized out>) at bionic/libc/bionic/pthread_mutex.cpp:430
#5 0x0000007961ad0354 in android::android_content_AssetManager_applyStyle (env=0x795127c740, themeToken=520810520368, defStyleAttr=<optimized out>, defStyleRes=16974731, xmlParserToken=1982366608, attrs=0x795f0bdcb4 <art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+256>, outValues=0x795f197c50 <art::gc::allocator::RosAlloc::AllocFromRun(art::Thread*, unsigned long, unsigned long*, unsigned long*, unsigned long*)+348>, outIndices=0x7942b9dee0, clazz=<optimized out>) at frameworks/base/core/jni/android_util_AssetManager.cpp:1430
#6 0x00000000748f3ecc in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
從這個堆棧來看它已經(jīng)進(jìn)入了JNI函數(shù),按理來說它應(yīng)該是kNative狀態(tài)才對少漆,但是這里卻為kRunnable狀態(tài)臼膏,有點奇怪,查看進(jìn)入Jni函數(shù)的代碼:
extern uint32_t JniMethodStart(Thread* self) {
JNIEnvExt* env = self->GetJniEnv();
DCHECK(env != nullptr);
uint32_t saved_local_ref_cookie = env->local_ref_cookie;
env->local_ref_cookie = env->locals.GetSegmentState();
ArtMethod* native_method = *self->GetManagedStack()->GetTopQuickFrame();
if (!native_method->IsFastNative()) { //如果這個Jni方法不是fast native方法示损,就改為suspend狀態(tài)
// When not fast JNI we transition out of runnable.
self->TransitionFromRunnableToSuspended(kNative);
}
return saved_local_ref_cookie;
}
所以如果這個Native方法是fast native方法的話渗磅,那么它的狀態(tài)就還是kRunnable,我們看android_content_AssetManager_applyStyle這個Jni函數(shù)注冊的地方:
{ "applyStyle","!(JIIJ[I[I[I)Z",(void*) android_content_AssetManager_applyStyle }
注冊的時候有加检访!號始鱼,所以這個函數(shù)的確是一個fast native方法,所以它的狀態(tài)就是kRunnable脆贵,fast native方法應(yīng)該是指能夠很快返回的jni方法医清,所以可以不用轉(zhuǎn)換狀態(tài),本來是一種優(yōu)化措施卖氨,但是從上面的堆棧來看会烙,這個fast native方法卻在等鎖,一旦等鎖的話筒捺,就可能不是那么快執(zhí)行完了柏腻,所以覺得這里把它置為fast native不是那么合適,而應(yīng)該去掉前面的 系吭!號五嫂,這樣就可以在進(jìn)入JNI之后變?yōu)閗Native狀態(tài),ART也不會卡死.