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 moreCác dòng quan trọng, theo thứ tự nên đọc:
| Dòng | Ý nghĩa |
|---|---|
FATAL EXCEPTION: main | Luồng đã chết. main là luồng giao diện, nên ứng dụng đóng ngay. |
Process: com.example.app, PID: 18422 | Tê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ùng | Nguyê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ên | Dò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 -dchỉ 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% kernelDò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.zipTrong 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êu | Lệnh |
|---|---|
| Lỗi trở lên từ mọi tag | adb logcat *:E |
| Một tag từ mức Debug trở lên, mọi thứ khác im lặng | adb logcat MyTag:D *:S |
| Chỉ tiến trình của ứng dụng bạn | adb logcat --pid=$(adb shell pidof -s com.example.app) |
| Xoá bộ đệm trước khi tái hiện | adb logcat -c |
| Dấu thời gian dễ đọc | adb logcat -v time |
| In nội dung bộ đệm rồi thoát | adb logcat -d |
| Lưu bản dump ra tệp | adb 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.txtNote:
--pidngừ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ộ đệmTì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.
- 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.
- 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).
- Bấm Clear logs, rồi tái hiện crash trên màn hình trực tiếp.
- Khi crash xuất hiện, bấm Pause để khung nhìn dừng cuộn. Mục
FATAL EXCEPTIONgấp các dòng trace bên dưới; mở rộng ra và chuột phải chọn Copy stack trace. - 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.modelvàadb shell getprop ro.build.version.releasecho hai thông tin đầu. - Trích đoạn log: toàn bộ khối
FATAL EXCEPTIONgồm mọi dòngCaused by:, khốiANR in, hoặc backtrace củaDEBUG. Đí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.