การดีบักรหัสแพลตฟอร์ม Android ดั้งเดิม

ส่วนนี้สรุปเครื่องมือที่มีประโยชน์และคำสั่งที่เกี่ยวข้องสำหรับการดีบัก การติดตาม และการทำโปรไฟล์โค้ดแพลตฟอร์ม Android ดั้งเดิมเมื่อพัฒนาคุณลักษณะระดับแพลตฟอร์ม

หมายเหตุ: หน้าในส่วนนี้และที่อื่นๆ ภายในไซต์นี้แนะนำให้ใช้ adb ร่วมกับอาร์กิวเมนต์ setprop เพื่อดีบักบางแง่มุมของ Android ใน Android 7.x และต่ำกว่า ชื่อพร็อพเพอร์ตี้ต้องมีความยาวไม่เกิน 32 อักขระ ซึ่งหมายความว่าหากต้องการสร้างคุณสมบัติการห่อด้วยชื่อของแอป จำเป็นต้องตัดชื่อให้พอดี ใน Android 8.0 ขึ้นไป ขีดจำกัดนี้มากกว่ามากและไม่ควรต้องมีการตัดทอน

หน้านี้ครอบคลุมข้อมูลพื้นฐานเกี่ยวกับดัมพ์ข้อขัดข้องที่พบในเอาต์พุต logcat หน้าอื่นๆ มีรายละเอียดเพิ่มเติมเกี่ยวกับ การวินิจฉัยการ แครชแบบเนทีฟ การสำรวจบริการของระบบด้วย dumpsys การดู หน่วยความจำ เนทีฟ เครือข่าย และการใช้ RAM โดยใช้ AddressSanitizer เพื่อตรวจหาข้อบกพร่องของหน่วยความจำในโค้ดเนทีฟ การประเมิน ปัญหาด้านประสิทธิภาพ (รวมถึง systrace ) และการใช้ ดีบักเกอร์

กองขยะและหลุมฝังศพ

เมื่อไฟล์เรียกทำงานที่เชื่อมโยงแบบไดนามิกเริ่มทำงาน ตัวจัดการสัญญาณหลายตัวจะถูกลงทะเบียนซึ่งในกรณีที่เกิดการขัดข้อง จะทำให้ดัมพ์การแครชพื้นฐานถูกเขียนไปยัง logcat และไฟล์ tombstone ที่มีรายละเอียดมากขึ้นจะถูกเขียนไปยัง /data/tombstones/ หลุมฝังศพเป็นไฟล์ที่มีข้อมูลเพิ่มเติมเกี่ยวกับกระบวนการที่ขัดข้อง โดยเฉพาะอย่างยิ่ง มันมีสแต็กเทรซสำหรับเธรดทั้งหมดในกระบวนการหยุดทำงาน (ไม่ใช่แค่เธรดที่จับสัญญาณ) แมปหน่วยความจำแบบเต็ม และรายการของตัวอธิบายไฟล์ที่เปิดอยู่ทั้งหมด

ก่อน Android 8.0 การขัดข้องได้รับการจัดการโดย debuggerd และ debuggerd64 daemons ใน Android 8.0 ขึ้นไป crash_dump32 และ crash_dump64 จะเกิดขึ้นตามต้องการ

เป็นไปได้ที่แดมเปอร์การแครชจะแนบก็ต่อเมื่อไม่ได้แนบอย่างอื่นอยู่แล้ว ซึ่งหมายความว่าการใช้เครื่องมือ เช่น strace หรือ lldb ป้องกันไม่ให้เกิดดัมพ์การแครช

ตัวอย่างผลลัพธ์ (โดยลบการประทับเวลาและข้อมูลที่ไม่เกี่ยวข้องออก):

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'Android/aosp_angler/angler:7.1.1/NYC/enh12211018:eng/test-keys'
Revision: '0'
ABI: 'arm'
pid: 17946, tid: 17949, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
    r0 0000000c  r1 00000000  r2 00000000  r3 00000000
    r4 00000000  r5 0000000c  r6 eccdd920  r7 00000078
    r8 0000461a  r9 ffc78c19  sl ab209441  fp fffff924
    ip ed01b834  sp eccdd800  lr ecfa9a1f  pc ecfd693e  cpsr 600e0030

