Phần này tóm tắt các công cụ hữu ích và các lệnh liên quan để gỡ lỗi, theo dõi và phân tích tài nguyên mã nền tảng Android gốc khi phát triển các tính năng cấp nền tảng.
Lưu ý: Các trang trong phần này và các trang khác trên trang web này đề xuất sử dụng adb
kết hợp với đối số setprop
để gỡ lỗi một số khía cạnh nhất định của Android.
Trong Android 7.x trở xuống, tên thuộc tính có giới hạn độ dài là 32 ký tự. Điều này có nghĩa là để tạo một thuộc tính gói có tên của ứng dụng, bạn cần phải cắt bớt tên để vừa với thuộc tính. Trên Android 8.0 trở lên, giới hạn này lớn hơn nhiều và không cần phải cắt bớt.
Trang này trình bày những thông tin cơ bản về tệp báo lỗi trong đầu ra logcat.
Các trang khác có nhiều thông tin chi tiết hơn về việc chẩn đoán sự cố gốc, khám phá các dịch vụ hệ thống bằng
dumpsys
, xem mức sử dụng bộ nhớ gốc, mạng và RAM, sử dụng AddressSanitizer để phát hiện lỗi bộ nhớ trong mã gốc, đánh giá các vấn đề về hiệu suất (bao gồm systrace) và sử dụng trình gỡ lỗi.
Tệp báo lỗi và tệp tombstone
Khi một tệp thực thi được liên kết động khởi động, một số trình xử lý tín hiệu sẽ được đăng ký. Trong trường hợp xảy ra sự cố, tệp báo lỗi cơ bản sẽ được ghi vào logcat và tệp tombstone chi tiết hơn sẽ được ghi vào /data/tombstones/
.
Tombstone là một tệp có dữ liệu bổ sung về quá trình gặp sự cố. Cụ thể, tệp này chứa dấu vết ngăn xếp cho tất cả các luồng trong quá trình gặp sự cố (chứ không chỉ luồng đã phát hiện tín hiệu), bản đồ bộ nhớ đầy đủ và danh sách tất cả các chỉ số mô tả tệp đang mở.
Trước Android 8.0, các sự cố được xử lý bằng trình nền debuggerd
và debuggerd64
. Trong Android 8.0 trở lên, crash_dump32
và crash_dump64
được tạo khi cần.
Trình kết xuất báo lỗi có thể chỉ đính kèm nếu không có gì khác được đính kèm, tức là việc sử dụng các công cụ như strace
hoặc lldb
sẽ ngăn việc xảy ra báo lỗi.
Kết quả mẫu (đã xoá dấu thời gian và thông tin không liên quan):
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 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
Dòng đầu ra cuối cùng cho biết vị trí của tệp tombstone đầy đủ trên ổ đĩa.
Nếu có các tệp nhị phân chưa bị loại bỏ, bạn có thể xem thông tin chi tiết hơn về số dòng bằng cách dán ngăn xếp vào development/scripts/stack
:
development/scripts/stack
Mẹo: Để thuận tiện, nếu đã chạy lunch
, thì stack
đã có trên $PATH
, vì vậy, bạn không cần cung cấp đường dẫn đầy đủ.
Kết quả ví dụ (dựa trên kết quả logcat ở trên):
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)
Bạn có thể sử dụng stack
trên toàn bộ bia mộ. Ví dụ:
stack < FS/data/tombstones/tombstone_05
Điều này sẽ hữu ích nếu bạn vừa giải nén một báo cáo lỗi trong thư mục hiện tại. Để biết thêm thông tin về cách chẩn đoán sự cố gốc và bia mộ, hãy xem phần Chẩn đoán sự cố gốc.
Nhận dấu vết ngăn xếp hoặc bia mộ từ một quy trình đang chạy
Bạn có thể sử dụng công cụ debuggerd
để tải tệp báo lỗi từ một quy trình đang chạy.
Từ dòng lệnh, hãy gọi debuggerd
bằng mã nhận dạng quy trình (PID) để kết xuất một bia mộ đầy đủ vào stdout
. Để chỉ lấy ngăn xếp cho mọi luồng trong quy trình, hãy thêm cờ -b
hoặc --backtrace
.
Tìm hiểu về một quá trình tháo dỡ phức tạp
Khi một ứng dụng gặp sự cố, ngăn xếp có xu hướng khá phức tạp. Ví dụ chi tiết sau đây nêu bật nhiều điểm phức tạp:
#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)
Khung #00–#03 là từ mã JNI gốc được lưu trữ không nén trong tệp APK để tiết kiệm dung lượng đĩa thay vì được trích xuất vào một tệp .so
riêng biệt. Trình gỡ bỏ ngăn xếp trong Android 9 trở lên không cần tệp .so
đã trích xuất để xử lý trường hợp phổ biến này dành riêng cho Android.
Khung #00–#02 không có tên ký hiệu vì nhà phát triển đã xoá các tên này.
Khung #03 cho thấy rằng trình gỡ bỏ sẽ sử dụng các biểu tượng khi có sẵn.
#04 pc 0000000000117550 /data/dalvik-cache/arm64/system@priv-app@Velvet@Velvet.apk@classes.dex (offset 0x108000) (com.google.speech.recognizer.AbstractRecognizer.nativeRun+160)
Khung #04 là mã Java được biên dịch trước. Trình gỡ bỏ cũ sẽ dừng tại đây, không thể gỡ bỏ thông qua 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)
Khung #05–#10 là từ quá trình triển khai trình thông dịch ART.
Trình gỡ bỏ ngăn xếp trong các bản phát hành thấp hơn Android 9 sẽ hiển thị các khung này mà không có ngữ cảnh của khung #11 giải thích mã mà trình thông dịch đang diễn giải. Các khung này sẽ hữu ích nếu bạn đang gỡ lỗi chính ART. Nếu đang gỡ lỗi ứng dụng, bạn có thể bỏ qua các lỗi này. Một số công cụ, chẳng hạn như simpleperf
, sẽ tự động bỏ qua các khung này.
#11 pc 00000000001992d6 /system/priv-app/Velvet/Velvet.apk (offset 0x26cf000) (com.google.speech.recognizer.AbstractRecognizer.run+18)
Khung #11 là mã Java đang được diễn giải.
#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)
Khung #12 –#16 là chính quá trình triển khai trình thông dịch.
#17 pc 00000000002454a0 /system/priv-app/Velvet/Velvet.apk (offset 0x1322000) (com.google.android.apps.gsa.speech.e.c.c.call+28)
Khung #17 là mã Java đang được diễn giải. Phương thức Java này tương ứng với các khung trình thông dịch #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)
Khung #18 –#20 là chính máy ảo, mã để chuyển đổi từ mã Java được biên dịch sang mã Java được diễn giải.
#21 pc 00000000002ce44c /system/framework/arm64/boot.oat (offset 0xdc000) (java.util.concurrent.FutureTask.run+204)
Khung #21 là phương thức Java đã biên dịch gọi phương thức Java trong #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)
Khung #22 –#27 là quá trình triển khai trình thông dịch, tạo lệnh gọi phương thức từ mã được diễn giải sang phương thức được biên dịch.
#28 pc 00000000003ed69e /system/priv-app/Velvet/Velvet.apk (com.google.android.apps.gsa.shared.util.concurrent.b.e.run+22)
Khung #28 là mã Java đang được diễn giải.
#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)
Khung #29–#31 là một quá trình chuyển đổi khác giữa mã được biên dịch và mã được diễn giải.
#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)
Khung #32 –#34 là các khung Java được biên dịch gọi trực tiếp lẫn nhau. Trong trường hợp này, ngăn xếp lệnh gọi gốc giống với ngăn xếp lệnh gọi 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)
Khung #35–#40 là chính trình thông dịch.
#41 pc 00000000003ed8e0 /system/priv-app/Velvet/Velvet.apk (com.google.android.apps.gsa.shared.util.concurrent.b.i.run+20)
Khung #41 là mã Java đang được diễn giải.
#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)
Khung #42–#49 là chính máy ảo. Lần này, đó là mã bắt đầu chạy Java trên một luồng mới.
#50 pc 0000000000082e24 /system/lib64/libc.so (__pthread_start(void*)+36) #51 pc 00000000000233bc /system/lib64/libc.so (__start_thread+68)
Khung #50–#51 là cách tất cả luồng bắt đầu. Đây là mã bắt đầu luồng mới libc
.