Skip to content

Commit 619246e

Browse files
committed
ci(macrobenchmark): Pull benchmark results via Real Device Access API
saucectl cannot retrieve Macrobenchmark's output. Its artifacts.download.match filters a hardcoded list of assets Sauce hosts for a job -- device.log, junit.xml, video.mp4, network.har, crash.json, screenshots.zip -- and never reads the device filesystem, so benchmarkData.json and the per-iteration perfetto traces stay stranded on the device. The POC's "*-benchmarkData.json" match line could never have matched anything; drop it. Drive the run through the Real Device Access API instead, which exposes adb shell and pullFile: reserve a device, install both APKs, run the instrumentation, then pull the results off. This also recovers the perfetto traces, the only way to resolve sub-millisecond SDK changes that timeToInitialDisplay cannot see. The API is an open-beta add-on, so the workflow probes entitlement before spending a Gradle build on it, and defaults to a one-iteration smoke run.
1 parent 65cf062 commit 619246e

3 files changed

Lines changed: 422 additions & 23 deletions

File tree

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
1-
name: 'Integration Tests - Macrobenchmark (POC)'
2-
# Proof-of-concept: run the sentry-uitest-android-macrobenchmark cold-start benchmark on Sauce
3-
# Labs real devices. Manual trigger only — this is not yet a per-PR gate. The goal is to learn
4-
# (a) whether Macrobenchmark runs on non-rooted cloud devices and which device guards fire, and
5-
# (b) whether timeToInitialDisplay can be retrieved from Sauce artifacts. See the module README.
1+
name: 'Integration Tests - Macrobenchmark'
2+
# Runs the sentry-uitest-android-macrobenchmark cold-start benchmark on a Sauce Labs real
3+
# device via the Real Device Access API, and pulls benchmarkData.json plus the per-iteration
4+
# perfetto traces off the device. saucectl cannot do the pulling: it only downloads assets
5+
# Sauce hosts for a job, never files from the device. See scripts/macrobenchmark-sauce.py.
6+
#
7+
# Manual trigger only -- this reports numbers, it does not gate PRs. Cloud devices have
8+
# unlocked CPU clocks, so run-to-run spread is far wider than most SDK-init changes.
69
on:
710
workflow_dispatch:
11+
inputs:
12+
mode:
13+
description: 'smoke = 1 iteration without AOT compilation (fast, validates the plumbing); full = the benchmark as declared'
14+
type: choice
15+
options:
16+
- smoke
17+
- full
18+
default: smoke
19+
device-name:
20+
description: 'Sauce device id or regex'
21+
required: false
22+
default: 'Google_Pixel_9_Pro_XL_15_real.*'
23+
# Temporary scaffolding: workflow_dispatch cannot target a workflow that does not exist on
24+
# the default branch yet, so trigger on pushes to this branch while we validate the flow.
25+
# Remove before merge.
26+
push:
27+
branches:
28+
- no/macrobenchmark-sauce-results
829

930
concurrency:
1031
group: ${{ github.workflow }}-${{ github.ref }}
@@ -18,43 +39,67 @@ jobs:
1839
# we copy the secret to the env variable in order to access it in the workflow
1940
env:
2041
SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }}
42+
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
2143
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
44+
# Defaults apply to the push trigger too, where `inputs` is empty.
45+
MODE: ${{ inputs.mode || 'smoke' }}
46+
DEVICE_NAME: ${{ inputs.device-name || 'Google_Pixel_9_Pro_XL_15_real.*' }}
2247

2348
steps:
2449
- name: Git checkout
25-
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
50+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
2651
with:
2752
submodules: 'recursive'
2853

54+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
55+
with:
56+
python-version: '3.10.5'
57+
58+
- name: Install Python dependencies
59+
run: |
60+
python3 -m pip install --upgrade pip
61+
python3 -m pip install -r requirements.txt
62+
2963
- name: 'Set up Java: 17'
30-
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5
64+
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5
3165
with:
3266
distribution: 'temurin'
3367
java-version: '17'
3468

3569
- name: Setup Gradle
36-
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
70+
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
3771
with:
3872
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
3973

74+
# Probe first: the Real Device Access API is an open-beta add-on, so a missing
75+
# entitlement should fail in seconds rather than after a full Gradle build.
76+
- name: Check Real Device Access API entitlement
77+
if: env.SAUCE_USERNAME != null
78+
run: python3 scripts/macrobenchmark-sauce.py --probe-only --device-name "$DEVICE_NAME"
79+
4080
- name: Assemble target app and Macrobenchmark apk
4181
if: env.SAUCE_USERNAME != null
4282
run: ./gradlew :sentry-samples:sentry-samples-android:assembleRelease :sentry-android-integration-tests:sentry-uitest-android-macrobenchmark:assembleBenchmark
4383

44-
- name: Run Macrobenchmark in SauceLab
45-
uses: saucelabs/saucectl-run-action@bc81720eb01738d9c664b07fe42621bd0014283f # pin@v3
84+
- name: Run Macrobenchmark on Sauce Labs
4685
if: env.SAUCE_USERNAME != null
47-
env:
48-
GITHUB_TOKEN: ${{ github.token }}
49-
with:
50-
sauce-username: ${{ secrets.SAUCE_USERNAME }}
51-
sauce-access-key: ${{ secrets.SAUCE_ACCESS_KEY }}
52-
config-file: .sauce/sentry-uitest-android-macrobenchmark.yml
86+
run: |
87+
if [ "$MODE" = "smoke" ]; then
88+
mode_args=(--iterations 1 --skip-compilation)
89+
else
90+
mode_args=()
91+
fi
92+
python3 scripts/macrobenchmark-sauce.py \
93+
--app sentry-samples/sentry-samples-android/build/outputs/apk/release/sentry-samples-android-release.apk \
94+
--test-app sentry-android-integration-tests/sentry-uitest-android-macrobenchmark/build/outputs/apk/benchmark/sentry-uitest-android-macrobenchmark-benchmark.apk \
95+
--device-name "$DEVICE_NAME" \
96+
--out-dir ./artifacts/macrobenchmark \
97+
"${mode_args[@]}"
5398
54-
- name: Upload Sauce artifacts
99+
- name: Upload benchmark results
55100
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
56101
if: always()
57102
with:
58-
name: macrobenchmark-artifacts
103+
name: macrobenchmark-results
59104
path: ./artifacts/
60105
if-no-files-found: warn

‎.sauce/sentry-uitest-android-macrobenchmark.yml‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@ suites:
2929
devices:
3030
- id: Google_Pixel_9_Pro_XL_15_real_sjc1 # Google Pixel 9 Pro XL - api 35 (15) - high end
3131

32-
# Grab both the benchmark JSON (if Sauce collects additional test output) and the device log, so
33-
# we can see which path actually yields timeToInitialDisplay. Macrobenchmark also logs guard
34-
# failures (UNLOCKED, DEBUGGABLE, ...) here, which is the expected first-run signal on cloud
35-
# hardware — see the module README.
32+
# The device log carries the actual results: Sauce only returns assets it produces itself, so it
33+
# cannot pull Macrobenchmark's `<pkg>-benchmarkData.json` off the device. SentryStartupBenchmark
34+
# echoes that JSON into logcat instead, and scripts/parse-macrobenchmark-log.py reassembles it.
35+
# The log also holds Macrobenchmark's device guard warnings (unlocked clocks, low battery, ...).
3636
artifacts:
3737
download:
3838
when: always
3939
match:
4040
- junit.xml
4141
- "*.log"
42-
- "*-benchmarkData.json"
4342
directory: ./artifacts/

0 commit comments

Comments
 (0)