backtrace:
    #00 pc 0004793e  /system/lib/libc.so (pthread_mutex_lock+1)
    #01 pc 0001aa1b  /system/lib/libc.so (readdir+10)
    #02 pc 00001b91  /system/xbin/crasher (readdir_null+20)
    #03 pc 0000184b  /system/xbin/crasher (do_action+978)
    #04 pc 00001459  /system/xbin/crasher (thread_callback+24)
    #05 pc 00047317  /system/lib/libc.so (_ZL15__pthread_startPv+22)
    #06 pc 0001a7e5  /system/lib/libc.so (__start_thread+34)
Tombstone written to: /data/tombstones/tombstone_06

บรรทัดสุดท้ายของเอาต์พุตให้ตำแหน่งของหลุมฝังศพเต็มบนดิสก์

หากคุณมีไบนารีที่ไม่มีแถบข้อมูล คุณสามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับข้อมูลหมายเลขบรรทัดโดยวางสแต็กลงใน development/scripts/stack :

development/scripts/stack

เคล็ดลับ: เพื่อความสะดวก หากคุณเรียกใช้ lunch แล้ว stack จะอยู่บน $PATH ของคุณอยู่แล้ว ดังนั้นคุณไม่จำเป็นต้องระบุพาธแบบเต็ม

ตัวอย่างเอาต์พุต (ตามเอาต์พุต logcat ด้านบน):

Reading native crash info from stdin
03-02 23:53:49.477 17951 17951 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
03-02 23:53:49.477 17951 17951 F DEBUG   : Build fingerprint: 'Android/aosp_angler/angler:7.1.1/NYC/enh12211018:eng/test-keys'
03-02 23:53:49.477 17951 17951 F DEBUG   : Revision: '0'
03-02 23:53:49.477 17951 17951 F DEBUG   : ABI: 'arm'
03-02 23:53:49.478 17951 17951 F DEBUG   : pid: 17946, tid: 17949, name: crasher  >>> crasher <<<
03-02 23:53:49.478 17951 17951 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
03-02 23:53:49.478 17951 17951 F DEBUG   :     r0 0000000c  r1 00000000  r2 00000000  r3 00000000
03-02 23:53:49.478 17951 17951 F DEBUG   :     r4 00000000  r5 0000000c  r6 eccdd920  r7 00000078
03-02 23:53:49.478 17951 17951 F DEBUG   :     r8 0000461a  r9 ffc78c19  sl ab209441  fp fffff924
03-02 23:53:49.478 17951 17951 F DEBUG   :     ip ed01b834  sp eccdd800  lr ecfa9a1f  pc ecfd693e  cpsr 600e0030
03-02 23:53:49.491 17951 17951 F DEBUG   :
03-02 23:53:49.491 17951 17951 F DEBUG   : backtrace:
03-02 23:53:49.492 17951 17951 F DEBUG   :     #00 pc 0004793e  /system/lib/libc.so (pthread_mutex_lock+1)
03-02 23:53:49.492 17951 17951 F DEBUG   :     #01 pc 0001aa1b  /system/lib/libc.so (readdir+10)
03-02 23:53:49.492 17951 17951 F DEBUG   :     #02 pc 00001b91  /system/xbin/crasher (readdir_null+20)
03-02 23:53:49.492 17951 17951 F DEBUG   :     #03 pc 0000184b  /system/xbin/crasher (do_action+978)
03-02 23:53:49.492 17951 17951 F DEBUG   :     #04 pc 00001459  /system/xbin/crasher (thread_callback+24)
03-02 23:53:49.492 17951 17951 F DEBUG   :     #05 pc 00047317  /system/lib/libc.so (_ZL15__pthread_startPv+22)
03-02 23:53:49.492 17951 17951 F DEBUG   :     #06 pc 0001a7e5  /system/lib/libc.so (__start_thread+34)
03-02 23:53:49.492 17951 17951 F DEBUG   :     Tombstone written to: /data/tombstones/tombstone_06
Reading symbols from /huge-ssd/aosp-arm64/out/target/product/angler/symbols
Revision: '0'
pid: 17946, tid: 17949, name: crasher  >>> crasher <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xc
     r0 0000000c  r1 00000000  r2 00000000  r3 00000000
     r4 00000000  r5 0000000c  r6 eccdd920  r7 00000078
     r8 0000461a  r9 ffc78c19  sl ab209441  fp fffff924
     ip ed01b834  sp eccdd800  lr ecfa9a1f  pc ecfd693e  cpsr 600e0030
