mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 06:50:29 +02:00
Written while testing the activity lifecycle work on a Galaxy S8 and a Quest 2. Mostly about ways the tooling answers a question you didn't ask. The expensive one: versionCode is the git commit count, so installing a feature branch over a longer branch fails with INSTALL_FAILED_VERSION_DOWNGRADE, which -r does not override. The device keeps running the previous build, and if the install output went through a pipe the failure is never seen. An A/B where both installs failed that way agrees with itself perfectly and means nothing. Also: dumpsys meminfo counts activities that are garbage but not yet collected, so the obvious reading looks like a leak whether or not there is one - trim and re-read until it settles; how to force an activity recreate over adb when the device won't rotate on command; why screencap is black on a Quest and what to read instead; and how to tell whether a device exercises the content-URI path at all before concluding a run covered it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
109 lines
5.5 KiB
Markdown
109 lines
5.5 KiB
Markdown
# Testing an Android build on a device
|
|
|
|
Notes from driving real devices over adb. Most of what follows is not about Android being hard, but
|
|
about ways the tooling quietly answers a question you did not ask - you measure the wrong build, or
|
|
measure garbage that has not been collected yet, and get a confident wrong answer.
|
|
|
|
## Install the build you think you are testing
|
|
|
|
`versionCode` is derived from the git commit count (see `gitVersionCode` in
|
|
`android/build.gradle.kts`), so a feature branch has a *lower* code than a longer branch. Installing
|
|
one over the other fails:
|
|
|
|
adb: failed to install ...: Failure [INSTALL_FAILED_VERSION_DOWNGRADE]
|
|
|
|
`-r` does not permit downgrades; `-d` does. Use `adb install -r -d <apk>`.
|
|
|
|
Do not pipe the install through `tail -1` or `grep Success` and move on. The failure is a single
|
|
line and easy to swallow, and the device then keeps running whatever was there before - every
|
|
measurement afterwards describes the wrong binary, including A/B comparisons where both arms end up
|
|
being the same build and therefore agree with each other.
|
|
|
|
Verify after every install, not just when something looks odd:
|
|
|
|
adb shell dumpsys package org.ppsspp.ppsspp | grep versionName
|
|
|
|
A second, independent check is worth it when a result is surprising: grep the log for a symbol that
|
|
exists only on the branch under test (a renamed class or log tag, say).
|
|
|
|
The version string itself can also lie. The Android build regenerates `git-version.cpp` but does not
|
|
always recompile it, so the APK can carry the previous build's string while containing current code.
|
|
When the version matters:
|
|
|
|
find android/.cxx -name git-version.cpp.o -delete
|
|
|
|
## Forcing an activity recreate without touching the device
|
|
|
|
- `adb shell settings put system font_scale 1.15`, then back. `fontScale` is not in the activity's
|
|
`configChanges`, so each change destroys and recreates the activity. Independent of orientation.
|
|
- `adb shell wm density <n>` also recreates - but only while `density` is absent from
|
|
`configChanges`.
|
|
- `adb shell settings put system user_rotation 1` does **not** rotate PPSSPP. The activity follows
|
|
the sensor, so this needs a physical rotation (or a cooperative device).
|
|
|
|
Put back whatever you changed: `font_scale`, `accelerometer_rotation`, `user_rotation`.
|
|
|
|
`wm density reset` is actively wrong on a device with a resolution override - a Galaxy S8 running
|
|
1080x2220 on a 1440x2960 panel resets to the *physical* panel's density and leaves the UI oversized.
|
|
Read the original with `wm density` first and set it back explicitly.
|
|
|
|
## Measuring retained activities
|
|
|
|
`adb shell dumpsys meminfo <pkg>` prints an Objects block with `Activities` and `AppContexts`.
|
|
|
|
The counts include objects that are garbage but not yet collected. Read it straight after N
|
|
recreates and you will see roughly N whether or not anything leaks, which looks exactly like a leak.
|
|
Force collection instead:
|
|
|
|
adb shell am send-trim-memory org.ppsspp.ppsspp COMPLETE
|
|
|
|
then wait around eight seconds and read. Repeat until two readings agree - three or four rounds is
|
|
normal, and a single trim is not enough.
|
|
|
|
The protocol that gives an answer you can trust: cold start and read the baseline, do N recreates,
|
|
trim until stable, compare. A sound build returns to the baseline; a leaking one sits at baseline+N
|
|
and stays there.
|
|
|
|
Worked example, Galaxy S8 on Android 9, same device and protocol, both builds confirmed by
|
|
`versionName`. Before the activity handover fix: 13 recreates left 13 Activities and 18 AppContexts,
|
|
stable across repeated trims. After: the same 13 recreates settled back to 1 and 6.
|
|
|
|
A JNI global reference is a GC root, so an activity pinned by one shows up here. This is a usable
|
|
check for JNI ownership bugs, not only Java ones.
|
|
|
|
## logcat on a noisy device
|
|
|
|
Filter to the process, since the tag alone is not enough:
|
|
|
|
adb logcat -d --pid=$(adb shell pidof org.ppsspp.ppsspp)
|
|
|
|
On a device with a chatty system - a Quest especially - the ring buffer can rotate the app's lines
|
|
away within seconds, which reads as "logging stopped" rather than "logging was lost". Start a stream
|
|
*before* launching the app instead:
|
|
|
|
adb logcat -c && adb logcat -s PPSSPP:V PpssppActivity:V # in the background, then start the app
|
|
|
|
## VR (Quest)
|
|
|
|
- The VR flavor is a separate package: `./gradlew :android:assembleVrDebug`, installing as
|
|
`org.ppsspp.ppssppvr`. It can sit alongside the normal build.
|
|
- `adb exec-out screencap` returns solid black for an immersive app - it captures the 2D panel
|
|
layer, which such an app does not have. Use the runtime's own line instead: a logcat entry like
|
|
`VrApi: FPS=73/72 ... App=2.67ms` proves frames are being submitted and how much GPU time the app
|
|
spent on them. Zero app time means the render loop is not producing.
|
|
- `am start` makes the activity `topResumedActivity`, but the shell keeps focus, so the app never
|
|
gets a surface and the render loop does not restart. Re-entering an immersive app has to be done
|
|
from inside the headset. A fresh `am start -S` after a `force-stop` does work.
|
|
- `debuggerd -b <tid>` needs root, so no native stacks from a retail headset.
|
|
|
|
## Check which paths the device actually exercises
|
|
|
|
Before concluding that a test covered something:
|
|
|
|
adb shell grep -E '^(MemStickDirectory|GraphicsBackend)' /storage/emulated/0/PSP/SYSTEM/ppsspp.ini
|
|
|
|
A plain filesystem path for the memstick means the SAF/content-URI code
|
|
(`Common/File/AndroidStorage.cpp`, `ContentUri.java`) is never entered, so a run on that device says
|
|
nothing about it - a recents list full of plain paths says the same. The VR flavor reads
|
|
`ppssppvr.ini`, not `ppsspp.ini`.
|