Hướng dẫnĐăng ngày: 7 phút đọc

Cách tìm lỗi crash ứng dụng trong logcat: stack trace, ANR, bộ lọc

Tìm nhanh lỗi crash ứng dụng trong logcat. Đọc stack trace FATAL EXCEPTION từ dưới lên, nhận diện ANR và crash native, lọc theo mức, tag, PID và xuất log sạch.

Để tìm lỗi crash ứng dụng trong logcat, hãy xoá bộ đệm, lọc chỉ còn lỗi bằng adb logcat *:E, tái hiện crash, rồi tìm khối bắt đầu bằng FATAL EXCEPTION dưới tag AndroidRuntime. Dòng Caused by: cuối cùng là nguyên nhân gốc, còn dòng at đầu tiên thuộc package của bạn là nơi nên bắt đầu đọc code. Bài này đi qua crash Java/Kotlin, ANR và crash native, các bộ lọc giúp tìm nhanh, và cách viết báo cáo lỗi. Nếu bạn mới làm quen với logcat, hãy đọc cách đọc logcat trên Android trước.

Crash ứng dụng trông như thế nào trong logcat

Một exception không được bắt trong ứng dụng Java hoặc Kotlin luôn tạo ra khối log cùng dạng dưới tag AndroidRuntime ở mức E:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.app, PID: 18422
    java.lang.IllegalStateException: Fragment not attached to a context.
        at androidx.fragment.app.Fragment.requireContext(Fragment.java:960)
        at com.example.app.ui.ProfileFragment.loadAvatar(ProfileFragment.kt:88)
    Caused by: java.lang.NullPointerException: user must not be null
        at com.example.app.data.UserRepository.current(UserRepository.kt:34)
        ... 12 more

Các dòng quan trọng, theo thứ tự nên đọc:

DòngÝ nghĩa
FATAL EXCEPTION: mainLuồng đã chết. main là luồng giao diện, nên ứng dụng đóng ngay.
Process: com.example.app, PID: 18422Tên gói và ID tiến trình, xác nhận đó là ứng dụng của bạn.
Caused by: cuối cùngNguyên nhân gốc. Exception được bọc nhiều lớp, nên đọc từ dưới lên.
Dòng at com.example... đầu tiênDòng code của bạn gần điểm lỗi nhất. Các frame android.*, androidx.*, java.* phía trên chỉ là ngữ cảnh.

Tip: Android giữ riêng một bộ đệm log crash. adb logcat -b crash -d chỉ in ra các crash, rất tiện khi bạn không kịp theo dõi lúc lỗi xảy ra.

ANR: khi ứng dụng treo thay vì crash

Hộp thoại Application Not Responding không phải exception. Tiến trình vẫn sống nhưng luồng chính bị chặn quá lâu: khoảng 5 giây với nhập liệu, 10 giây với broadcast receiver, 20 giây với foreground service. Trong logcat, ANR nằm dưới tag ActivityManager:

E ActivityManager: ANR in com.example.app (com.example.app/.MainActivity)
E ActivityManager: PID: 18422
E ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event ...)
E ActivityManager:   96% 18422/com.example.app: 91% user + 5% kernel

Dòng Reason: cho biết nguyên nhân: Input dispatching timed out là thao tác chạm không được xử lý, Broadcast of Intent là receiver bị chặn, executing service là service chạy quá lâu. Dòng CPU cho biết tiến trình đang bận (việc nặng trên luồng chính) hay đang chờ (khoá, gọi mạng đồng bộ).

Stack của từng luồng lúc ANR không nằm trong logcat mà trong tệp traces; cách đơn giản nhất để lấy là tạo bug report:

adb bugreport anr-report.zip

Trong tệp ZIP, tìm thư mục FS/data/anr/; luồng "main" cho thấy luồng giao diện đang làm gì khi hệ thống bỏ cuộc. Thiết bị cũ ghi vào /data/anr/traces.txt.

Crash native: signal 11 và tombstone

Ứng dụng dùng NDK, hoặc thư viện dùng NDK (bộ giải mã video, game engine), có thể crash ở tầng dưới Java. Khi đó không có FATAL EXCEPTION; thay vào đó tag DEBUG in ra:

F DEBUG   : pid: 18422, tid: 18490, name: RenderThread  >>> com.example.app <<<
F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
F DEBUG   : backtrace:
F DEBUG   :       #00 pc 000000000004f2a8  /data/app/.../lib/arm64/libnative.so (decode_frame+120)
F DEBUG   :       #01 pc 00000000000b113c  /data/app/.../lib/arm64/libnative.so (Java_com_example_app_Decoder_next+44)

signal 11 (SIGSEGV) là truy cập bộ nhớ không hợp lệ, thường do con trỏ null; signal 6 (SIGABRT) là lệnh dừng chủ đích từ assert thất bại. Frame #00 của backtrace là nơi phát sinh lỗi. Bản dump đầy đủ được lưu dưới dạng tombstone trong /data/tombstones/ và gom vào adb bugreport. Nếu các frame chỉ hiện địa chỉ hex, thư viện đã bị strip và cần symbolise bằng ndk-stack.

