Skip to content

Commit e8d78a1

Browse files
committed
chore: add background binary wrapper and standalone adapter-cycle probe to CI integration tests
1 parent ad95720 commit e8d78a1

2 files changed

Lines changed: 107 additions & 5 deletions

File tree

‎.github/workflows/integration_test.yml‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,36 @@ jobs:
107107
ulimit -c unlimited
108108
# Wrap the app binary so its stderr (Rust panic messages — the
109109
# flutter tool discards child stderr) lands in a file we can print.
110+
# `flutter test` reinstalls the bundle at the start of every
111+
# invocation, so a background watcher re-wraps the fresh binary in
112+
# the build→launch gap.
110113
BUNDLE=build/linux/x64/debug/bundle
111-
if [ -f "$BUNDLE/nitro_webgpu_example" ] && [ ! -f "$BUNDLE/nitro_webgpu_example.real" ]; then
112-
mv "$BUNDLE/nitro_webgpu_example" "$BUNDLE/nitro_webgpu_example.real"
113-
cat > "$BUNDLE/nitro_webgpu_example" <<'WRAP'
114+
wrap_binary() {
115+
if [ -f "$BUNDLE/nitro_webgpu_example" ] \
116+
&& ! head -c2 "$BUNDLE/nitro_webgpu_example" | grep -q '#!'; then
117+
mv "$BUNDLE/nitro_webgpu_example" "$BUNDLE/nitro_webgpu_example.real"
118+
cat > "$BUNDLE/nitro_webgpu_example" <<'WRAP'
114119
#!/bin/bash
115120
exec "$(dirname "$0")/nitro_webgpu_example.real" "$@" 2>>/tmp/app_stderr.log
116121
WRAP
117-
chmod +x "$BUNDLE/nitro_webgpu_example"
118-
fi
122+
chmod +x "$BUNDLE/nitro_webgpu_example"
123+
echo "(re)wrapped app binary"
124+
fi
125+
}
126+
wrap_binary
127+
( while true; do wrap_binary; sleep 0.3; done ) &
128+
WATCHER=$!
119129
xvfb-run -a -s "-screen 0 1280x800x24" flutter test integration_test/wgpu_instance_test.dart -d linux \
120130
--dart-define=WGPU_FORCE_FALLBACK=true --reporter expanded || true
131+
kill $WATCHER 2>/dev/null || true
121132
echo "=== app stderr ==="
122133
tail -80 /tmp/app_stderr.log 2>/dev/null || echo "(no stderr captured)"
134+
echo "=== standalone adapter-cycle probe (wgpu panic goes to visible stderr) ==="
135+
cc ../scripts/ci/adapter_cycle_probe.c \
136+
../src/third_party/wgpu_native/linux-x86_64/lib/libwgpu_native.a \
137+
-I../src/third_party/wgpu_native/include -lm -ldl -lpthread \
138+
-o /tmp/adapter_cycle_probe
139+
/tmp/adapter_cycle_probe 2>&1 | tail -8 || echo "PROBE CRASHED (exit $?)"
123140
shopt -s nullglob
124141
cores=(/tmp/core.*)
125142
if [ ${#cores[@]} -eq 0 ]; then echo "NO CORE PRODUCED"; exit 0; fi

‎scripts/ci/adapter_cycle_probe.c‎

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Standalone CI probe: repeated fallback-adapter + device acquire/release
2+
// cycles on one WGPUInstance — the exact pattern the integration suite runs
3+
// before wgpuInstanceRequestAdapter starts panicking on lavapipe (CI Linux,
4+
// core-dump verified). Running it outside Flutter keeps wgpu's panic message
5+
// on a visible stderr.
6+
//
7+
// Build (from the repo root, after fetch_wgpu_native.sh):
8+
// cc scripts/ci/adapter_cycle_probe.c \
9+
// src/third_party/wgpu_native/linux-x86_64/lib/libwgpu_native.a \
10+
// -Isrc/third_party/wgpu_native/include -lm -ldl -lpthread \
11+
// -o /tmp/adapter_cycle_probe
12+
#include <stdio.h>
13+
#include <webgpu/webgpu.h>
14+
15+
static WGPUAdapter g_adapter;
16+
static WGPUDevice g_device;
17+
18+
static void onAdapter(WGPURequestAdapterStatus status, WGPUAdapter adapter,
19+
WGPUStringView message, void* u1, void* u2) {
20+
(void)u1; (void)u2;
21+
if (status != WGPURequestAdapterStatus_Success) {
22+
fprintf(stderr, "adapter error: %.*s\n", (int)message.length,
23+
message.data ? message.data : "");
24+
}
25+
g_adapter = adapter;
26+
}
27+
28+
static void onDevice(WGPURequestDeviceStatus status, WGPUDevice device,
29+
WGPUStringView message, void* u1, void* u2) {
30+
(void)u1; (void)u2;
31+
if (status != WGPURequestDeviceStatus_Success) {
32+
fprintf(stderr, "device error: %.*s\n", (int)message.length,
33+
message.data ? message.data : "");
34+
}
35+
g_device = device;
36+
}
37+
38+
int main(void) {
39+
WGPUInstance instance = wgpuCreateInstance(NULL);
40+
if (!instance) {
41+
fprintf(stderr, "wgpuCreateInstance failed\n");
42+
return 1;
43+
}
44+
for (int i = 0; i < 40; i++) {
45+
g_adapter = NULL;
46+
g_device = NULL;
47+
48+
WGPURequestAdapterOptions opts = WGPU_REQUEST_ADAPTER_OPTIONS_INIT;
49+
opts.forceFallbackAdapter = 1;
50+
WGPURequestAdapterCallbackInfo acb =
51+
WGPU_REQUEST_ADAPTER_CALLBACK_INFO_INIT;
52+
acb.mode = WGPUCallbackMode_AllowProcessEvents;
53+
acb.callback = onAdapter;
54+
wgpuInstanceRequestAdapter(instance, &opts, acb);
55+
for (int t = 0; t < 1000 && !g_adapter; t++) {
56+
wgpuInstanceProcessEvents(instance);
57+
}
58+
if (!g_adapter) {
59+
fprintf(stderr, "iter %d: no adapter\n", i);
60+
return 1;
61+
}
62+
63+
WGPUDeviceDescriptor dd = WGPU_DEVICE_DESCRIPTOR_INIT;
64+
WGPURequestDeviceCallbackInfo dcb =
65+
WGPU_REQUEST_DEVICE_CALLBACK_INFO_INIT;
66+
dcb.mode = WGPUCallbackMode_AllowProcessEvents;
67+
dcb.callback = onDevice;
68+
wgpuAdapterRequestDevice(g_adapter, &dd, dcb);
69+
for (int t = 0; t < 1000 && !g_device; t++) {
70+
wgpuInstanceProcessEvents(instance);
71+
}
72+
if (!g_device) {
73+
fprintf(stderr, "iter %d: no device\n", i);
74+
return 1;
75+
}
76+
77+
WGPUQueue queue = wgpuDeviceGetQueue(g_device);
78+
wgpuQueueRelease(queue);
79+
wgpuDeviceRelease(g_device);
80+
wgpuAdapterRelease(g_adapter);
81+
fprintf(stderr, "iter %d ok\n", i);
82+
}
83+
fprintf(stderr, "PROBE PASSED: 40 adapter+device cycles\n");
84+
return 0;
85+
}

0 commit comments

Comments
 (0)