Phần này tóm tắt các công cụ hữu ích và lệnh có liên quan để gỡ lỗi, theo dõi và phân tích 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à phần khác
trong trang web này, bạn nên sử dụng adb
cùng với
Đối số setprop
để gỡ lỗi một số khía cạ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 bao bọc có tên của ứng dụng,
cần phải cắt bớt tên cho vừa. Trong Android 8.0 trở lên, phương thức 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 thông tin cơ bản liên quan đến tệp kết xuất sự cố có trong đầu ra logcat.
Các trang khác có thông tin chi tiết hơn về
chẩn đoán trục trặc mã gốc
khám phá các dịch vụ hệ thống bằng
dumpsys
, đang xem
bộ nhớ gốc,
mạng,
và RAM
sử dụng, sử dụng AddressSanitizer để phát hiện bộ nhớ
lỗi trong mã gốc, đánh giá
vấn đề về hiệu suất (bao gồm
systrace) và sử dụng
trình gỡ lỗi.
Bãi xe va chạm và bia mộ
Khi 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ẽ
đã đăng ký rằng, trong trường hợp xảy ra sự cố, tệp kết xuất sự cố cơ bản sẽ được ghi vào logcat
và một tệp tombstone chi tiết hơn sẽ được ghi vào /data/tombstones/
.
Tombstone là một tệp có thêm dữ liệu về quá trình gặp sự cố. Cụ thể, hồ sơ này còn chứa
dấu vết ngăn xếp của tất cả các luồng trong quá trình gặp sự cố (không chỉ luồng bắt gặp
), bản đồ bộ nhớ đầy đủ và danh sách tất 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 thiết.
Trình thông báo sự cố có thể chỉ đính kèm nếu không có nội dung nào khác
đã được đính kèm, tức là việc sử dụng các công cụ như strace
hoặc
lldb
ngăn việc báo lỗi.
Kết quả ví dụ (đã 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 tombstone đầy đủ trên đĩa.
Nếu có tệp nhị phân chưa được gỡ bỏ, bạn có thể tải tệp nhị phân chi tiết hơn
thư giãn với thông tin 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 bạn đã chạy lunch
,
thì stack
đã có trên $PATH
, nên bạn không cần cung cấp
đường dẫn đầy đủ.
Kết quả ví dụ (dựa trên đầu ra 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ộ một tombstone. Ví dụ:
stack < FS/data/tombstones/tombstone_05
Điều này rất 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à tombstone, hãy xem Chẩn đoán trục trặc mã gốc.
Lấy dấu vết ngăn xếp hoặc tombstone qua một quy trình đang chạy
Bạn có thể sử dụng công cụ debuggerd
để lấy tệp kết xuất ngăn xếp qua một quy trình đang chạy.
Từ dòng lệnh, hãy gọi debuggerd
bằng mã quy trình (PID) để kết xuất
toàn bộ bia mộ đến stdout
. Để chỉ lấy ngăn xếp cho mỗi luồng trong
cho quy trình này, bao gồm cờ -b
hoặc --backtrace
.
Hiểu rõ một cách thư giãn 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)
Các khung hình #00–03 là từ mã JNI gốc đã được lưu trữ không nén trong APK để lưu ổ đĩa
thay vì được trích xuất vào một tệp .so
riêng biệt. Bộ 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ý vấn đề phổ biến này
Trường hợp dành riêng cho Android.
Khung #00–02 không có tên biểu tượng vì nhà phát triển đã xoá.
Khung #03 cho thấy khi có các ký hiệu, bộ gỡ bỏ sẽ sử dụng các ký hiệu đó.
#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 số 04 là mã Java được biên dịch trước khi hoạt động. Bộ gỡ bỏ cũ sẽ dừng lại tại đây, không thể để thư giãn 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)
Các khung hình từ 05 đến 10 là của quá trình triển khai trình phiên dịch ART.
Bộ 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ị những khung này mà không có ngữ cảnh
trong khung số 11 giải thích mã nào mà trình phiên dịch đang dịch. Những khung này rất hữu ích nếu
bạn đang gỡ lỗi ART. Nếu đang gỡ lỗi một ứng dụng, bạn có thể bỏ qua chúng. Một số công cụ, chẳng hạn như
simpleperf
, 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)
Các khung từ 12 đến 16 là quá trình triển khai trình phiên 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 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)
Các khung #18–20 là chính máy ảo, mã để chuyển đổi từ mã Java đã biên dịch sang mã Java thông dịch.
#21 pc 00000000002ce44c /system/framework/arm64/boot.oat (offset 0xdc000) (java.util.concurrent.FutureTask.run+204)
Khung #21 là phương thức Java được 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à triển khai trình thông dịch, thực hiện lệnh gọi phương thức từ thông dịch mã vào 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 sự chuyển đổi khác giữa mã được biên dịch và mã được thông dịch.
#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)
Các khung hình #32–34 được biên dịch, các khung Java gọi trực tiếp lẫn nhau. Trong trường hợp này, lệnh gọi gốc giống như 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)
Các khung hình #35–40 là 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)
Các khung #42–49 là chính máy ảo. Lần này, 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)
Các khung #50–51 là cách tất cả các luồng sẽ bắt đầu. Đây là libc
mã bắt đầu chuỗi mới.