Using arm toolchain from: /huge-ssd/aosp-arm64/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/

Stack Trace:
  RELADDR   FUNCTION                   FILE:LINE
  0004793e  pthread_mutex_lock+2       bionic/libc/bionic/pthread_mutex.cpp:515
  v------>  ScopedPthreadMutexLocker   bionic/libc/private/ScopedPthreadMutexLocker.h:27
  0001aa1b  readdir+10                 bionic/libc/bionic/dirent.cpp:120
  00001b91  readdir_null+20            system/core/debuggerd/crasher.cpp:131
  0000184b  do_action+978              system/core/debuggerd/crasher.cpp:228
  00001459  thread_callback+24         system/core/debuggerd/crasher.cpp:90
  00047317  __pthread_start(void*)+22  bionic/libc/bionic/pthread_create.cpp:202 (discriminator 1)
  0001a7e5  __start_thread+34          bionic/libc/bionic/clone.cpp:46 (discriminator 1)

คุณสามารถใช้ stack บนหลุมฝังศพทั้งหมดได้ ตัวอย่าง:

stack < FS/data/tombstones/tombstone_05

สิ่งนี้มีประโยชน์หากคุณเพิ่งคลายซิปรายงานข้อบกพร่องในไดเรกทอรีปัจจุบัน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการวินิจฉัยข้อขัดข้องดั้งเดิมและหลุมฝังศพ โปรดดูที่ การวินิจฉัยการขัดข้อง ของระบบ

รับ stack trace/tombstone จากกระบวนการที่ทำงานอยู่

คุณสามารถใช้เครื่องมือ debuggerd เพื่อรับสแต็กดัมพ์จากกระบวนการที่ทำงานอยู่ จากบรรทัดรับคำสั่ง เรียกใช้ debuggerd โดยใช้ process ID (PID) เพื่อดัมพ์ tombstone แบบเต็มไปยัง stdout หากต้องการรับเฉพาะสแต็กสำหรับทุกเธรดในกระบวนการ ให้รวมแฟล็ก -b หรือ --backtrace

ทำความเข้าใจกับความผ่อนคลายที่ซับซ้อน

เมื่อแอปขัดข้อง สแต็กมักจะค่อนข้างซับซ้อน ตัวอย่างโดยละเอียดต่อไปนี้เน้นถึงความซับซ้อนมากมาย:

    #00 pc 00000000007e6918  /system/priv-app/Velvet/Velvet.apk (offset 0x346b000)
    #01 pc 00000000001845cc  /system/priv-app/Velvet/Velvet.apk (offset 0x346b000)
    #02 pc 00000000001847e4  /system/priv-app/Velvet/Velvet.apk (offset 0x346b000)
    #03 pc 00000000001805c0  /system/priv-app/Velvet/Velvet.apk (offset 0x346b000) (Java_com_google_speech_recognizer_AbstractRecognizer_nativeRun+176)

เฟรม #00–#03 มาจากโค้ด JNI ดั้งเดิมที่จัดเก็บแบบไม่บีบอัดใน APK เพื่อประหยัดพื้นที่ดิสก์แทนที่จะแยกเป็นไฟล์ . .so แยกต่างหาก ตัวคลายสแต็กใน Android 9 และสูงกว่าไม่ต้องการไฟล์ . .so ที่แยกออกมาเพื่อรับมือกับกรณีทั่วไปเฉพาะของ Android นี้

เฟรม #00–#02 ไม่มีชื่อสัญลักษณ์เพราะถูกถอดโดยผู้พัฒนา

