Android Stability - gdb和coredump

在分析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也不會卡死.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肯尺,一起剝皮案震驚了整個濱河市沃缘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蟆盹,老刑警劉巖孩灯,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闺金,死亡現(xiàn)場離奇詭異逾滥,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門寨昙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來讥巡,“玉大人,你說我怎么就攤上這事舔哪』肚辏” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵捉蚤,是天一觀的道長抬驴。 經(jīng)常有香客問我,道長缆巧,這世上最難降的妖魔是什么布持? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮陕悬,結(jié)果婚禮上题暖,老公的妹妹穿的比我還像新娘。我一直安慰自己捉超,他們只是感情好胧卤,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拼岳,像睡著了一般枝誊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惜纸,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天侧啼,我揣著相機與錄音,去河邊找鬼堪簿。 笑死痊乾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的椭更。 我是一名探鬼主播哪审,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼虑瀑!你這毒婦竟也來了湿滓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤舌狗,失蹤者是張志新(化名)和其女友劉穎叽奥,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痛侍,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡朝氓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赵哲。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡待德,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出枫夺,到底是詐尸還是另有隱情将宪,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布橡庞,位于F島的核電站较坛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扒最。R本人自食惡果不足惜燎潮,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扼倘。 院中可真熱鬧确封,春花似錦、人聲如沸再菊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纠拔。三九已至秉剑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稠诲,已是汗流浹背侦鹏。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留臀叙,地道東北人略水。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像劝萤,于是被迫代替她去往敵國和親渊涝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • layout: wikititle: Android逆向分析筆記categories: Reverse_Engin...
    超哥__閱讀 10,704評論 1 17
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,285評論 25 707
  • 作為曾經(jīng)的學(xué)生黨床嫌,看到這樣接地氣的問題坐不住了跨释,必須要來答一答。 對于題主的疑問厌处,首先要說明的是鳖谈,排座位是個必須的...
    嵐風(fēng)的葉子閱讀 1,470評論 0 1
  • 親愛的自己: 今天是否舍不得睡? 生命翻開了新的篇章阔涉,生活總是給予太多的驚喜缆娃。 一直覺得自己很幸運捷绒,得到了生命中很...
    周洋_圖樂園閱讀 216評論 4 7
  • 1、這個時代充斥著太多的假象龄恋,媒體建造了一個個舞臺疙驾,聚光燈凶伙,音響郭毕,音樂.....營造著浮夸的氛圍,臺下的觀眾沸騰著...
    憂小刺閱讀 123評論 0 0