diff --git a/android/debug.sh b/android/debug.sh new file mode 100755 index 000000000..1bcc5e7b9 --- /dev/null +++ b/android/debug.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash + +# mainly based on https://www.sh-zam.com/2019/05/debugging-krita-on-android.html + +set -euo pipefail +IFS=$'\n\t' + +# customize +default_app_id=uk.co.powdertoy.tpt +if which jq >/dev/null && [[ -f meson-info/intro-buildoptions.json ]]; then + default_app_id=$(jq -r '.[] | select(.name == "app_id") | .value' < meson-info/intro-buildoptions.json) +fi +app_id=${APP_ID:-$default_app_id} +lldb_server=${LLDB_SERVER:-/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/17/lib/linux/aarch64/lldb-server} +lldb_server_port=${LLDB_SERVER_PORT:-9998} +jdb_port=${JDB_PORT:-13456} +lldb_client=${LLDB_CLIENT:-/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/lldb.sh} +adb=${ADB:-adb} +jdb=${JDB:-jdb} + +# don't customize unless necessary +app_activity=${APP_ACTIVITY:-PowderActivity} +lldb_server_staging=${LLDB_SERVER_STAGING:-/data/local/tmp/lldb-server} +lldb_server_remote=${LLDB_SERVER_REMOTE:-lldb-server} +pidof_retry_count=${PIDOF_RETRY_COUNT:-20} +pidof_retry_delay=${PIDOF_RETRY_DELAY:-0.1} + +function check_which() { + if ! which $1; then + >&2 echo "[-] can't run $1" + return 1 + fi +} + +function check_env() { + if ! [[ -f $lldb_server ]]; then + >&2 echo "[-] $lldb_server doesn't exist" + return 1 + fi + check_which $lldb_client + check_which $adb + check_which $jdb +} + +function check_adb() { + $adb shell whoami >/dev/null +} + +function check_debuggable() { + $adb shell run-as $app_id whoami >/dev/null +} + +function find_lldb_server() { + $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null +} + +function kill_lldb_server() { + if ! $adb shell run-as $app_id pkill $lldb_server_remote; then + >&2 echo "[-] failed" + return 1 + fi +} + +function adb_forward() { + >&2 echo "[+] adb forwarding tcp:$jdb_port jdwp:$pid" + if ! ($adb forward tcp:$jdb_port jdwp:$pid | grep $jdb_port >/dev/null); then + >&2 echo "[+] failed" + return 1 + fi +} + +function adb_unforward() { + $adb forward --remove tcp:$jdb_port +} + +function undo_current_adb_forward() { + >&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port" + adb_unforward +} + +function maybe_undo_previous_adb_forward() { + if $adb forward --list | grep tcp:$jdb_port; then + >&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port" + adb_unforward + fi +} + +function maybe_kill_previous_lldb_server() { + if find_lldb_server; then + >&2 echo "[+] killing orphaned $lldb_server_remote" + kill_lldb_server + fi +} + +function kill_current_lldb_server() { + >&2 echo "[+] killing $lldb_server_remote" + kill_lldb_server +} + +function start_app() { + >&2 echo "[+] starting $app_id/.$app_activity" + set +e + $adb shell am start -D -n "$app_id/.$app_activity" >/dev/null + set -e + local i + local maybe_pid + local pidof_result + for ((i = 0; i <= $pidof_retry_count; i++)); do + set +e + maybe_pid=$($adb shell pidof $app_id) + pidof_result=$? + set -e + if [[ $pidof_result == 0 ]]; then + pid=$maybe_pid + break + fi + sleep $pidof_retry_delay + done + if [[ -z ${pid-} ]]; then + >&2 echo "[-] failed" + return 1 + fi + echo $pid +} + +function jdb_attach() { + >&2 echo "[+] attaching jdb in the background" + $jdb -attach localhost:$jdb_port >/dev/null 2>/dev/null & + disown $! + # at some point jdb exits because it doesn't have an stdin... fine by me +} + +function maybe_deploy_lldb_server() { + if ! $adb shell [[ -f $lldb_server_staging ]]; then + >&2 echo "[+] $lldb_server_remote not present on host, deploying" + if ! ($adb push $lldb_server $lldb_server_staging && $adb shell chmod +x $lldb_server_staging); then + >&2 echo "[-] failed" + fi + fi +} + +function start_lldb_server() { + if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then + >&2 echo "[+] $lldb_server_remote not running on host, starting" + $adb shell run-as $app_id cp $lldb_server_staging /data/data/$app_id/$lldb_server_remote + $adb shell run-as $app_id ./$lldb_server_remote platform --server --listen "*:$lldb_server_port" >/dev/null 2>/dev/null & + disown $! + if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then + >&2 echo "[-] failed" + return 1 + fi + fi +} + +function start_lldb() { + local pid=$1 + >&2 echo "[+] starting $lldb_client" + local lldb_init=$(mktemp) + cat - << LLDB_INIT > $lldb_init +platform select remote-android +platform connect connect://localhost:$lldb_server_port +attach $pid +continue +LLDB_INIT + local lldb_status + set +e + $lldb_client --source $lldb_init + lldb_status=$? + set -e + >&2 echo "[+] $lldb_client exited with status $lldb_status" + rm $lldb_init +} + +check_env +check_adb +check_debuggable +maybe_kill_previous_lldb_server +maybe_undo_previous_adb_forward +if [[ ${1-} == clean ]]; then + >&2 echo "[+] done" + exit 0 +fi +maybe_deploy_lldb_server +start_lldb_server +pid=$(start_app) +adb_forward +jdb_attach +start_lldb $pid +kill_current_lldb_server +undo_current_adb_forward +>&2 echo "[+] done"