ส่วนนี้สรุปเครื่องมือที่มีประโยชน์และคำสั่งที่เกี่ยวข้องสำหรับการดีบัก การติดตาม และการทำโปรไฟล์โค้ดแพลตฟอร์ม 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