Khi gỡ lỗi và phân tích tài nguyên cho ứng dụng bằng mã gốc, bạn nên dùng những công cụ gỡ lỗi cần được bật khi khởi động quy trình. Muốn vậy, bạn phải chạy ứng dụng theo một quy trình mới thay vì sao chép từ zygote. Ví dụ:
- Theo dõi lệnh gọi từ hệ thống bằng strace.
- Tìm lỗi bộ nhớ bằng tính năng gỡ lỗi Malloc hoặc Address Sanitizer (ASan – công cụ phát hiện lỗi bộ nhớ).
- Phân tích tài nguyên bằng Simpleperf.
Sử dụng tập lệnh bao bọc môi trường shell
wrap.sh
rất dễ sử dụng:
- Biên dịch một tệp APK tuỳ chỉnh có thể gỡ lỗi giúp đóng gói nội dung sau:
- Một tập lệnh môi trường shell có tên
wrap.sh
. Xem Tạo tập lệnh bao bọc môi trường shell và Đóng gói wrap.sh để biết thêm chi tiết. - Bất kỳ công cụ bổ sung nào mà tập lệnh môi trường shell cần (chẳng hạn như tệp nhị phân
strace
của riêng bạn).
- Một tập lệnh môi trường shell có tên
- Cài đặt APK có thể gỡ lỗi trên một thiết bị.
- Khởi chạy ứng dụng.
Tạo tập lệnh bao bọc môi trường shell
Khi bạn khởi chạy một APK có thể gỡ lỗi chứa wrap.sh
, hệ thống sẽ thực thi tập lệnh và truyền lệnh khởi động ứng dụng dưới dạng đối số. Tập lệnh chịu trách nhiệm khởi động ứng dụng, nhưng có thể làm thay đổi bất kỳ môi trường hoặc đối số nào. Tập lệnh phải tuân theo cú pháp MirBSD Korn shell (mksh).
Đoạn mã sau đây cho biết cách viết một tệp wrap.sh
đơn giản chỉ có trách nhiệm khởi động ứng dụng:
#!/system/bin/sh exec "$@"
Gỡ lỗi Malloc
Để sử dụng công cụ gỡ lỗi malloc thông qua wrap.sh
, bạn cần bao gồm dòng sau:
#!/system/bin/sh LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"
ASan
Tài liệu về ASan có một ví dụ về cách thực hiện việc này cho ASan.
Đóng gói wrap.sh
Để tận dụng wrap.sh
, APK của bạn phải là tệp có thể gỡ lỗi. Hãy đảm bảo rằng chế độ cài đặt android:debuggable="true"
được định cấu hình trong phần tử <application>
của tệp kê khai Android hoặc nếu bạn đang sử dụng Android Studio, thì đảm bảo bạn đã định cấu hình bản gỡ lỗi trong tệp build.gradle
.
Bạn cũng cần đặt useLegacyPackaging
thành true
trong tệp build.gradle
của ứng dụng. Trong hầu hết các trường hợp, tuỳ chọn này được đặt là false
theo mặc định. Vì vậy, bạn nên đặt tuỳ chọn này là true
một cách rõ ràng để tránh mọi tình huống bất ngờ.
Bạn phải đóng gói tập lệnh wrap.sh
bằng các thư viện gốc của ứng dụng. Nếu ứng dụng của bạn không chứa thư viện gốc, hãy thêm thư mục lib vào thư mục dự án theo cách thủ công. Đối với mỗi cấu trúc mà ứng dụng của bạn hỗ trợ, bạn phải cung cấp một bản sao của tập lệnh bao bọc môi trường shell trong thư mục thư viện gốc đó.
Ví dụ sau đây cho thấy bố cục tệp để hỗ trợ cả cấu trúc ARMv8 và x86-64:
# App Directory |- AndroidManifest.xml |- … |- lib |- arm64-v8a |- ... |- wrap.sh |- x86_64 |- ... |- wrap.sh
Android Studio chỉ đóng gói các tệp .so
từ thư mục lib/
nên nếu là người dùng Android Studio, bạn cần đặt tệp wrap.sh
trong thư mục src/main/resources/lib/*
để chúng được đóng gói đúng cách.
Lưu ý: resources/lib/x86
sẽ hiển thị trong giao diện người dùng dưới dạng lib.x86
nhưng đây thực ra là một thư mục con:
Gỡ lỗi khi sử dụng wrap.sh
Nếu muốn đính kèm một trình gỡ lỗi khi sử dụng wrap.sh
, tập lệnh môi trường shell của bạn sẽ cần bật tính năng gỡ lỗi theo cách thủ công. Cách thực hiện việc này sẽ khác nhau giữa các bản phát hành, vì vậy, ví dụ này cho biết cách thêm tuỳ chọn thích hợp cho tất cả các bản phát hành hỗ trợ wrap.sh
:
#!/system/bin/sh
cmd=$1
shift
os_version=$(getprop ro.build.version.sdk)
if [ "$os_version" -eq "27" ]; then
cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
elif [ "$os_version" -eq "28" ]; then
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
else
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
fi
exec $cmd