Google cam kết thúc đẩy công bằng chủng tộc cho các cộng đồng Đen. Xem thế nào.
Trang này được dịch bởi Cloud Translation API.
Switch to English

Gỡ lỗi mã nền tảng Android gốc

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, truy tìm và lập hồ sơ 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 trong trang web này khuyên bạn nên sử dụng adb kết hợp với đối số setprop để gỡ lỗi các 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 thuộc tính bọc với tên của ứng dụng, cần phải cắt bớt tên cho phù hợp. Trong Android 8.0 trở lên, giới hạn này lớn hơn nhiều và không yêu cầu cắt bớt.

Trang này trình bày những kiến ​​thức cơ bản xung quanh kết xuất sự cố được tìm thấy 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 lỗi gốc , khám phá các dịch vụ hệ thống với dumpsys , xem bộ nhớ gốc , mạng và mức sử dụng 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 hệ thống ) và sử dụng trình gỡ lỗi GNU (GDB) và các công cụ gỡ lỗi khác.

Bãi rác và bia mộ

Khi một tệp thực thi được liên kết động bắt đầu, một số trình xử lý tín hiệu được đăng ký, trong trường hợp xảy ra sự cố, sẽ tạo ra một kết xuất lỗi cơ bản được ghi vào logcat và một tệp bia mộ chi tiết hơn sẽ được ghi vào /data/tombstones/ . Bia mộ là một tập tin có thêm dữ liệu về quá trình bị rơi. Đặc biệt, nó 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ố (không chỉ luồng bắt được tín hiệu), bản đồ bộ nhớ đầy đủ và danh sách tất cả các bộ mô tả tệp đang mở.

Trước Android 8.0, sự cố đã được xử lý bởi debuggerddebuggerd64 64 daemon. Trong Android 8.0 trở lên, crash_dump32crash_dump64 được tạo ra khi cần thiết.

Trình kết xuất sự cố chỉ có thể đính kèm nếu không có gì khác đã được đính kèm, điều đó có nghĩa là sử dụng các công cụ như strace hoặc gdb ngăn việc đổ lỗi xảy ra.

Đầu ra mẫu (đã xóa 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 cuối cùng của đầu ra cho biết vị trí của bia mộ đầy đủ trên đĩa.

Nếu bạn có sẵn các tệp nhị phân chưa được đánh dấu, bạn có thể xem chi tiết hơn với thông tin 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 bạn, vì vậy bạn không cần cung cấp đường dẫn đầy đủ.

Đầu ra 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ộ bia mộ. Thí 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ố và bia mộ bản địa, hãy xem Chẩn đoán sự cố bản địa .

Lấy dấu vết ngăn xếp / bia ​​mộ từ một quá trình đang chạy

Bạn có thể sử dụng công cụ debuggerd để lấy kết xuất ngăn xếp từ một quy trình đang chạy. Từ dòng lệnh, gọi debuggerd bằng cách sử dụng ID quy trình (PID) để kết xuấ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 bao gồm cờ -b hoặc --backtrace .

Hiểu một 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 sự 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 APK để tiết kiệm dung lượng đĩa thay vì được giải nén thành tệp .so riêng biệt. Trình giải nén ngăn xếp trong Android 9 trở lên không cần tệp .so được giải .so để đối phó với trường hợp phổ biến dành riêng cho Android này.

Khung # 00– # 02 không có tên biểu tượng vì chúng đã bị nhà phát triển loại bỏ.

Khung # 03 cho thấy rằng khi có các ký hiệu, trình giải nén sẽ sử dụng chúng.

    #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 thời hạn. Trình giải nén cũ sẽ dừng lại ở đây, không thể giải nén 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ừ việc triển khai trình thông dịch ART. Trình giải nén 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 số 11 giải thích mã trình thông dịch đang diễn giải. Những khung này hữu ích nếu bạn đang gỡ lỗi chính ART. Nếu bạn đ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 thông dịch.

    #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 việc 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 thông dịch. Phương thức Java này tương ứng với 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)

Khung # 18– # 20 là chính máy ảo, mã để chuyển từ mã Java đã biên dịch sang mã Java được 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 đã 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 một lệnh gọi phương thức từ mã được thông dịch sang phương thứ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 thông dịch.

    #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ã đã 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 # 32– # 34 là các khung Java đã biên dịch gọi nhau trực tiếp. Trong trường hợp này, ngăn xếp cuộc gọi gốc giống như ngăn xếp cuộc 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 chính 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 thông dịch.

    #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ả các chuỗi sẽ bắt đầu. Đây là mã bắt đầu luồng mới của libc .