סעיף זה מסכם כלים שימושיים ופקודות קשורות לאיתור באגים, מעקב ויצירת פרופילים של קוד פלטפורמת אנדרואיד מקורי בעת פיתוח תכונות ברמת הפלטפורמה.
הערה: הדפים בסעיף זה ובמקומות אחרים באתר זה ממליצים על השימוש ב- 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
.