กรอบ #03 แสดงว่าเมื่อมีสัญลักษณ์อยู่ ตัวคลี่คลายจะใช้สัญลักษณ์เหล่านั้น

    #04 pc 0000000000117550  /data/dalvik-cache/arm64/system@priv-app@Velvet@Velvet.apk@classes.dex (offset 0x108000) (com.google.speech.recognizer.AbstractRecognizer.nativeRun+160)

Frame #04 เป็นโค้ด Java ที่คอมไพล์ล่วงหน้า ตัวคลี่คลายแบบเก่าจะหยุดที่นี่ ไม่สามารถคลี่คลายผ่าน Java ได้

    #05 pc 0000000000559f88  /system/lib64/libart.so (art_quick_invoke_stub+584)
    #06 pc 00000000000ced40  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
    #07 pc 0000000000280cf0  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
    #08 pc 000000000027acac  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+948)
    #09 pc 000000000052abc0  /system/lib64/libart.so (MterpInvokeDirect+296)
    #10 pc 000000000054c614  /system/lib64/libart.so (ExecuteMterpImpl+14484)

เฟรม #05–#10 มาจากการใช้งานล่าม ART ตัวคลายสแต็กในรีลีสที่ต่ำกว่า Android 9 จะแสดงเฟรมเหล่านี้โดยไม่มีบริบทของเฟรม #11 อธิบายว่าโค้ดใดที่ล่ามกำลังตีความ เฟรมเหล่านี้มีประโยชน์หากคุณกำลังดีบั๊ก ART เอง หากคุณกำลังแก้ไขจุดบกพร่องของแอป คุณสามารถเพิกเฉยได้ เครื่องมือบางอย่าง เช่น simpleperf ละเว้นเฟรมเหล่านี้โดยอัตโนมัติ

    #11 pc 00000000001992d6  /system/priv-app/Velvet/Velvet.apk (offset 0x26cf000) (com.google.speech.recognizer.AbstractRecognizer.run+18)

เฟรม #11 คือโค้ด Java ที่กำลังตีความ

    #12 pc 00000000002547a8  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.780698333+496)
    #13 pc 000000000025a328  /system/lib64/libart.so (art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+216)
    #14 pc 000000000027ac90  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+920)
    #15 pc 0000000000529880  /system/lib64/libart.so (MterpInvokeVirtual+584)
    #16 pc 000000000054c514  /system/lib64/libart.so (ExecuteMterpImpl+14228)

เฟรม #12–#16 คือการใช้งานล่ามเอง

    #17 pc 00000000002454a0  /system/priv-app/Velvet/Velvet.apk (offset 0x1322000) (com.google.android.apps.gsa.speech.e.c.c.call+28)

เฟรม #17 คือโค้ด Java ที่กำลังตีความ เมธอด Java นี้สอดคล้องกับเฟรมล่าม #12–#16

    #18 pc 00000000002547a8  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.780698333+496)
    #19 pc 0000000000519fd8  /system/lib64/libart.so (artQuickToInterpreterBridge+1032)
    #20 pc 00000000005630fc  /system/lib64/libart.so (art_quick_to_interpreter_bridge+92)

เฟรม #18–#20 คือ VM เอง โค้ดจะเปลี่ยนจากโค้ด Java ที่คอมไพล์เป็นโค้ด Java ที่ตีความ

    #21 pc 00000000002ce44c  /system/framework/arm64/boot.oat (offset 0xdc000) (java.util.concurrent.FutureTask.run+204)

Frame #21 เป็นวิธีการ Java ที่คอมไพล์แล้วซึ่งเรียกวิธีการ Java ใน #17

    #22 pc 0000000000559f88  /system/lib64/libart.so (art_quick_invoke_stub+584)
    #23 pc 00000000000ced40  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
    #24 pc 0000000000280cf0  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
    #25 pc 000000000027acac  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+948)
    #26 pc 0000000000529880  /system/lib64/libart.so (MterpInvokeVirtual+584)
    #27 pc 000000000054c514  /system/lib64/libart.so (ExecuteMterpImpl+14228)

