การแก้จุดบกพร่องรหัสแพลตฟอร์ม 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

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

รับสแต็กแทร็ก / ทูมสโตนจากกระบวนการที่กำลังรัน

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

Frame # 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)

Frame # 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)

Frame # 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 ที่เรียกหากันโดยตรง ในกรณีนี้สแต็กการโทรแบบเนทีฟจะเหมือนกับสแต็กการเรียก Java

    #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)

Frame # 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