اشکال زدایی کد پلتفرم اندروید بومی

این بخش ابزارهای مفید و دستورات مرتبط را برای اشکال زدایی، ردیابی، و پروفایل کدهای پلتفرم اندروید بومی در هنگام توسعه ویژگی های سطح پلت فرم خلاصه می کند.

توجه: صفحات این بخش و جاهای دیگر این سایت استفاده از adb را همراه با آرگومان setprop برای اشکال زدایی برخی از جنبه های اندروید توصیه می کنند. در اندروید 7.x و پایین‌تر، نام دارایی‌ها دارای محدودیت طول 32 نویسه بودند. این بدان معنی است که برای ایجاد یک ویژگی wrap با نام برنامه، لازم است نام را کوتاه کنید تا مناسب باشد. در اندروید 8.0 و بالاتر، این محدودیت بسیار بیشتر است و نباید به کوتاهی نیاز داشته باشد.

این صفحه اصول اولیه مربوط به خرابی های تصادفی که در خروجی logcat یافت می شود را پوشش می دهد. سایر صفحات جزئیات بسیار بیشتری در مورد تشخیص خرابی های بومی ، کاوش در سرویس های سیستم با dumpsys ، مشاهده حافظه بومی ، شبکه و استفاده از RAM ، استفاده از AddressSanitizer برای شناسایی اشکالات حافظه در کد بومی، ارزیابی مشکلات عملکرد (شامل systrace ) و استفاده از اشکال زدا دارند.

زباله ها و سنگ قبرها

هنگامی که یک فایل اجرایی پیوند شده به صورت پویا شروع می شود، چندین کنترل کننده سیگنال ثبت می شوند که در صورت خرابی، باعث می شود که یک خرابی اولیه در logcat و یک فایل سنگ قبر با جزئیات بیشتر در /data/tombstones/ نوشته شود. سنگ قبر یک فایل با داده های اضافی در مورد فرآیند خراب است. به طور خاص، شامل ردیابی پشته‌ای برای همه رشته‌ها در فرآیند خرابی (نه فقط رشته‌ای که سیگنال را گرفت)، یک نقشه حافظه کامل، و فهرستی از تمام توصیف‌گرهای فایل باز است.

قبل از اندروید 8.0، خرابی ها توسط دیمون های debuggerd و debuggerd64 مدیریت می شد. در اندروید 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 برای دریافت stack 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 کد جاوا زود کامپایل شده است. بازگشایی قدیمی در اینجا متوقف می شد و نمی توانست از طریق جاوا باز شود.

    #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 هستند. باز کردن پشته در نسخه‌های پایین‌تر از اندروید 9، این فریم‌ها را بدون توضیح قاب شماره 11 نشان می‌داد که چه کدی را مفسر تفسیر می‌کند. اگر خود ART را اشکال زدایی می کنید، این فریم ها مفید هستند. اگر برنامه ای را اشکال زدایی می کنید، می توانید آنها را نادیده بگیرید. برخی از ابزارها، مانند simpleperf ، به طور خودکار این فریم ها را حذف می کنند.

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

فریم شماره 11 کد جاوا در حال تفسیر است.

    #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 کد جاوا در حال تفسیر است. این روش جاوا با فریم های مفسر #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 خود ماشین مجازی هستند، کدی برای انتقال از کد جاوا کامپایل شده به کد جاوا تفسیر شده.

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

فریم شماره 21 متد جاوا کامپایل شده است که متد جاوا را در شماره 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 کد جاوا در حال تفسیر است.

    #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 فریم های جاوا کامپایل شده ای هستند که مستقیماً یکدیگر را فراخوانی می کنند. در این مورد پشته تماس بومی با پشته تماس جاوا یکسان است.

    #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 کد جاوا در حال تفسیر است.

    #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 خود ماشین مجازی هستند. این بار این کد است که شروع به اجرای جاوا در یک رشته جدید می کند.

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

فریم های شماره 50 تا 51 نحوه شروع همه موضوعات است. این کد شروع رشته جدید libc است.