mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
354 lines
13 KiB
Bash
354 lines
13 KiB
Bash
#!/bin/sh
|
|
set -eu
|
|
|
|
ADB="${ADB:-adb}"
|
|
PYTHON="${PYTHON:-python3}"
|
|
INFRASTRUCTURE_FAILURE_EXIT_CODE=75
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
sh android-plugin/trace-replay-ci.sh \
|
|
--apk-file FILE \
|
|
--package PACKAGE \
|
|
--backend BACKEND \
|
|
--result-root DIR \
|
|
--fixture-root DIR \
|
|
--case NAME \
|
|
--trace-archive FILE \
|
|
--trace-file FILE_IN_ARCHIVE \
|
|
--golden FILE \
|
|
[--alternate-golden FILE] \
|
|
--target-call N \
|
|
--width N \
|
|
--height N \
|
|
--ssim-threshold N \
|
|
--crop-x N \
|
|
--crop-y N \
|
|
--crop-width N \
|
|
--crop-height N \
|
|
[--use-pbuffer] \
|
|
[--avoid-angle-llvmpipe-sampler-mipmap-min-filter] \
|
|
[--coherent-as-flush] \
|
|
--timeout-seconds N
|
|
|
|
Set MOBILEGL_USE_ANGLE=1 to run DirectGLES replay with packaged ANGLE
|
|
instead of the device system GLES driver.
|
|
Set MOBILEGL_TRACE_ANGLE_VARIANT to the packaged ANGLE short hash used by
|
|
DirectGLES replay.
|
|
Set MOBILEGL_RETRACE_USE_PBUFFER=1 or pass --use-pbuffer to run DirectGLES
|
|
against an offscreen EGL pbuffer instead of the Activity surface.
|
|
Pass --avoid-angle-llvmpipe-sampler-mipmap-min-filter for DirectGLES traces that
|
|
need ANGLE llvmpipe sampler mipmap filters downgraded to avoid driver stalls.
|
|
Pass --coherent-as-flush for traces whose engine writes persistent
|
|
GL_MAP_FLUSH_EXPLICIT_BIT maps it never flushes (MOBILEGL_COHERENT_AS_FLUSH=1).
|
|
EOF
|
|
}
|
|
|
|
die() {
|
|
echo "trace-replay-ci.sh: $*" >&2
|
|
usage >&2
|
|
exit 2
|
|
}
|
|
|
|
host_path_for_adb() {
|
|
if command -v cygpath >/dev/null 2>&1; then
|
|
cygpath -w "$1"
|
|
else
|
|
printf '%s' "$1"
|
|
fi
|
|
}
|
|
|
|
adb_device_path() {
|
|
MSYS_NO_PATHCONV=1 MSYS2_ARG_CONV_EXCL='*' "${ADB}" "$@"
|
|
}
|
|
|
|
next_arg() {
|
|
if [ "$#" -lt 2 ]; then
|
|
die "$1 requires a value"
|
|
fi
|
|
printf '%s' "$2"
|
|
}
|
|
|
|
require_value() {
|
|
value="$1"
|
|
name="$2"
|
|
if [ -z "${value}" ]; then
|
|
die "missing ${name}"
|
|
fi
|
|
}
|
|
|
|
apk_file=""
|
|
package_name=""
|
|
backend=""
|
|
result_root=""
|
|
fixture_root=""
|
|
case_name=""
|
|
trace_archive=""
|
|
trace_file=""
|
|
golden_path=""
|
|
alternate_golden_path=""
|
|
target_call=""
|
|
width=""
|
|
height=""
|
|
ssim_threshold=""
|
|
crop_x=""
|
|
crop_y=""
|
|
crop_width=""
|
|
crop_height=""
|
|
use_pbuffer=0
|
|
avoid_angle_llvmpipe_sampler_mipmap_min_filter=0
|
|
coherent_as_flush=0
|
|
timeout_seconds=""
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--apk-file) apk_file="$(next_arg "$@")"; shift 2 ;;
|
|
--package) package_name="$(next_arg "$@")"; shift 2 ;;
|
|
--backend) backend="$(next_arg "$@")"; shift 2 ;;
|
|
--result-root) result_root="$(next_arg "$@")"; shift 2 ;;
|
|
--fixture-root) fixture_root="$(next_arg "$@")"; shift 2 ;;
|
|
--case) case_name="$(next_arg "$@")"; shift 2 ;;
|
|
--trace-archive) trace_archive="$(next_arg "$@")"; shift 2 ;;
|
|
--trace-file) trace_file="$(next_arg "$@")"; shift 2 ;;
|
|
--golden) golden_path="$(next_arg "$@")"; shift 2 ;;
|
|
--alternate-golden)
|
|
if [ "$#" -lt 2 ] || [ "${2#--}" != "$2" ]; then
|
|
alternate_golden_path=""
|
|
shift 1
|
|
else
|
|
alternate_golden_path="$2"
|
|
shift 2
|
|
fi
|
|
;;
|
|
--target-call) target_call="$(next_arg "$@")"; shift 2 ;;
|
|
--width) width="$(next_arg "$@")"; shift 2 ;;
|
|
--height) height="$(next_arg "$@")"; shift 2 ;;
|
|
--ssim-threshold) ssim_threshold="$(next_arg "$@")"; shift 2 ;;
|
|
--crop-x) crop_x="$(next_arg "$@")"; shift 2 ;;
|
|
--crop-y) crop_y="$(next_arg "$@")"; shift 2 ;;
|
|
--crop-width) crop_width="$(next_arg "$@")"; shift 2 ;;
|
|
--crop-height) crop_height="$(next_arg "$@")"; shift 2 ;;
|
|
--use-pbuffer) use_pbuffer=1; shift 1 ;;
|
|
--avoid-angle-llvmpipe-sampler-mipmap-min-filter)
|
|
avoid_angle_llvmpipe_sampler_mipmap_min_filter=1
|
|
shift 1
|
|
;;
|
|
--coherent-as-flush) coherent_as_flush=1; shift 1 ;;
|
|
--timeout-seconds) timeout_seconds="$(next_arg "$@")"; shift 2 ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*) die "unknown argument: $1" ;;
|
|
esac
|
|
done
|
|
|
|
require_value "${apk_file}" "--apk-file"
|
|
require_value "${package_name}" "--package"
|
|
require_value "${backend}" "--backend"
|
|
require_value "${result_root}" "--result-root"
|
|
require_value "${fixture_root}" "--fixture-root"
|
|
require_value "${case_name}" "--case"
|
|
require_value "${trace_archive}" "--trace-archive"
|
|
require_value "${trace_file}" "--trace-file"
|
|
require_value "${golden_path}" "--golden"
|
|
require_value "${target_call}" "--target-call"
|
|
require_value "${width}" "--width"
|
|
require_value "${height}" "--height"
|
|
require_value "${ssim_threshold}" "--ssim-threshold"
|
|
require_value "${crop_x}" "--crop-x"
|
|
require_value "${crop_y}" "--crop-y"
|
|
require_value "${crop_width}" "--crop-width"
|
|
require_value "${crop_height}" "--crop-height"
|
|
require_value "${timeout_seconds}" "--timeout-seconds"
|
|
|
|
test -f "${apk_file}" || die "APK does not exist: ${apk_file}"
|
|
test -f "${trace_archive}" || die "trace archive does not exist: ${trace_archive}"
|
|
test -f "${golden_path}" || die "golden image does not exist: ${golden_path}"
|
|
if [ -n "${alternate_golden_path}" ]; then
|
|
test -f "${alternate_golden_path}" || die "alternate golden image does not exist: ${alternate_golden_path}"
|
|
fi
|
|
safe_case="$(printf '%s' "${case_name}" | sed 's/[^A-Za-z0-9._-]/_/g')"
|
|
app_dir="/data/user/0/${package_name}/files/trace-replay"
|
|
|
|
collect_run_diagnostics() {
|
|
diagnostics_dir="$1"
|
|
mkdir -p "${diagnostics_dir}"
|
|
adb_state="$(adb_device_path get-state 2>/dev/null | tr -d '\r' || true)"
|
|
printf '%s\n' "${adb_state}" > "${diagnostics_dir}/adb-state.txt"
|
|
: > "${diagnostics_dir}/logcat.txt"
|
|
if [ "${adb_state}" != "device" ]; then
|
|
return
|
|
fi
|
|
"${ADB}" logcat -d -t 2000 > "${diagnostics_dir}/logcat.txt" || true
|
|
adb_device_path shell pidof "${package_name}" > "${diagnostics_dir}/pidof.txt" 2>&1 || true
|
|
adb_device_path shell dumpsys activity activities > "${diagnostics_dir}/activity.txt" 2>&1 || true
|
|
adb_device_path shell run-as "${package_name}" ls -laR "${app_dir}" > "${diagnostics_dir}/app-files.txt" 2>&1 || true
|
|
adb_device_path exec-out run-as "${package_name}" cat "${app_dir}/output/retrace.log" > "${diagnostics_dir}/retrace.log" || true
|
|
adb_device_path exec-out run-as "${package_name}" cat "${app_dir}/output/mobilegl.log" > "${diagnostics_dir}/mobilegl.log" || true
|
|
}
|
|
|
|
is_infrastructure_failure() {
|
|
diagnostics_dir="$1"
|
|
adb_state="$(cat "${diagnostics_dir}/adb-state.txt" 2>/dev/null || true)"
|
|
if [ "${adb_state}" != "device" ]; then
|
|
echo "trace-replay-ci.sh: Android device is unavailable (state: ${adb_state:-unknown})" >&2
|
|
return 0
|
|
fi
|
|
if grep -Eq 'Fatal signal [0-9]+.*[(]system_server[)]|F system_server[ :]' "${diagnostics_dir}/logcat.txt"; then
|
|
echo "trace-replay-ci.sh: Android system_server crashed during retrace" >&2
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
copy_app_artifact() {
|
|
source_path="$1"
|
|
destination_path="$2"
|
|
if ! adb_device_path exec-out run-as "${package_name}" cat "${source_path}" > "${destination_path}"; then
|
|
echo "trace-replay-ci.sh: warning: failed to copy ${source_path}" >&2
|
|
rm -f "${destination_path}"
|
|
fi
|
|
}
|
|
|
|
prepare_fixture() {
|
|
fixture_dir="${fixture_root}/${safe_case}"
|
|
rm -rf "${fixture_dir}"
|
|
mkdir -p "${fixture_dir}"
|
|
tar -xzf "${trace_archive}" -C "${fixture_dir}"
|
|
|
|
extracted_trace="${fixture_dir}/${trace_file}"
|
|
test -f "${extracted_trace}" || die "trace file not found in archive: ${trace_file}"
|
|
|
|
adb_device_path push "$(host_path_for_adb "${extracted_trace}")" "/data/local/tmp/mobilegl-${safe_case}.trace"
|
|
adb_device_path push "$(host_path_for_adb "${golden_path}")" "/data/local/tmp/mobilegl-${safe_case}.golden.png"
|
|
if [ -n "${alternate_golden_path}" ]; then
|
|
adb_device_path push "$(host_path_for_adb "${alternate_golden_path}")" "/data/local/tmp/mobilegl-${safe_case}.alternate-golden.png"
|
|
fi
|
|
adb_device_path shell chmod 0644 "/data/local/tmp/mobilegl-${safe_case}.trace" "/data/local/tmp/mobilegl-${safe_case}.golden.png"
|
|
if [ -n "${alternate_golden_path}" ]; then
|
|
adb_device_path shell chmod 0644 "/data/local/tmp/mobilegl-${safe_case}.alternate-golden.png"
|
|
fi
|
|
}
|
|
|
|
copy_fixture_to_app() {
|
|
trace_tmp="/data/local/tmp/mobilegl-${safe_case}.trace"
|
|
golden_tmp="/data/local/tmp/mobilegl-${safe_case}.golden.png"
|
|
alternate_golden_tmp="/data/local/tmp/mobilegl-${safe_case}.alternate-golden.png"
|
|
|
|
adb_device_path shell run-as "${package_name}" rm -rf "${app_dir}"
|
|
adb_device_path shell run-as "${package_name}" mkdir -p "${app_dir}/input" "${app_dir}/output"
|
|
adb_device_path shell run-as "${package_name}" cp "${trace_tmp}" "${app_dir}/input/trace.trace"
|
|
adb_device_path shell run-as "${package_name}" cp "${golden_tmp}" "${app_dir}/input/golden.png"
|
|
if [ -n "${alternate_golden_path}" ]; then
|
|
adb_device_path shell run-as "${package_name}" cp "${alternate_golden_tmp}" "${app_dir}/input/alternate-golden.png"
|
|
fi
|
|
}
|
|
|
|
run_retrace() {
|
|
result_dir="${result_root}/${safe_case}-${backend}"
|
|
alternate_golden_app_path=""
|
|
use_angle=0
|
|
if [ -n "${alternate_golden_path}" ]; then
|
|
alternate_golden_app_path="${app_dir}/input/alternate-golden.png"
|
|
fi
|
|
if [ "${MOBILEGL_USE_ANGLE:-}" = "1" ] && [ "${backend}" = "DirectGLES" ]; then
|
|
use_angle=1
|
|
test -n "${MOBILEGL_TRACE_ANGLE_VARIANT:-}" || die "MOBILEGL_TRACE_ANGLE_VARIANT is required for DirectGLES ANGLE replay"
|
|
fi
|
|
if [ "${MOBILEGL_RETRACE_USE_PBUFFER:-}" = "1" ] && [ "${backend}" = "DirectGLES" ]; then
|
|
use_pbuffer=1
|
|
fi
|
|
|
|
mkdir -p "${result_dir}"
|
|
"${ADB}" install -r "$(host_path_for_adb "${apk_file}")"
|
|
copy_fixture_to_app
|
|
adb_device_path shell am force-stop "${package_name}"
|
|
"${ADB}" logcat -c
|
|
set -- am start -a top.mobilegl.plugin.TRACE_REPLAY \
|
|
-n "${package_name}/top.mobilegl.plugin.trace.TraceReplayActivity" \
|
|
--es trace_path "${app_dir}/input/trace.trace" \
|
|
--es golden_path "${app_dir}/input/golden.png"
|
|
if [ -n "${alternate_golden_app_path}" ]; then
|
|
set -- "$@" --es alternate_golden_path "${alternate_golden_app_path}"
|
|
fi
|
|
if [ "${use_angle}" -eq 1 ]; then
|
|
set -- "$@" --ez use_angle true
|
|
set -- "$@" --es angle_variant "${MOBILEGL_TRACE_ANGLE_VARIANT}"
|
|
fi
|
|
if [ "${use_pbuffer}" -eq 1 ] && [ "${backend}" = "DirectGLES" ]; then
|
|
set -- "$@" --ez use_pbuffer true
|
|
fi
|
|
if [ "${avoid_angle_llvmpipe_sampler_mipmap_min_filter}" -eq 1 ] && [ "${backend}" = "DirectGLES" ]; then
|
|
set -- "$@" --ez avoid_angle_llvmpipe_sampler_mipmap_min_filter true
|
|
fi
|
|
if [ "${coherent_as_flush}" -eq 1 ]; then
|
|
set -- "$@" --ez coherent_as_flush true
|
|
fi
|
|
set -- "$@" \
|
|
--es output_dir "${app_dir}/output" \
|
|
--es diff_path "${app_dir}/output/${safe_case}-diff.png" \
|
|
--es backend "${backend}" \
|
|
--el target_call "${target_call}" \
|
|
--ei width "${width}" \
|
|
--ei height "${height}" \
|
|
--es ssim_threshold "${ssim_threshold}" \
|
|
--ei crop_x "${crop_x}" \
|
|
--ei crop_y "${crop_y}" \
|
|
--ei crop_width "${crop_width}" \
|
|
--ei crop_height "${crop_height}"
|
|
adb_device_path shell "$@"
|
|
|
|
app_exited=0
|
|
saw_app_process=0
|
|
for _ in $(seq 1 "${timeout_seconds}"); do
|
|
if adb_device_path shell run-as "${package_name}" ls "${app_dir}/output/result.json" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
if adb_device_path shell pidof "${package_name}" >/dev/null 2>&1; then
|
|
saw_app_process=1
|
|
elif [ "${saw_app_process}" -eq 1 ]; then
|
|
app_exited=1
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
collect_run_diagnostics "${result_dir}"
|
|
if ! adb_device_path shell run-as "${package_name}" ls "${app_dir}/output/result.json" >/dev/null 2>&1; then
|
|
if [ "${app_exited}" -eq 1 ]; then
|
|
echo "trace-replay-ci.sh: app process exited before result.json was created" >&2
|
|
else
|
|
echo "trace-replay-ci.sh: result.json was not created after ${timeout_seconds}s" >&2
|
|
fi
|
|
if [ -s "${result_dir}/retrace.log" ]; then
|
|
echo "trace-replay-ci.sh: retrace.log:" >&2
|
|
cat "${result_dir}/retrace.log" >&2
|
|
fi
|
|
if [ -s "${result_dir}/mobilegl.log" ]; then
|
|
echo "trace-replay-ci.sh: tail of mobilegl.log:" >&2
|
|
tail -200 "${result_dir}/mobilegl.log" >&2
|
|
fi
|
|
echo "trace-replay-ci.sh: recent relevant logcat lines:" >&2
|
|
grep -E 'MobileGLTraceRunner|AndroidRuntime|FATAL EXCEPTION|trace_replay|MobileGL|libc' "${result_dir}/logcat.txt" | tail -200 >&2 || true
|
|
echo "trace-replay-ci.sh: app trace-replay files:" >&2
|
|
cat "${result_dir}/app-files.txt" >&2 || true
|
|
if is_infrastructure_failure "${result_dir}"; then
|
|
echo "trace-replay-ci.sh: requesting one infrastructure retry" >&2
|
|
exit "${INFRASTRUCTURE_FAILURE_EXIT_CODE}"
|
|
fi
|
|
exit 1
|
|
fi
|
|
adb_device_path exec-out run-as "${package_name}" cat "${app_dir}/output/result.json" > "${result_dir}/result.json"
|
|
cat "${result_dir}/result.json"
|
|
copy_app_artifact "${app_dir}/output/actual.png" "${result_dir}/${safe_case}-${backend}-actual.png"
|
|
copy_app_artifact "${app_dir}/output/${safe_case}-diff.png" "${result_dir}/${safe_case}-${backend}-diff.png"
|
|
copy_app_artifact "${app_dir}/output/retrace.log" "${result_dir}/retrace.log"
|
|
copy_app_artifact "${app_dir}/output/mobilegl.log" "${result_dir}/mobilegl.log"
|
|
|
|
"${PYTHON}" -c 'import json, sys; result = json.load(open(sys.argv[1], encoding="utf-8")); sys.exit(0 if result.get("passed") else f"trace replay failed: {result}")' "${result_dir}/result.json"
|
|
}
|
|
|
|
mkdir -p "${fixture_root}" "${result_root}"
|
|
prepare_fixture
|
|
|
|
run_retrace
|