scripts/ #
Tiny Python tools for driving the xenomorphic GUI under repeatable benchmarks. We use the -e/--execute CLI flag of the binary (it writes commands to the PTY right after spawn) and parse PERF_DEBUG=1 stderr output into structured stats.
scripts/
xenobench/ importable library
__init__.py public surface (find_binary, run_benchmark, ...)
runner.py spawn + parse, never crash on PERF_DEBUG lines
bigfile.py "cat a huge file" — one workload, N runs, CSV
postkey.c post synthesized key events to a macOS app by PID
Install #
Nothing to install. The library is scripts/xenobench; bigfile.py puts scripts/ on sys.path at run time. No third-party deps — stdlib only.
cd ~/Development/xenomorphic
python3 scripts/bigfile.py --help
For ad-hoc python invocation from anywhere:
PYTHONPATH=~/Development/xenomorphic/scripts python3 -c "from xenobench import find_binary; print(find_binary())"
Big-file benchmark #
cd ~/Development/xenomorphic
python3 scripts/bigfile.py --lines 1000000 --warmup 1 --repeat 3 --csv /tmp/result.csv
What it does:
- Generates one deterministic workload (long digit-suffixed lines) under
--workload-dir. - Spawns
./result/bin/xenomorphiconce per run with one-econtainingwc -l; sync; cat <workload>; sync; touch <marker>. - Waits for the touch sentinel, terminates the binary cleanly, parses the PERF_DEBUG log.
- Prints phase-by-phase p50/p95 stats per run on stderr; appends one CSV row per measured run to
--csv. - Logs land under
--log-dirasxeno-perf-<marker>-<unix>.log.
Smoke check (no GUI required) #
python3 -c "
import sys; sys.path.insert(0, 'scripts')
from xenobench import PhaseStats, BenchmarkOutcome, find_binary
print(find_binary())
"
# FileNotFoundError if the binary isn't built yet: run `nix build .#default` first.
For reading an existing PERF_DEBUG log without launching a new run:
python3 -c "
import sys; sys.path.insert(0, 'scripts')
from xenobench import parse_perf_log
phases, gaps, lat, total_bytes, frames, last_dirty = parse_perf_log('/tmp/xeno.perf')
print('bytes=', total_bytes, 'frames=', frames, 'last_dirty=', last_dirty)
for name, s in phases.items(): print(name, s.total_ms, 'ms in', s.count, 'samples')
"
Output dataclass #
BenchmarkOutcome (selected fields):
elapsed_swall time from spawn until marker existstotal_bytessum ofperf-input: bytes=Nover the runphase_statsdict[name -> PhaseStats]with.count, .total_ms, .min_ms, .max_ms, .percentile(p)gap_statsinter-frameperf-gapdeltaslatency_update_to_syncms from QML update -> render thread synchframe_countperf-frame: ...countlast_vt_dirtylastvtDirtyvalue reported by the renderer (2=FULL,1=PARTIAL,0=FALSE)vt_throughput_MBps,fpsderived helpers
Notes / gotchas #
--linesof one million is roughly 128 MiB; the workload is regenerated every run unless--workload-diralready has that path. Reuse is good idea for--repeat > 1.PERF_DEBUG=1is set automatically byrun_benchmarkunlessperf_debug=False.- The app must be built (
nix build .#default). - The GUI window must be focused for fullspeed; headless runs (
QT_QPA_PLATFORM=offscreen) return no frames but the pipeline still feeds the PTY.
postkey — synthesized key events for UI testing #
A small C helper that posts a key-down + key-up pair with modifier flags to a specific macOS process via CGEventPostToPid. This is the only reliable key-injection channel on a locked macOS console: CGEventPost (system-wide) and CGEventPostToHIDEventTap are silently dropped when the loginwindow is frontmost.
Build #
clang -framework CoreFoundation -framework CoreGraphics \
-o scripts/postkey scripts/postkey.c
Usage #
./scripts/postkey <pid> <keycode> <mods>
<pid>— target process (e.g. the xenomorphic app PID fromhub ps)<keycode>— macOS virtual keycode (18=1,19=2,36=Return,123=Left,124=Right,126=Up,125=Down)<mods>— space-separated flags from:cmd shift alt ctrl
Example #
Post Cmd+2 (workspace jump) to a running xenomorphic app:
./scripts/postkey 66195 19 cmd
Pair with the dev-driver TCP bridge (XENO_DEV_SERVER=1, port 45773) to verify the effect: post the key, then query shell.activeWorkspaceIndex over the dev server to confirm the Shortcut fired.