เฟรม #22–#27 เป็นการนำล่ามไปใช้ ทำให้การเรียกใช้เมธอดจากโค้ดที่แปลเป็นวิธีการที่คอมไพล์

    #28 pc 00000000003ed69e  /system/priv-app/Velvet/Velvet.apk (com.google.android.apps.gsa.shared.util.concurrent.b.e.run+22)

เฟรม #28 คือโค้ด Java ที่กำลังตีความ

    #29 pc 00000000002547a8  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.780698333+496)
    #30 pc 0000000000519fd8  /system/lib64/libart.so (artQuickToInterpreterBridge+1032)
    #31 pc 00000000005630fc  /system/lib64/libart.so (art_quick_to_interpreter_bridge+92)

เฟรม #29–#31 เป็นการเปลี่ยนผ่านระหว่างโค้ดที่คอมไพล์แล้วและโค้ดที่แปลแล้ว

    #32 pc 0000000000329284  /system/framework/arm64/boot.oat (offset 0xdc000) (java.util.concurrent.ThreadPoolExecutor.runWorker+996)
    #33 pc 00000000003262a0  /system/framework/arm64/boot.oat (offset 0xdc000) (java.util.concurrent.ThreadPoolExecutor$Worker.run+64)
    #34 pc 00000000002037e8  /system/framework/arm64/boot.oat (offset 0xdc000) (java.lang.Thread.run+72)

เฟรม #32–#34 เป็นเฟรม Java ที่คอมไพล์แล้วเรียกกันและกันโดยตรง ในกรณีนี้ Native call stack จะเหมือนกับ Java call stack

    #35 pc 0000000000559f88  /system/lib64/libart.so (art_quick_invoke_stub+584)
    #36 pc 00000000000ced40  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
    #37 pc 0000000000280cf0  /system/lib64/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+344)
    #38 pc 000000000027acac  /system/lib64/libart.so (bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+948)
    #39 pc 0000000000529f10  /system/lib64/libart.so (MterpInvokeSuper+1408)
    #40 pc 000000000054c594  /system/lib64/libart.so (ExecuteMterpImpl+14356)

เฟรม #35–#40 คือตัวแปลเอง

    #41 pc 00000000003ed8e0  /system/priv-app/Velvet/Velvet.apk (com.google.android.apps.gsa.shared.util.concurrent.b.i.run+20)

เฟรม #41 คือโค้ด Java ที่กำลังตีความ

    #42 pc 00000000002547a8  /system/lib64/libart.so (_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_6JValueEb.llvm.780698333+496)
    #43 pc 0000000000519fd8  /system/lib64/libart.so (artQuickToInterpreterBridge+1032)
    #44 pc 00000000005630fc  /system/lib64/libart.so (art_quick_to_interpreter_bridge+92)
    #45 pc 0000000000559f88  /system/lib64/libart.so (art_quick_invoke_stub+584)
    #46 pc 00000000000ced40  /system/lib64/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200)
    #47 pc 0000000000460d18  /system/lib64/libart.so (art::(anonymous namespace)::InvokeWithArgArray(art::ScopedObjectAccessAlreadyRunnable const&, art::ArtMethod*, art::(anonymous namespace)::ArgArray*, art::JValue*, char const*)+104)
    #48 pc 0000000000461de0  /system/lib64/libart.so (art::InvokeVirtualOrInterfaceWithJValues(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, jvalue*)+424)
    #49 pc 000000000048ccb0  /system/lib64/libart.so (art::Thread::CreateCallback(void*)+1120)

เฟรม #42–#49 คือ VM เอง คราวนี้เป็นรหัสที่เริ่มรัน Java บนเธรดใหม่

    #50 pc 0000000000082e24  /system/lib64/libc.so (__pthread_start(void*)+36)
    #51 pc 00000000000233bc  /system/lib64/libc.so (__start_thread+68)

เฟรม #50–#51 คือวิธีที่เธรดทั้งหมดควรเริ่มต้น นี่คือรหัสเริ่มต้นเธรดใหม่ของ libc