Beim Debuggen und Erstellen von Profilen für Anwendungen mit nativem Code ist es oft nützlich, Debugging-Tools zu verwenden, die beim Start des Prozesses aktiviert werden müssen. Dies erfordert, dass Sie die Anwendung in einem neuen Prozess ausführen, anstatt sie aus der Zygote zu klonen. Beispiele:
- Systemaufrufe mit strace verfolgen.
- Ermitteln von Arbeitsspeicherfehlern mit Malloc Debug oder Address Sanitizer (ASan)
- Profilerstellung mit Simpleperf.
Shell-Skript für Wrapping verwenden
Die Verwendung von wrap.sh
ist einfach:
- Kompilieren Sie ein benutzerdefiniertes debugfähiges APK, das Folgendes verpackt:
- Ein Shell-Skript mit dem Namen
wrap.sh
. Weitere Informationen finden Sie unter Wrapping-Shell-Script erstellen und Package wrap.sh. - Alle zusätzlichen Tools, die Ihr Shell-Skript benötigt (z. B. Ihr eigenes
strace
-Binärprogramm).
- Ein Shell-Skript mit dem Namen
- Installiere das Debug-fähige APK auf einem Gerät.
- Starten Sie die App.
Shell-Skript für Wrapping erstellen
Wenn Sie ein debugfähiges APK starten, das wrap.sh
enthält, führt das System das Skript aus und übergibt den Befehl zum Starten der App als Argumente. Das Skript ist für das Starten der Anwendung verantwortlich, kann jedoch alle Umgebungs- oder Argumentänderungen vornehmen. Das Skript sollte der MirBSD Korn Shell-Syntax (mksh) folgen.
Das folgende Snippet zeigt, wie Sie eine einfache wrap.sh
-Datei schreiben, mit der nur die Anwendung gestartet wird:
#!/system/bin/sh exec "$@"
Malloc-Fehlerbehebung
Sie würden die folgende Zeile einfügen, um die Malloc-Fehlerbehebung über wrap.sh
zu verwenden:
#!/system/bin/sh LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper "$@"
Asan
In der ASan-Dokumentation finden Sie ein Beispiel für die entsprechende Vorgehensweise für ASan.
Paket wrap.sh
Damit du die Vorteile von wrap.sh
nutzen kannst, muss dein APK debugfähig sein. Achte darauf, dass die Einstellung android:debuggable="true"
im Element <application>
in deinem Android-Manifest konfiguriert ist. Wenn du Android Studio verwendest, hast du in der Datei build.gradle
einen Debug-Build konfiguriert.
Außerdem musst du in der Datei build.gradle
deiner App useLegacyPackaging
auf true
setzen. In den meisten Fällen ist diese Option standardmäßig auf false
gesetzt, sodass Sie sie explizit auf true
setzen sollten, um Überraschungen zu vermeiden.
Sie müssen das Skript wrap.sh
mit den nativen Bibliotheken der App verpacken. Wenn Ihre App keine nativen Bibliotheken enthält, fügen Sie das lib-Verzeichnis manuell Ihrem Projektverzeichnis hinzu. Für jede von Ihrer Anwendung unterstützte Architektur müssen Sie in diesem nativen Bibliotheksverzeichnis eine Kopie des Wrap-Shell-Skripts bereitstellen.
Das folgende Beispiel zeigt das Dateilayout zur Unterstützung der Architektur ARMv8 und x86-64:
# App Directory |- AndroidManifest.xml |- … |- lib |- arm64-v8a |- ... |- wrap.sh |- x86_64 |- ... |- wrap.sh
In Android Studio werden nur .so
-Dateien aus den lib/
-Verzeichnissen gepackt. Wenn du Android Studio-Nutzer bist, musst du deine wrap.sh
-Dateien stattdessen in den src/main/resources/lib/*
-Verzeichnissen ablegen, damit sie richtig gepackt werden.
resources/lib/x86
wird in der UI als lib.x86
angezeigt, sollte tatsächlich aber ein Unterverzeichnis sein:
Fehler mit wrap.sh beheben
Wenn Sie bei der Verwendung von wrap.sh
einen Debugger hinzufügen möchten, muss das Shell-Skript die Fehlerbehebung manuell aktivieren. Die Vorgehensweise hierfür variiert zwischen Releases. In diesem Beispiel wird gezeigt, wie Sie die entsprechenden Optionen für alle Releases hinzufügen, die wrap.sh
unterstützen:
#!/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