Các bộ lọc giúp tìm crash nhanh hơn

Điện thoại ghi hàng nghìn dòng log mỗi phút, còn crash chỉ chiếm mười dòng. Các bộ lọc sau cắt bỏ phần còn lại.

Mục tiêuLệnh
Lỗi trở lên từ mọi tagadb logcat *:E
Một tag từ mức Debug trở lên, mọi thứ khác im lặngadb logcat MyTag:D *:S
Chỉ tiến trình của ứng dụng bạnadb logcat --pid=$(adb shell pidof -s com.example.app)
Xoá bộ đệm trước khi tái hiệnadb logcat -c
Dấu thời gian dễ đọcadb logcat -v time
In nội dung bộ đệm rồi thoátadb logcat -d
Lưu bản dump ra tệpadb logcat -d -v time > crash.txt

Một phiên ghi log điển hình gồm hai lệnh với bước tái hiện ở giữa: xoá, tái hiện đến khi hộp thoại hiện ra, rồi dump.

adb logcat -c
adb logcat -d -v time *:W > crash.txt

Note: --pid ngừng khớp khi ứng dụng khởi động lại, vì tiến trình mới có PID mới. Nếu crash khiến ứng dụng tự mở lại, hãy tìm theo tên gói hoặc lọc theo tag.

Log bị rơi và kích thước bộ đệm

Nếu bản ghi có khoảng trống, hoặc logcat in ra chatty: uid=10234 com.example.app expire 180 lines, thiết bị đã bỏ bớt dòng. Bộ đệm log là vòng tròn và nhỏ (thường 256 KB), nên ứng dụng ghi nhiều có thể đẩy crash ra ngoài trong vài giây, và logd cũng gộp các dòng lặp lại thành số đếm chatty hoặc expire.

Để mở rộng, vào Cài đặt → Tuỳ chọn nhà phát triển → Logger buffer sizes và nâng lên 4 MB hoặc 16 MB trước phiên tái hiện dài. Từ dòng lệnh:

adb logcat -g              # xem kích thước bộ đệm hiện tại
adb logcat -G 16M          # đặt kích thước bộ đệm

Tìm crash trong ADBKit

ADBKit hiển thị logcat ngay cạnh màn hình thiết bị trực tiếp, nên bạn vừa thao tác vừa xem log mà không cần terminal. Nó chạy trong Chrome hoặc Edge qua WebUSB; không cài gì và dữ liệu log chỉ đi giữa trình duyệt và điện thoại.

  1. Kết nối điện thoại và mở công cụ Logcat. Đặt Minimum log level thành Error để cảnh báo và dòng rác biến mất.
  2. Dùng bộ lọc App để chọn gói của bạn, hoặc gõ tên gói vào ô tìm kiếm (các dòng stack trace cũng được tìm).
  3. Bấm Clear logs, rồi tái hiện crash trên màn hình trực tiếp.
  4. Khi crash xuất hiện, bấm Pause để khung nhìn dừng cuộn. Mục FATAL EXCEPTION gấp các dòng trace bên dưới; mở rộng ra và chuột phải chọn Copy stack trace.
  5. Bấm Export logs để lưu các dòng đang hiển thị thành tệp văn bản. Bộ lọc mức và ứng dụng áp dụng cho cả bản xuất, nên tệp chỉ chứa crash.

Dùng công cụ Screenshot để chụp hộp thoại lỗi, hoặc bật quay màn hình trước khi tái hiện; xem chụp và quay màn hình Android. Bảng cũng hiển thị số logs dropped; nếu tăng, hãy nâng kích thước bộ đệm như trên.

Viết báo cáo lỗi để được sửa

Báo cáo crash có ích khi nhà phát triển tái hiện được mà không cần hỏi lại. Hãy kèm:

  • Các bước, đánh số, từ một trạng thái xác định ("cài mới", "đăng nhập bằng tài khoản test").
  • Thiết bị và hệ điều hành: dòng máy, phiên bản Android, bản build. adb shell getprop ro.product.model và adb shell getprop ro.build.version.release cho hai thông tin đầu.
  • Trích đoạn log: toàn bộ khối FATAL EXCEPTION gồm mọi dòng Caused by:, khối ANR in, hoặc backtrace của DEBUG. Đính kèm tệp đã xuất; chỉ dán inline 20 đến 40 dòng.
  • Ảnh chụp hoặc video điều người dùng nhìn thấy.
  • Tần suất: lần nào cũng gặp, hay một trong năm lần.

Warning: Log có thể chứa email, token xác thực và dữ liệu cá nhân. Hãy đọc kỹ trích đoạn trước khi dán vào issue tracker công khai và che đi những gì không nên có.

Phần còn lại của bộ lệnh có trong bảng tóm tắt lệnh ADB; nếu bạn kiểm thử build mà không dùng IDE, xem kiểm thử APK trên thiết bị thật không cần Android Studio.