איתור באגים בקוד פלטפורמת אנדרואיד מקורי

סעיף זה מסכם כלים שימושיים ופקודות קשורות לאיתור באגים, מעקב ויצירת פרופילים של קוד פלטפורמת אנדרואיד מקורי בעת פיתוח תכונות ברמת הפלטפורמה.

הערה: הדפים בסעיף זה ובמקומות אחרים באתר זה ממליצים על השימוש ב- adb בשילוב עם הארגומנט setprop כדי לנפות באגים בהיבטים מסוימים של אנדרואיד. ב-Android 7.x ומטה, לשמות נכסים הייתה הגבלת אורך של 32 תווים. המשמעות היא שכדי ליצור מאפיין גלישה עם שם האפליקציה, היה צורך לקטוע את השם כך שיתאים. באנדרואיד 8.0 ומעלה, מגבלה זו היא הרבה יותר גדולה ולא אמורה לדרוש קיצוץ.

דף זה מכסה את העקרונות הבסיסיים סביב dump קריסה שנמצאו בפלט logcat. בדפים אחרים יש הרבה יותר פרטים על אבחון קריסות מקוריות , חקר שירותי מערכת עם dumpsys , צפייה בשימוש בזיכרון מקורי , רשת ו- RAM , שימוש ב- AddressSanitizer לאיתור באגי זיכרון בקוד מקורי, הערכת בעיות ביצועים (כולל systrace ) ושימוש באגים .

מזבלות התרסקות ומצבות

כאשר מופעל קובץ הפעלה מקושר דינמית, נרשמים מספר מטפלי אותות שבמקרה של קריסה, גורמים לכתיבה של dump קריסה בסיסי ל-logcat ולכתיבה של קובץ מצבות מפורט יותר אל /data/tombstones/ . המצבה היא קובץ עם נתונים נוספים על התהליך התרסק. בפרט, הוא מכיל עקבות מחסנית עבור כל השרשורים בתהליך הקריסה (לא רק השרשור שתפס את האות), מפת זיכרון מלאה ורשימה של כל מתארי הקבצים הפתוחים.

לפני אנדרואיד 8.0, קריסות טופלו על ידי הדמונים debuggerd ו- debuggerd64 . ב-Android 8.0 ומעלה, crash_dump32 ו- crash_dump64 נוצרים לפי הצורך.

יתכן שה-crash dumper יתחבר רק אם שום דבר אחר כבר לא מחובר, מה שאומר ששימוש בכלים כגון strace או lldb מונעים מ-crash dumps להתרחש.

פלט לדוגמה (עם חותמות זמן ומידע זר הוסרו):

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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 כדי לקבל מחסנית dump מתהליך פועל. משורת הפקודה, הפעל debuggerd באמצעות מזהה תהליך (PID) כדי לזרוק מצבה מלאה אל 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 נפרד. ה-Stack Unwinder באנדרואיד 9 ומעלה אינו זקוק לקובץ .so שחולץ כדי להתמודד עם מקרה נפוץ זה ספציפי לאנדרואיד.

למסגרות מס' 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)

מסגרת מס' 04 היא קוד 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. ה-Stack Unwinder במהדורות נמוכות מאנדרואיד 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)

מסגרת מס' 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.

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