Do debugowania i profilowania aplikacji za pomocą kodu natywnego często warto użyć narzędzi do debugowania, które trzeba włączyć przy uruchamianiu procesu. Wymaga to uruchomienia aplikacji w nowym procesie, a nie jej klonowania z zygoty. Przykłady:
- Śledzenie wywołań systemowych za pomocą strace.
- Wykrywanie błędów w pamięci za pomocą debugowania malloc lub Address Sanitizer (ASan).
- Profilowanie za pomocą Simpleperf.
Używanie skryptu powłoki zawijania
Korzystanie z wrap.sh
jest łatwe:
- Skompiluj niestandardowy plik APK z możliwością debugowania, który zawiera następujące pakiety:
- Skrypt powłoki o nazwie
wrap.sh
. Więcej informacji znajdziesz w materiałach na temat tworzenia skryptu powłoki zawijania i tworzenia pliku wrap.sh. - wszelkie dodatkowe narzędzia, których potrzebuje skrypt powłoki (np. Twój plik binarny
strace
).
- Skrypt powłoki o nazwie
- Zainstaluj na urządzeniu plik APK możliwy do debugowania.
- Uruchom aplikację.
Tworzenie skryptu powłoki opakowującej
Gdy uruchomisz pakiet APK z możliwością debugowania, który zawiera wrap.sh
, system uruchomi skrypt i przekaże polecenie uruchomienia aplikacji jako argumenty. Skrypt odpowiada za uruchomienie aplikacji, ale może wprowadzać dowolne zmiany w środowisku lub argumentach. Skrypt powinien być zgodny ze składnią powłoki MirBSD Korn (mksh).
Ten fragment kodu pokazuje, jak napisać prosty plik wrap.sh
, który powoduje uruchomienie aplikacji:
#!/system/bin/sh exec "$@"
Debugowanie Malloca
Aby użyć debugowania malloc za pomocą wrap.sh
, musisz dodać ten wiersz:
#!/system/bin/sh LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"
ASan
Przykład znajdziesz w dokumentacji ASan.
Opakowywanie wrap.sh
Aby można było skorzystać z funkcji wrap.sh
, plik APK musi być możliwy do debugowania. Upewnij się, że ustawienie android:debuggable="true"
jest skonfigurowane w elemencie <application>
w pliku manifestu Androida lub jeśli w Android Studio masz skonfigurowaną kompilację debugowania w pliku build.gradle
.
Musisz też ustawić useLegacyPackaging
na true
w pliku build.gradle
aplikacji. W większości przypadków ta opcja ma domyślną wartość false
, dlatego warto ustawić ją bezpośrednio na true
, aby uniknąć niespodzianek.
Musisz spakować skrypt wrap.sh
z natywnymi bibliotekami aplikacji. Jeśli Twoja aplikacja nie zawiera bibliotek natywnych, dodaj ręcznie katalog lib do katalogu projektu. W przypadku każdej architektury obsługiwanej przez Twoją aplikację musisz udostępnić kopię skryptu powłoki zawijania w katalogu biblioteki natywnej.
Poniższy przykład przedstawia układ pliku zapewniający obsługę architektur ARMv8 i x86-64:
# App Directory |- AndroidManifest.xml |- … |- lib |- arm64-v8a |- ... |- wrap.sh |- x86_64 |- ... |- wrap.sh
Android Studio pakuje tylko pliki .so
z katalogów lib/
. Jeśli korzystasz z Androida Studio, pliki wrap.sh
musisz umieścić w katalogach src/main/resources/lib/*
, by zostały poprawnie spakowane.
Uwaga: adres resources/lib/x86
będzie wyświetlany w interfejsie jako lib.x86
, ale w rzeczywistości powinien to być podkatalog:
Debuguj przy użyciu wrap.sh
Jeśli chcesz podłączyć debuger, gdy używasz wrap.sh
, musisz ręcznie włączyć debugowanie za pomocą skryptu powłoki. Różnią się sposoby wykonywania tych czynności w poszczególnych wersjach, więc w tym przykładzie pokazujemy, jak dodawać odpowiednie opcje dla wszystkich wersji obsługujących 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