Add early macOS startup diagnostics (#4504)

This commit is contained in:
Neil
2026-06-02 14:02:19 -07:00
committed by GitHub
parent 7784eaf7d6
commit 8bfab754e0
2 changed files with 499 additions and 1 deletions
+366
View File
@@ -0,0 +1,366 @@
#!/usr/bin/env bash
# Capture one-shot macOS launch diagnostics for a published Orca release.
#
# Usage:
# ORCA_DIAGNOSTIC_TAG=v1.4.42-rc.1 bash config/scripts/macos-launch-diagnostics.sh
# bash config/scripts/macos-launch-diagnostics.sh --tag v1.4.42-rc.1
set -euo pipefail
REPO="${ORCA_DIAGNOSTIC_REPO:-stablyai/orca}"
TAG="${ORCA_DIAGNOSTIC_TAG:-}"
KEEP=0
while [[ $# -gt 0 ]]; do
case "$1" in
--tag)
TAG="${2:-}"
shift 2
;;
--repo)
REPO="${2:-}"
shift 2
;;
--keep)
KEEP=1
shift
;;
-h|--help)
cat <<'EOF'
Capture one-shot macOS launch diagnostics for a published Orca release.
Usage:
ORCA_DIAGNOSTIC_TAG=v1.4.42-rc.1 bash config/scripts/macos-launch-diagnostics.sh
bash config/scripts/macos-launch-diagnostics.sh --tag v1.4.42-rc.1
EOF
exit 0
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: $0 --tag vX.Y.Z-rc.N [--repo owner/repo] [--keep]" >&2
exit 2
;;
esac
done
if [[ "$(uname -s)" != "Darwin" ]]; then
echo "This diagnostic script only supports macOS." >&2
exit 2
fi
if [[ -z "$TAG" ]]; then
echo "Set ORCA_DIAGNOSTIC_TAG or pass --tag vX.Y.Z-rc.N." >&2
exit 2
fi
ARCH="$(uname -m)"
case "$ARCH" in
arm64) ASSET="orca-macos-arm64.dmg" ;;
x86_64) ASSET="orca-macos-x64.dmg" ;;
*)
echo "Unsupported macOS architecture: $ARCH" >&2
exit 2
;;
esac
TIMESTAMP="$(date -u '+%Y%m%dT%H%M%SZ')"
WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/orca-launch-diagnostics.XXXXXXXX")"
OUT_DIR="$WORK_DIR/output"
MOUNT_DIR="$WORK_DIR/mount"
APP_DIR="$WORK_DIR/Orca.app"
DMG_PATH="$WORK_DIR/$ASSET"
mkdir -p "$OUT_DIR" "$MOUNT_DIR"
if [[ -d "$HOME/Desktop" ]]; then
ZIP_PATH="$HOME/Desktop/orca-launch-diagnostics-${TAG}-${TIMESTAMP}.zip"
else
ZIP_PATH="$PWD/orca-launch-diagnostics-${TAG}-${TIMESTAMP}.zip"
fi
log() {
printf '[orca-diagnostics] %s\n' "$*"
}
run_capture() {
local name="$1"
shift
{
echo "\$ $*"
"$@"
} >"$OUT_DIR/$name.out" 2>"$OUT_DIR/$name.err" || {
local code=$?
echo "exit=$code" >>"$OUT_DIR/$name.err"
return 0
}
}
cleanup() {
if mount | grep -F " on $MOUNT_DIR " >/dev/null 2>&1; then
hdiutil detach "$MOUNT_DIR" -quiet >/dev/null 2>&1 || true
fi
if [[ "$KEEP" -eq 0 ]]; then
rm -rf "$WORK_DIR"
else
log "kept working directory: $WORK_DIR"
fi
}
trap cleanup EXIT
write_environment_report() {
{
echo "tag=$TAG"
echo "repo=$REPO"
echo "asset=$ASSET"
echo "timestamp_utc=$TIMESTAMP"
echo
echo "## sw_vers"
sw_vers || true
echo
echo "## uname"
uname -a || true
echo
echo "## shell"
echo "$SHELL"
echo
echo "## current Orca processes before diagnostics"
pgrep -fl 'Orca|orca' || true
} >"$OUT_DIR/environment.txt"
}
ensure_no_existing_orca() {
local existing
existing="$(pgrep -x Orca || true)"
if [[ -z "$existing" ]]; then
return 0
fi
{
echo "An Orca process is already running. Close Orca and run this script again."
echo
ps -p "$(printf '%s' "$existing" | paste -sd, -)" -o pid=,ppid=,command= || true
} | tee "$OUT_DIR/existing-orca-process.txt" >&2
exit 2
}
download_and_copy_app() {
local url="https://github.com/$REPO/releases/download/$TAG/$ASSET"
log "downloading $url"
curl -fL --retry 3 --retry-delay 2 -o "$DMG_PATH" "$url"
shasum -a 256 "$DMG_PATH" >"$OUT_DIR/dmg.sha256" || true
log "mounting DMG"
hdiutil attach "$DMG_PATH" -nobrowse -readonly -mountpoint "$MOUNT_DIR" >"$OUT_DIR/hdiutil-attach.txt"
local source_app
source_app="$(find "$MOUNT_DIR" -maxdepth 1 -name '*.app' -type d | head -n 1)"
if [[ -z "$source_app" ]]; then
echo "No .app bundle found in $DMG_PATH" >&2
exit 1
fi
log "copying app to isolated temp path"
ditto "$source_app" "$APP_DIR"
hdiutil detach "$MOUNT_DIR" -quiet
}
write_app_report() {
{
echo "app=$APP_DIR"
echo
echo "## Info.plist"
plutil -p "$APP_DIR/Contents/Info.plist" || true
echo
echo "## spctl"
spctl -a -vvv "$APP_DIR" || true
echo
echo "## codesign display"
codesign -dv --verbose=4 "$APP_DIR" || true
echo
echo "## codesign verify"
codesign --verify --strict --deep "$APP_DIR" || true
echo
echo "## xattr"
xattr -lr "$APP_DIR" || true
echo
echo "## native modules"
find "$APP_DIR/Contents/Resources" -name '*.node' -print || true
} >"$OUT_DIR/app-report.txt" 2>&1
}
start_log_stream() {
local file="$1"
local predicate='process == "Orca" OR eventMessage CONTAINS[c] "Orca" OR eventMessage CONTAINS[c] "com.stablyai.orca"'
if command -v log >/dev/null 2>&1; then
log stream --style compact --predicate "$predicate" >"$file" 2>&1 &
echo "$!"
else
echo ""
fi
}
stop_log_stream() {
local pid="$1"
if [[ -n "$pid" ]] && kill -0 "$pid" >/dev/null 2>&1; then
kill "$pid" >/dev/null 2>&1 || true
wait "$pid" >/dev/null 2>&1 || true
fi
}
latest_orca_pid_for_app() {
pgrep -nf "$APP_DIR/Contents/MacOS/Orca" || true
}
sample_process_once() {
local pid="$1"
local file="$2"
if [[ -n "$pid" ]] && command -v sample >/dev/null 2>&1; then
sample "$pid" 1 -file "$file" >"$file.command.out" 2>"$file.command.err" || true
fi
}
terminate_app_processes() {
local pid
pid="$(latest_orca_pid_for_app)"
if [[ -n "$pid" ]]; then
kill "$pid" >/dev/null 2>&1 || true
sleep 1
if kill -0 "$pid" >/dev/null 2>&1; then
kill -9 "$pid" >/dev/null 2>&1 || true
fi
fi
}
wait_for_probe() {
local runner_pid="$1"
local label="$2"
local sample_file="$OUT_DIR/$label.sample.txt"
local sampled=0
local i
for i in $(seq 1 200); do
local app_pid
app_pid="$(latest_orca_pid_for_app)"
if [[ "$sampled" -eq 0 && -n "$app_pid" ]]; then
sampled=1
echo "sampled_pid=$app_pid" >>"$OUT_DIR/$label.meta"
sample_process_once "$app_pid" "$sample_file"
fi
if ! kill -0 "$runner_pid" >/dev/null 2>&1; then
set +e
wait "$runner_pid"
local runner_exit=$?
set -e
echo "runner_exit=$runner_exit" >>"$OUT_DIR/$label.meta"
return 0
fi
sleep 0.1
done
echo "runner_timeout_after_seconds=20" >>"$OUT_DIR/$label.meta"
terminate_app_processes
wait "$runner_pid" >/dev/null 2>&1 || true
}
run_launchservices_probe() {
local label="$1"
local extra_env_name="${2:-}"
local extra_env_value="${3:-}"
local stderr_file="$OUT_DIR/$label.stderr.log"
local stdout_file="$OUT_DIR/$label.stdout.log"
local bootstrap_file="$OUT_DIR/$label.bootstrap.log"
local stream_file="$OUT_DIR/$label.system-stream.log"
local stream_pid
log "running LaunchServices probe: $label"
stream_pid="$(start_log_stream "$stream_file")"
{
echo "label=$label"
echo "started_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "bootstrap_file=$bootstrap_file"
[[ -n "$extra_env_name" ]] && echo "$extra_env_name=$extra_env_value"
} >"$OUT_DIR/$label.meta"
if [[ -n "$extra_env_name" ]]; then
open -n -W \
--stdout "$stdout_file" \
--stderr "$stderr_file" \
--env ELECTRON_ENABLE_LOGGING=1 \
--env ORCA_STARTUP_DIAGNOSTICS=trace \
--env ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT=30000 \
--env ORCA_STARTUP_DIAGNOSTICS_FILE="$bootstrap_file" \
--env "$extra_env_name=$extra_env_value" \
"$APP_DIR" &
else
open -n -W \
--stdout "$stdout_file" \
--stderr "$stderr_file" \
--env ELECTRON_ENABLE_LOGGING=1 \
--env ORCA_STARTUP_DIAGNOSTICS=trace \
--env ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT=30000 \
--env ORCA_STARTUP_DIAGNOSTICS_FILE="$bootstrap_file" \
"$APP_DIR" &
fi
local runner_pid="$!"
wait_for_probe "$runner_pid" "$label"
echo "ended_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >>"$OUT_DIR/$label.meta"
stop_log_stream "$stream_pid"
}
run_direct_exec_probe() {
local label="direct-exec"
local stderr_file="$OUT_DIR/$label.stderr.log"
local stdout_file="$OUT_DIR/$label.stdout.log"
local bootstrap_file="$OUT_DIR/$label.bootstrap.log"
local stream_file="$OUT_DIR/$label.system-stream.log"
local stream_pid
log "running direct exec probe"
stream_pid="$(start_log_stream "$stream_file")"
{
echo "label=$label"
echo "started_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "bootstrap_file=$bootstrap_file"
} >"$OUT_DIR/$label.meta"
ELECTRON_ENABLE_LOGGING=1 \
ORCA_STARTUP_DIAGNOSTICS=trace \
ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT=30000 \
ORCA_STARTUP_DIAGNOSTICS_FILE="$bootstrap_file" \
"$APP_DIR/Contents/MacOS/Orca" >"$stdout_file" 2>"$stderr_file" &
local runner_pid="$!"
wait_for_probe "$runner_pid" "$label"
echo "ended_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >>"$OUT_DIR/$label.meta"
stop_log_stream "$stream_pid"
}
write_system_log_snapshot() {
local predicate='process == "Orca" OR eventMessage CONTAINS[c] "Orca" OR eventMessage CONTAINS[c] "com.stablyai.orca"'
if command -v log >/dev/null 2>&1; then
log "capturing recent unified log snapshot"
log show --style syslog --last 10m --predicate "$predicate" >"$OUT_DIR/system-log-last-10m.log" 2>&1 || true
fi
}
package_results() {
{
echo "Diagnostics completed."
echo "Tag: $TAG"
echo "Output zip: $ZIP_PATH"
echo
echo "Please attach this zip to the GitHub issue:"
echo " $ZIP_PATH"
} >"$OUT_DIR/README.txt"
log "creating zip"
(cd "$OUT_DIR" && zip -qry "$ZIP_PATH" .)
log "wrote $ZIP_PATH"
}
write_environment_report
ensure_no_existing_orca
download_and_copy_app
write_app_report
run_launchservices_probe "launchservices-trace"
run_launchservices_probe "launchservices-bypass-lock" "ORCA_BYPASS_SINGLE_INSTANCE_LOCK" "1"
run_direct_exec_probe
write_system_log_snapshot
package_results
+133 -1
View File
@@ -33,6 +33,137 @@ const ORCA_DIAGNOSTICS_TOKEN_URL_LITERAL =
? JSON.stringify(orcaDiagnosticsTokenUrl)
: 'null'
function createStartupDiagnosticsBanner(chunkName: string): string {
return `
;(() => {
const env = typeof process !== 'undefined' ? process.env : undefined
const mode = env?.ORCA_STARTUP_DIAGNOSTICS
if (mode !== '1' && mode !== 'trace') {
return
}
const safeJson = (value) => {
try {
return JSON.stringify(value)
} catch {
return '"<unserializable>"'
}
}
let closeSync
let diagnosticFileDescriptor
let openSync
let writeSync
try {
const fs = require('node:fs')
closeSync = fs.closeSync
openSync = fs.openSync
writeSync = fs.writeSync
} catch {
closeSync = undefined
openSync = undefined
writeSync = undefined
}
const diagnosticFile = env?.ORCA_STARTUP_DIAGNOSTICS_FILE
if (typeof diagnosticFile === 'string' && diagnosticFile.length > 0 && typeof openSync === 'function') {
try {
diagnosticFileDescriptor = openSync(diagnosticFile, 'a', 0o600)
} catch {
diagnosticFileDescriptor = undefined
}
}
const writeLine = (message) => {
try {
const line = message.endsWith('\\n') ? message : message + '\\n'
if (typeof writeSync === 'function') {
writeSync(2, line)
if (typeof diagnosticFileDescriptor === 'number') {
writeSync(diagnosticFileDescriptor, line)
}
}
} catch {
// Diagnostics must never affect startup.
}
}
const chunkName = ${JSON.stringify(chunkName)}
writeLine('[bootstrap] bundle-enter chunk=' + safeJson(chunkName) + ' pid=' + process.pid + ' ppid=' + process.ppid + ' execPath=' + safeJson(process.execPath) + ' argv=' + safeJson(process.argv) + ' electronRunAsNode=' + safeJson(env?.ELECTRON_RUN_AS_NODE ?? null))
if (!globalThis.__ORCA_BOOTSTRAP_EXIT_LOG_INSTALLED__) {
globalThis.__ORCA_BOOTSTRAP_EXIT_LOG_INSTALLED__ = true
process.once('exit', (code) => {
writeLine('[bootstrap] process-exit code=' + code)
if (typeof closeSync === 'function' && typeof diagnosticFileDescriptor === 'number') {
try {
closeSync(diagnosticFileDescriptor)
} catch {
// Diagnostics must never affect shutdown.
}
}
})
process.on('uncaughtExceptionMonitor', (error, origin) => {
const message = error && typeof error === 'object' && 'stack' in error ? error.stack : error
writeLine('[bootstrap] uncaught-exception origin=' + safeJson(origin) + ' error=' + safeJson(String(message)))
})
process.on('unhandledRejection', (reason) => {
const message = reason && typeof reason === 'object' && 'stack' in reason ? reason.stack : reason
writeLine('[bootstrap] unhandled-rejection error=' + safeJson(String(message)))
})
}
if (mode === 'trace' && !globalThis.__ORCA_BOOTSTRAP_REQUIRE_TRACE_INSTALLED__) {
globalThis.__ORCA_BOOTSTRAP_REQUIRE_TRACE_INSTALLED__ = true
try {
const Module = require('node:module')
const originalLoad = Module._load
const parsedTraceLimit = Number(env?.ORCA_STARTUP_DIAGNOSTICS_TRACE_LIMIT ?? 20000)
const traceLimit = Number.isFinite(parsedTraceLimit) && parsedTraceLimit > 0 ? parsedTraceLimit : 20000
let traceLineCount = 0
let traceLimitReported = false
const writeTraceLine = (message) => {
if (traceLineCount >= traceLimit) {
if (!traceLimitReported) {
traceLimitReported = true
writeLine('[bootstrap] require-trace-limit-reached limit=' + safeJson(traceLimit))
}
return
}
traceLineCount += 1
writeLine(message)
}
Module._load = function (request, parent, isMain) {
const parentName = parent && parent.filename ? parent.filename : null
writeTraceLine('[bootstrap] require-start request=' + safeJson(request) + ' parent=' + safeJson(parentName) + ' isMain=' + safeJson(Boolean(isMain)))
try {
const result = Reflect.apply(originalLoad, this, arguments)
writeTraceLine('[bootstrap] require-ok request=' + safeJson(request))
return result
} catch (error) {
const message = error && typeof error === 'object' && 'stack' in error ? error.stack : error
writeTraceLine('[bootstrap] require-error request=' + safeJson(request) + ' error=' + safeJson(String(message)))
throw error
}
}
} catch (error) {
writeLine('[bootstrap] require-trace-install-error error=' + safeJson(String(error)))
}
}
})();
`
}
function createStartupDiagnosticsBootstrapPlugin() {
return {
name: 'orca-startup-diagnostics-bootstrap',
generateBundle(_options, bundle) {
const mainChunk = bundle['index.js']
if (!mainChunk || mainChunk.type !== 'chunk') {
return
}
// Why: source-level startup diagnostics run after Rollup's generated
// prelude and require() list. Mutate the final emitted chunk so macOS
// launch failures can identify the earliest JS boundary reached.
mainChunk.code = createStartupDiagnosticsBanner(mainChunk.fileName) + mainChunk.code
}
}
}
export default defineConfig({
main: {
build: {
@@ -54,7 +185,8 @@ export default defineConfig({
'agent-hooks/managed-agent-hook-controls': resolve(
'src/main/agent-hooks/managed-agent-hook-controls.ts'
)
}
},
plugins: [createStartupDiagnosticsBootstrapPlugin()]
}
},
// Why: compile-time substitution for the telemetry gate. See the block