diff --git a/.github/scripts/diagnostic-quiet-build.sh b/.github/scripts/diagnostic-quiet-build.sh new file mode 100644 index 0000000..1268ff8 --- /dev/null +++ b/.github/scripts/diagnostic-quiet-build.sh @@ -0,0 +1,121 @@ +#!/bin/bash +set -euo pipefail + +log_dir=".buildchain/diagnostics" +log_file="${log_dir}/quiet-build.log" +process_context_file="${log_dir}/process-context.log" +mkdir -p "${log_dir}" +: > "${log_file}" +: > "${process_context_file}" + +echo "quiet build stdout/stderr: ${log_file}" +echo "quiet process context: ${process_context_file}" +echo "quiet build started: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + +ps_one() { + pid="$1" + ps -o pid=,ppid=,pgid=,sess=,tty=,stat=,nice=,pri=,user=,command= -p "${pid}" 2>/dev/null \ + || ps -o pid=,ppid=,pgid=,tty=,stat=,nice=,user=,command= -p "${pid}" 2>/dev/null \ + || true +} + +parent_pid() { + pid="$1" + ps -o ppid= -p "${pid}" 2>/dev/null | awk '{print $1}' || true +} + +collect_process_context() { + label="$1" + { + echo + echo "## process context: ${label}" + echo "timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + echo "whoami: $(whoami 2>/dev/null || true)" + echo "pwd: $(pwd)" + echo "uname: $(uname -a 2>/dev/null || true)" + echo "shell: ${SHELL:-}" + echo "bash_pid: ${BASHPID:-}" + echo "shell_pid: $$" + echo "parent_pid: ${PPID:-}" + echo "stdout_is_tty: $(test -t 1 && echo yes || echo no)" + echo "stderr_is_tty: $(test -t 2 && echo yes || echo no)" + echo "github_actions: ${GITHUB_ACTIONS:-}" + echo "github_workflow: ${GITHUB_WORKFLOW:-}" + echo "github_job: ${GITHUB_JOB:-}" + echo "github_action: ${GITHUB_ACTION:-}" + echo "github_ref: ${GITHUB_REF:-}" + echo "github_sha: ${GITHUB_SHA:-}" + echo "runner_name: ${RUNNER_NAME:-}" + echo "runner_os: ${RUNNER_OS:-}" + echo "runner_arch: ${RUNNER_ARCH:-}" + echo "runner_temp: ${RUNNER_TEMP:-}" + echo "runner_workspace: ${RUNNER_WORKSPACE:-}" + echo + echo "### stdio fds" + ls -l /dev/fd/0 /dev/fd/1 /dev/fd/2 2>/dev/null || true + if command -v lsof >/dev/null 2>&1; then + lsof -a -p "$$" -d 0,1,2 2>/dev/null || true + fi + echo + echo "### parent chain" + current="$$" + depth=0 + while [ -n "${current}" ] && [ "${current}" != "0" ] && [ "${depth}" -lt 16 ]; do + ps_one "${current}" + next="$(parent_pid "${current}")" + [ "${next}" = "${current}" ] && break + current="${next}" + depth=$((depth + 1)) + done + echo + echo "### relevant process snapshot" + ps -axo pid=,ppid=,pgid=,sess=,tty=,stat=,nice=,pri=,user=,pcpu=,etime=,command= 2>/dev/null \ + | grep -E 'Runner\\.|actions-runner|diagnostic-quiet-build|buildchain\\.mjs|node-make|make -j|make -C out|corepack pnpm|ccache|clang' \ + | grep -v 'grep -E' \ + || true + } >> "${process_context_file}" 2>&1 +} + +heartbeat() { + while true; do + sleep 60 + echo "quiet build heartbeat: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + collect_process_context "heartbeat" + if command -v ccache >/dev/null 2>&1; then + ccache --show-stats 2>/dev/null | sed -n '1,5p' || true + fi + done +} + +collect_process_context "script-start" + +heartbeat & +heartbeat_pid="$!" +trap 'kill "${heartbeat_pid}" >/dev/null 2>&1 || true' EXIT + +run_quiet() { + name="$1" + shift + { + echo + echo "## ${name}" + echo "start: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + printf '$' + printf ' %q' "$@" + echo + } >> "${log_file}" + echo "quiet build step start: ${name}" + collect_process_context "before-${name}" + "$@" >> "${log_file}" 2>&1 + collect_process_context "after-${name}" + { + echo "end: $(date -u +%Y-%m-%dT%H:%M:%SZ)" + } >> "${log_file}" + echo "quiet build step end: ${name}" +} + +run_quiet "make" corepack pnpm make +run_quiet "build" node .gyp/run-with-env.js KF_SKIP_MAKE_LIBNODE=true -- corepack pnpm build +run_quiet "package" corepack pnpm package + +echo "quiet build finished: $(date -u +%Y-%m-%dT%H:%M:%SZ)" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e28b42e..686c398 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,8 +31,12 @@ jobs: artifact-name-template: '{artifact}-{platform}-{sha}' artifact-paths: | build/stage - release-candidate: ${{ startsWith(github.base_ref, 'alpha/') }} - publish-channel: ${{ startsWith(github.base_ref, 'alpha/') && 'alpha' || 'none' }} + platforms-json: >- + ${{ github.event_name == 'pull_request' && startsWith(github.base_ref, 'alpha/') && github.head_ref == 'feature/macos-alpha-diagnostic' && '[{"id":"macos-arm64","name":"macOS ARM64","runner":"[\"self-hosted\",\"macOS\",\"ARM64\",\"kungfu-build-v4-macos-arm64\"]"}]' || '' }} + release-candidate: ${{ startsWith(github.base_ref, 'alpha/') && github.head_ref != 'feature/macos-alpha-diagnostic' }} + publish-channel: ${{ startsWith(github.base_ref, 'alpha/') && github.head_ref != 'feature/macos-alpha-diagnostic' && 'alpha' || 'none' }} + build-command: >- + ${{ github.event_name == 'pull_request' && startsWith(github.base_ref, 'alpha/') && github.head_ref == 'feature/macos-alpha-diagnostic' && 'bash .github/scripts/diagnostic-quiet-build.sh' || '' }} expected-artifacts-json: >- {"minFiles":1,"minTotalBytes":10000000} artifact-retention-days: 14 diff --git a/.gyp/node-make.js b/.gyp/node-make.js index 19bf8b5..1aa9a97 100644 --- a/.gyp/node-make.js +++ b/.gyp/node-make.js @@ -38,6 +38,12 @@ function runCacheTool(tool, args) { run(tool, args, { check: false }); } +function showDirectorySize(label, dir) { + if (!dir || !fs.existsSync(dir) || process.platform === 'win32' || !commandExists('du')) return; + console.log(`compiler cache ${label} size:`); + runCacheTool('du', ['-sh', dir]); +} + function prepareUnixCompilerCache(mode) { if (mode === 'sccache') { const sccache = commandExists('sccache'); @@ -102,6 +108,16 @@ function showCompilerCacheStats() { } } +function showCompilerCacheDiagnostics(stage) { + console.log(`compiler cache diagnostics (${stage}):`); + for (const name of ['KF_COMPILER_CACHE', 'KF_COMPILER_CACHE_ROOT', 'CCACHE_DIR', 'SCCACHE_DIR', 'CC', 'CXX']) { + console.log(`${name}=${process.env[name] || ''}`); + } + showDirectorySize('ccache dir', process.env.CCACHE_DIR); + showDirectorySize('sccache dir', process.env.SCCACHE_DIR); + showCompilerCacheStats(); +} + function cleanNodeBuildState() { const generatedPaths = ['out', 'Release', 'Debug', 'config.gypi', 'config.mk']; for (const entry of generatedPaths) { @@ -190,18 +206,20 @@ const buildWin = () => { cleanNodeBuildState(); prepareWindowsPythonEnv(); const cacheArgs = prepareCompilerCache(); + showCompilerCacheDiagnostics('before build'); run(path.join('.', 'vcbuild.bat'), ['dll', arch, 'release', 'projgen', 'nobuild', ...cacheArgs], { cwd: nodeSrcDir }); runWinPatch(); run(path.join('.', 'vcbuild.bat'), ['dll', 'noprojgen', ...cacheArgs], { cwd: nodeSrcDir }); - showCompilerCacheStats(); + showCompilerCacheDiagnostics('after build'); }; const buildUnix = () => { cleanNodeBuildState(); prepareCompilerCache(); + showCompilerCacheDiagnostics('before build'); run('sh', [path.join('.', 'configure'), '--shared'], { cwd: nodeSrcDir }); run('make', ['-j', `${buildJobs()}`], { cwd: nodeSrcDir }); - showCompilerCacheStats(); + showCompilerCacheDiagnostics('after build'); }; const build = process.platform === 'win32' ? buildWin : buildUnix;