kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 306b2919c7c7aea6a5641295b79bb3a9df6b16ba
parent f6b905f93ad5d469c4983625fbe7f28aaa64b906
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 17:39:44 -0700

Speed up tier1 timing diagnostics

Diffstat:
Mmk/test.mk | 2+-
Mtest/driver/run.sh | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/tier1/cross/run.sh | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mtest/tier1/run_logged.sh | 55++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mtest/tier1/system/debug-dwarf.sh | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mtest/tier1/system/dist.sh | 55++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mtest/tier1/system/runtime-headers.sh | 18++++++++++++------
Mtest/tier1/system/tools.sh | 55++++++++++++++++++++++++++++++++++++++++++++++++++-----
Atest/tier1/timing.sh | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 425 insertions(+), 56 deletions(-)

diff --git a/mk/test.mk b/mk/test.mk @@ -281,7 +281,7 @@ test-t1-link-jit: bin test-t1-cross: bin @$(T1_LOG) cross env KIT=$(abspath $(BIN)) test/tier1/cross/run.sh -test-t1-rt: +test-t1-rt: bin @$(T1_LOG) rt test/tier1/system/runtime-headers.sh test-t1-debug: diff --git a/test/driver/run.sh b/test/driver/run.sh @@ -26,6 +26,41 @@ KIT_KIT_DIR="$repo_root/test/lib" . "$repo_root/test/lib/kit_sh_kit.sh" kit_report_init +driver_now_ms() { + perl -MTime::HiRes=time -e 'printf "%.0f\n", time() * 1000' 2>/dev/null || + printf '%s000\n' "$(date +%s 2>/dev/null || printf '0')" +} + +driver_section_id= +driver_section_start=0 + +driver_slow_timing_enabled() { + case "${KIT_TIER1_SLOW_TIMING:-0}" in + 1|yes|true|on) return 0 ;; + *) return 1 ;; + esac +} + +driver_section_done() { + [ -n "$driver_section_id" ] || return 0 + if ! driver_slow_timing_enabled; then + driver_section_id= + return 0 + fi + driver_section_end=$(driver_now_ms) + driver_section_ms=$((driver_section_end - driver_section_start)) + [ "$driver_section_ms" -lt 0 ] && driver_section_ms=0 + kit_time "$driver_section_id" "$driver_section_ms" + driver_section_id= +} + +driver_section() { + driver_slow_timing_enabled || return 0 + driver_section_done + driver_section_id=$1 + driver_section_start=$(driver_now_ms) +} + cat > "$work/main.c" <<'SRC' int main(void) { return 0; } int _start(void) { return 0; } @@ -35,6 +70,8 @@ cat > "$work/other.c" <<'SRC' int other(void) { return 0; } SRC +driver_section cc_ld_modes + # ---- executable permission bits (cc/ld) ---- if (umask 077; "$KIT" cc "$work/main.c" -o "$work/cc-exe") \ > "$work/cc.out" 2> "$work/cc.err"; then @@ -291,6 +328,8 @@ else fi # ---- runtime auto-build + link via nm (aarch64) ---- +driver_section runtime_link + mkdir -p "$work/rt-support/rt" cp -R "$repo_root/rt/include" "$work/rt-support/rt/include" cp -R "$repo_root/rt/lib" "$work/rt-support/rt/lib" @@ -441,6 +480,8 @@ else fi # ---- ld PIE without dynamic deps has no INTERP/DYNAMIC program headers ---- +driver_section pie_macho_darwin + cat > "$work/ld-static-pie.c" <<'SRC' void _start(void) { for (;;) {} } SRC @@ -637,6 +678,8 @@ SRC fi # ---- run: JIT compile a source + archive on demand, exit status is the result ---- +driver_section run_hosted + cat > "$work/run-main.c" <<'SRC' int add42(int); int main(void) { return add42(0); } @@ -875,6 +918,8 @@ else fi # ---- rv64 cross-target end-to-end (as, cc, ld, objdump) ---- +driver_section rv64_check_tools + # Exercises the rv64 lane of each tool the toolchain claims to support. # Cross-compile-only; no qemu/native exec required. cat > "$work/rv64-asm.S" <<'SRC' @@ -1222,6 +1267,8 @@ else fi # ---- install: symlink the kit tools into a dir ---- +driver_section install_backtrace_autovar + # Default set: toolchain + standard-named byte utils, each link -> the binary. inst_dir="$work/inst" run_ok "install-default" "$KIT" install "$inst_dir" @@ -1373,5 +1420,6 @@ else "host cannot natively run kit cc output" fi +driver_section_done kit_summary driver-cc kit_exit diff --git a/test/tier1/cross/run.sh b/test/tier1/cross/run.sh @@ -8,10 +8,12 @@ CASES="$ROOT/test/cross/cases" KITCHEN="$ROOT/test/tier1/kitchen/cross" WORK="$ROOT/build/test/tier1/cross" SELECTOR="${T1_CROSS_TARGETS:-all}" +PARALLEL_DIR="$WORK/.parallel" -mkdir -p "$WORK" +mkdir -p "$WORK" "$PARALLEL_DIR" . "$ROOT/test/lib/kit_sh_report.sh" +. "$ROOT/test/tier1/timing.sh" kit_report_init KIT_SKIP_IS_FAILURE=1 @@ -60,10 +62,24 @@ mode_flag() { run_cmd() { local label="$1" log="$2" shift 2 + local start_ms dur_ms + start_ms=$(t1_now_ms) if "$@" >"$log" 2>&1; then + if t1_slow_timing_enabled; then + dur_ms=$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)") + printf ' ok: %s (%sms)\n' "$label" "$dur_ms" + else + printf ' ok: %s\n' "$label" + fi return 0 fi kit_fail "$label" "see $log" + if t1_slow_timing_enabled; then + dur_ms=$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)") + printf ' failed: %s (%sms)\n' "$label" "$dur_ms" + else + printf ' failed: %s\n' "$label" + fi return 1 } @@ -251,29 +267,70 @@ freestanding_lane() { if [ -s "$exe" ]; then kit_pass "$token"; else kit_fail "$token" "linked artifact missing"; fi } +enqueue_lane() { + local label="$1" id out + shift + id=$(t1_step_id "$label") + out="$PARALLEL_DIR/$id.out" + printf '%s\t%s\n' "$label" "$out" >> "$PARALLEL_DIR/order" + t1_parallel_wait_for_slot "$T1_CROSS_JOBS" + "$@" > "$out" 2>&1 & +} + +replay_and_exit() { + local combined="$PARALLEL_DIR/combined.out" pass fail skip failures skips + : > "$combined" + while IFS="$(printf '\t')" read -r label out; do + [ -n "$label" ] || continue + cat "$out" + cat "$out" >> "$combined" + done < "$PARALLEL_DIR/order" + + pass=$(awk '/^[[:space:]]+PASS[[:space:]]/{n++} END{print n+0}' "$combined") + fail=$(awk '/^[[:space:]]+FAIL[[:space:]]/{n++} END{print n+0}' "$combined") + skip=$(awk '/^[[:space:]]+SKIP[[:space:]]/{n++} END{print n+0}' "$combined") + failures=$(awk '/^[[:space:]]+FAIL[[:space:]]/{printf " %s", $2}' "$combined") + skips=$(awk '/^[[:space:]]+SKIP[[:space:]]/{printf " %s", $2}' "$combined") + + [ -n "$failures" ] && printf '\ntest-t1-cross: failures:%s\n' "$failures" + [ -n "$skips" ] && printf 'test-t1-cross: skipped:%s\n' "$skips" + printf '\ntest-t1-cross: %d pass, %d fail, %d skip\n' "$pass" "$fail" "$skip" + + [ "$fail" -gt 0 ] && exit 1 + if [ "${KIT_SKIP_IS_FAILURE:-0}" = "1" ] && [ "$skip" -gt 0 ] && + [ "${KIT_TEST_ALLOW_SKIP:-0}" != "1" ]; then + exit 1 + fi + exit 0 +} + +rm -f "$PARALLEL_DIR/order" +: > "$PARALLEL_DIR/order" +T1_CROSS_JOBS=$(t1_parallel_jobs) || exit $? + for arch in aa64 x64 rv64; do - hosted_lane "linux-glibc-$arch" default - linux_shared_lane "linux-glibc-$arch" - hosted_lane "linux-musl-$arch" static - hosted_lane "linux-musl-$arch" dynamic - linux_shared_lane "linux-musl-$arch" - hosted_lane "freebsd-$arch" static + enqueue_lane "linux-glibc-$arch" hosted_lane "linux-glibc-$arch" default + enqueue_lane "linux-glibc-$arch:shared" linux_shared_lane "linux-glibc-$arch" + enqueue_lane "linux-musl-$arch:static" hosted_lane "linux-musl-$arch" static + enqueue_lane "linux-musl-$arch:dynamic" hosted_lane "linux-musl-$arch" dynamic + enqueue_lane "linux-musl-$arch:shared" linux_shared_lane "linux-musl-$arch" + enqueue_lane "freebsd-$arch:static" hosted_lane "freebsd-$arch" static done for arch in aa64 x64; do - hosted_lane "windows-$arch" default - hosted_lane "macos-$arch" default + enqueue_lane "windows-$arch" hosted_lane "windows-$arch" default + enqueue_lane "macos-$arch" hosted_lane "macos-$arch" default done -android_lane +enqueue_lane android-aa64 android_lane -apple_lane ios-aa64 arm64-apple-ios iphoneos -apple_lane ios-sim-aa64 arm64-apple-ios-simulator iphonesimulator -apple_lane ios-sim-x64 x86_64-apple-ios-simulator iphonesimulator +enqueue_lane ios-aa64 apple_lane ios-aa64 arm64-apple-ios iphoneos +enqueue_lane ios-sim-aa64 apple_lane ios-sim-aa64 arm64-apple-ios-simulator iphonesimulator +enqueue_lane ios-sim-x64 apple_lane ios-sim-x64 x86_64-apple-ios-simulator iphonesimulator for arch in aa64 x64 rv64 rv32 arm32; do - freestanding_lane "$arch" + enqueue_lane "freestanding-$arch" freestanding_lane "$arch" done -kit_summary test-t1-cross -kit_exit +wait || true +replay_and_exit diff --git a/test/tier1/run_logged.sh b/test/tier1/run_logged.sh @@ -14,17 +14,66 @@ log_dir="$root/build/test/tier1/logs" log="$log_dir/$label.log" mkdir -p "$log_dir" +. "$root/test/tier1/timing.sh" + +print_child_timings() { + max=${T1_TIMING_LINES:-8} + timings=$( + awk ' + /^tier1\/[^:]+: / { + cur = $0 + sub(/^tier1\/[^:]+: /, "", cur) + next + } + /^[[:space:]]+ok: / { + if (match($0, /\(([0-9]+)ms\)/)) { + ms = substr($0, RSTART + 1, RLENGTH - 4) + name = cur + if ($0 ~ /^[[:space:]]+ok: [^\/]/) { + name = $0 + sub(/^[[:space:]]+ok: /, "", name) + sub(/[[:space:]]+\([0-9]+ms\).*$/, "", name) + } + if (name != "") printf "%s\t%s\n", ms, name + } + } + /^[^:]+: time / { + for (i = 3; i <= NF; ++i) { + split($i, kv, "=") + if (kv[1] != "" && kv[2] ~ /^[0-9]+ms$/) { + sub(/ms$/, "", kv[2]) + printf "%s\t%s\n", kv[2], kv[1] + } + } + } + ' "$log" | sort -nr | head -n "$max" + ) + [ -n "$timings" ] || return 0 + printf ' slow steps: ' + printf '%s\n' "$timings" | + awk -F '\t' '{ printf "%s%s=%sms", sep, $2, $1; sep=", " } END { printf "\n" }' +} + +start_ms=$(t1_now_ms) "$@" >"$log" 2>&1 rc=$? +dur_ms=$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)") + if [ "$rc" -ne 0 ]; then - printf 'T1 %s failed; tail of %s:\n' "$label" "$log" >&2 + printf 'T1 %s failed after %sms; tail of %s:\n' "$label" "$dur_ms" "$log" >&2 tail -100 "$log" >&2 || true exit "$rc" fi skips="$(sed -n 's/^[^:][^:]*: skipped: //p' "$log" | tr '\n' ' ' | sed -e 's/[[:space:]][[:space:]]*/ /g' -e 's/^ //' -e 's/ $//')" if [ -n "$skips" ]; then nskip="$(printf '%s\n' "$skips" | wc -w | tr -d '[:space:]')" - printf 'T1 %-18s OK (%s skip: %s)\n' "$label" "$nskip" "$skips" + printf 'T1 %-18s OK %sms (%s skip: %s)\n' "$label" "$dur_ms" "$nskip" "$skips" + if t1_slow_timing_enabled; then + print_child_timings + fi exit 0 fi -printf 'T1 %-18s OK\n' "$label" +printf 'T1 %-18s OK %sms\n' "$label" "$dur_ms" +if t1_slow_timing_enabled; then + print_child_timings +fi diff --git a/test/tier1/system/debug-dwarf.sh b/test/tier1/system/debug-dwarf.sh @@ -9,18 +9,30 @@ manifest="$repo_root/test/tier1/manifests/system/debug-dwarf.txt" log_dir="${KIT_TIER1_LOG_DIR:-$repo_root/build/test/tier1/logs/system/debug-dwarf}" mkdir -p "$log_dir" +. "$script_dir/../timing.sh" run_step() { name=$1 shift log="$log_dir/$name.log" + start_ms=$(t1_now_ms) printf 'tier1/debug-dwarf: %s\n' "$name" - if "$@" > "$log" 2>&1; then - printf ' ok: %s\n' "$log" + "$@" > "$log" 2>&1 + rc=$? + if [ "$rc" -eq 0 ]; then + if t1_slow_timing_enabled; then + printf ' ok: %s (%sms)\n' "$log" "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" + else + printf ' ok: %s\n' "$log" + fi return 0 fi - rc=$? - printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + if t1_slow_timing_enabled; then + printf ' FAIL: %s (exit %s, %sms)\n' "$log" "$rc" \ + "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" >&2 + else + printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + fi tail -n 80 "$log" >&2 return "$rc" } @@ -31,6 +43,7 @@ run_objdump_dwarf() { actual="$log_dir/$name.actual" expected="$repo_root/test/objdump/dwarf/cases/01-dwarf-all.expected" case_sh="$repo_root/test/objdump/dwarf/cases/01-dwarf-all.sh" + start_ms=$(t1_now_ms) printf 'tier1/debug-dwarf: %s\n' "$name" ( @@ -43,10 +56,19 @@ run_objdump_dwarf() { rc=$? fi if [ "$rc" -eq 0 ]; then - printf ' ok: %s\n' "$log" + if t1_slow_timing_enabled; then + printf ' ok: %s (%sms)\n' "$log" "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" + else + printf ' ok: %s\n' "$log" + fi return 0 fi - printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + if t1_slow_timing_enabled; then + printf ' FAIL: %s (exit %s, %sms)\n' "$log" "$rc" \ + "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" >&2 + else + printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + fi tail -n 80 "$log" >&2 return "$rc" } @@ -57,33 +79,73 @@ if [ ! -f "$manifest" ]; then fi count=0 -while IFS= read -r step || [ -n "$step" ]; do - step=${step%%#*} - step=$(printf '%s' "$step" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') - [ -n "$step" ] || continue - count=$((count + 1)) +order="$log_dir/.order.$$" +: > "$order" +jobs=$(t1_parallel_jobs) || exit $? + +run_manifest_step() { + step=$1 case "$step" in - make:bin) - run_step bin make -C "$repo_root" bin || exit $? ;; make:test-dwarf) - run_step test-dwarf make -C "$repo_root" test-dwarf || exit $? ;; + run_step test-dwarf make -C "$repo_root" test-dwarf ;; make:test-debug) - run_step test-debug make -C "$repo_root" test-debug || exit $? ;; + run_step test-debug make -C "$repo_root" test-debug ;; driver:objdump-dwarf/01-dwarf-all) - run_objdump_dwarf || exit $? ;; + run_objdump_dwarf ;; driver:dbg/repl-help) run_step dbg-repl-help env KIT="$repo_root/build/kit" DBG_CASE=repl-help \ - sh "$repo_root/test/dbg/run.sh" || exit $? ;; + sh "$repo_root/test/dbg/run.sh" ;; driver:addr2line-symbolize) - run_step addr2line-symbolize make -C "$repo_root" test-rt-backtrace \ - || exit $? ;; + run_step addr2line-symbolize make -C "$repo_root" test-rt-backtrace ;; *) printf 'tier1/debug-dwarf: unknown manifest step: %s\n' "$step" >&2 - exit 2 ;; + return 2 ;; + esac +} + +start_step() { + step=$1 + id=$(t1_step_id "$step") + out="$log_dir/.step-$id.out" + status="$log_dir/.step-$id.status" + printf '%s\t%s\t%s\n' "$id" "$out" "$status" >> "$order" + t1_parallel_wait_for_slot "$jobs" + ( + if run_manifest_step "$step" > "$out" 2>&1; then + printf '0\n' > "$status" + else + printf '%s\n' "$?" > "$status" + fi + ) & +} + +while IFS= read -r step || [ -n "$step" ]; do + step=${step%%#*} + step=$(printf '%s' "$step" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + [ -n "$step" ] || continue + count=$((count + 1)) + case "$step" in + make:bin) + run_step bin make -C "$repo_root" bin || exit $? ;; + *) + start_step "$step" ;; esac done < "$manifest" +wait || true + if [ "$count" -eq 0 ]; then printf 'tier1/debug-dwarf: manifest has no runnable steps: %s\n' "$manifest" >&2 exit 2 fi + +rc=0 +while IFS="$(printf '\t')" read -r id out status; do + [ -n "$id" ] || continue + cat "$out" + step_rc=$(cat "$status" 2>/dev/null || printf '1\n') + if [ "$step_rc" -ne 0 ] && [ "$rc" -eq 0 ]; then + rc=$step_rc + fi +done < "$order" +exit "$rc" diff --git a/test/tier1/system/dist.sh b/test/tier1/system/dist.sh @@ -9,18 +9,30 @@ manifest="$repo_root/test/tier1/manifests/system/dist.txt" log_dir="${KIT_TIER1_LOG_DIR:-$repo_root/build/test/tier1/logs/system/dist}" mkdir -p "$log_dir" +. "$script_dir/../timing.sh" run_step() { name=$1 shift log="$log_dir/$name.log" + start_ms=$(t1_now_ms) printf 'tier1/dist: %s\n' "$name" - if "$@" > "$log" 2>&1; then - printf ' ok: %s\n' "$log" + "$@" > "$log" 2>&1 + rc=$? + if [ "$rc" -eq 0 ]; then + if t1_slow_timing_enabled; then + printf ' ok: %s (%sms)\n' "$log" "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" + else + printf ' ok: %s\n' "$log" + fi return 0 fi - rc=$? - printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + if t1_slow_timing_enabled; then + printf ' FAIL: %s (exit %s, %sms)\n' "$log" "$rc" \ + "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" >&2 + else + printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + fi tail -n 80 "$log" >&2 return "$rc" } @@ -31,15 +43,48 @@ if [ ! -f "$manifest" ]; then fi count=0 +order="$log_dir/.order.$$" +: > "$order" +jobs=$(t1_parallel_jobs) || exit $? + +start_step() { + target=$1 + id=$(t1_step_id "$target") + out="$log_dir/.step-$id.out" + status="$log_dir/.step-$id.status" + printf '%s\t%s\t%s\n' "$id" "$out" "$status" >> "$order" + t1_parallel_wait_for_slot "$jobs" + ( + if run_step "$target" make -C "$repo_root" "$target" > "$out" 2>&1; then + printf '0\n' > "$status" + else + printf '%s\n' "$?" > "$status" + fi + ) & +} + while IFS= read -r target || [ -n "$target" ]; do target=${target%%#*} target=$(printf '%s' "$target" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') [ -n "$target" ] || continue count=$((count + 1)) - run_step "$target" make -C "$repo_root" "$target" || exit $? + start_step "$target" done < "$manifest" +wait || true + if [ "$count" -eq 0 ]; then printf 'tier1/dist: manifest has no runnable targets: %s\n' "$manifest" >&2 exit 2 fi + +rc=0 +while IFS="$(printf '\t')" read -r id out status; do + [ -n "$id" ] || continue + cat "$out" + step_rc=$(cat "$status" 2>/dev/null || printf '1\n') + if [ "$step_rc" -ne 0 ] && [ "$rc" -eq 0 ]; then + rc=$step_rc + fi +done < "$order" +exit "$rc" diff --git a/test/tier1/system/runtime-headers.sh b/test/tier1/system/runtime-headers.sh @@ -7,6 +7,7 @@ script_dir=$(cd "$(dirname "$0")" && pwd) repo_root=$(cd "$script_dir/../../.." && pwd) manifest="$repo_root/test/tier1/manifests/system/runtime-headers.txt" log_dir="${KIT_TIER1_LOG_DIR:-$repo_root/build/test/tier1/logs/system/runtime-headers}" +KIT="${KIT:-$repo_root/build/kit}" mkdir -p "$log_dir" @@ -15,11 +16,12 @@ run_step() { shift log="$log_dir/$name.log" printf 'tier1/runtime-headers: %s\n' "$name" - if "$@" > "$log" 2>&1; then + "$@" > "$log" 2>&1 + rc=$? + if [ "$rc" -eq 0 ]; then printf ' ok: %s\n' "$log" return 0 fi - rc=$? printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 tail -n 80 "$log" >&2 return "$rc" @@ -36,7 +38,11 @@ while IFS= read -r target || [ -n "$target" ]; do target=${target%%#*} target=$(printf '%s' "$target" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') [ -n "$target" ] || continue - targets="$targets $target" + if [ -n "$targets" ]; then + targets="$targets $target" + else + targets="$target" + fi count=$((count + 1)) done < "$manifest" @@ -45,11 +51,11 @@ if [ "$count" -eq 0 ]; then exit 2 fi -run_step test-rt-headers make -C "$repo_root" \ - RT_HEADER_TEST_TARGETS="$targets" test-rt-headers || exit $? - for target in $targets; do out="$repo_root/build/test/rt-headers/$target/smoke.o" + mkdir -p "$(dirname "$out")" + run_step "rt-header-$target" "$KIT" cc -target "$target" -Werror \ + -c "$repo_root/test/rt/smoke.c" -o "$out" || exit $? if [ ! -s "$out" ]; then printf 'tier1/runtime-headers: missing compiled header smoke: %s\n' "$out" >&2 exit 1 diff --git a/test/tier1/system/tools.sh b/test/tier1/system/tools.sh @@ -9,18 +9,30 @@ manifest="$repo_root/test/tier1/manifests/system/tools.txt" log_dir="${KIT_TIER1_LOG_DIR:-$repo_root/build/test/tier1/logs/system/tools}" mkdir -p "$log_dir" +. "$script_dir/../timing.sh" run_step() { name=$1 shift log="$log_dir/$name.log" + start_ms=$(t1_now_ms) printf 'tier1/tools: %s\n' "$name" - if "$@" > "$log" 2>&1; then - printf ' ok: %s\n' "$log" + "$@" > "$log" 2>&1 + rc=$? + if [ "$rc" -eq 0 ]; then + if t1_slow_timing_enabled; then + printf ' ok: %s (%sms)\n' "$log" "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" + else + printf ' ok: %s\n' "$log" + fi return 0 fi - rc=$? - printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + if t1_slow_timing_enabled; then + printf ' FAIL: %s (exit %s, %sms)\n' "$log" "$rc" \ + "$(t1_elapsed_ms "$start_ms" "$(t1_now_ms)")" >&2 + else + printf ' FAIL: %s (exit %s)\n' "$log" "$rc" >&2 + fi tail -n 80 "$log" >&2 return "$rc" } @@ -31,15 +43,48 @@ if [ ! -f "$manifest" ]; then fi count=0 +order="$log_dir/.order.$$" +: > "$order" +jobs=$(t1_parallel_jobs) || exit $? + +start_step() { + target=$1 + id=$(t1_step_id "$target") + out="$log_dir/.step-$id.out" + status="$log_dir/.step-$id.status" + printf '%s\t%s\t%s\n' "$id" "$out" "$status" >> "$order" + t1_parallel_wait_for_slot "$jobs" + ( + if run_step "$target" make -C "$repo_root" "$target" > "$out" 2>&1; then + printf '0\n' > "$status" + else + printf '%s\n' "$?" > "$status" + fi + ) & +} + while IFS= read -r target || [ -n "$target" ]; do target=${target%%#*} target=$(printf '%s' "$target" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') [ -n "$target" ] || continue count=$((count + 1)) - run_step "$target" make -C "$repo_root" "$target" || exit $? + start_step "$target" done < "$manifest" +wait || true + if [ "$count" -eq 0 ]; then printf 'tier1/tools: manifest has no runnable targets: %s\n' "$manifest" >&2 exit 2 fi + +rc=0 +while IFS="$(printf '\t')" read -r id out status; do + [ -n "$id" ] || continue + cat "$out" + step_rc=$(cat "$status" 2>/dev/null || printf '1\n') + if [ "$step_rc" -ne 0 ] && [ "$rc" -eq 0 ]; then + rc=$step_rc + fi +done < "$order" +exit "$rc" diff --git a/test/tier1/timing.sh b/test/tier1/timing.sh @@ -0,0 +1,57 @@ +# Shared wall-clock helpers for Tier 1 shell wrappers. + +t1_now_ms() { + perl -MTime::HiRes=time -e 'printf "%.0f\n", time() * 1000' 2>/dev/null || + printf '%s000\n' "$(date +%s 2>/dev/null || printf '0')" +} + +t1_elapsed_ms() { + t1_em_start=$1 + t1_em_end=$2 + t1_em_dur=$((t1_em_end - t1_em_start)) + [ "$t1_em_dur" -lt 0 ] && t1_em_dur=0 + printf '%s\n' "$t1_em_dur" +} + +t1_slow_timing_enabled() { + case "${KIT_TIER1_SLOW_TIMING:-0}" in + 1|yes|true|on) return 0 ;; + *) return 1 ;; + esac +} + +t1_parallel_jobs() { + t1_pj_requested="${KIT_TIER1_JOBS:-${KIT_TEST_JOBS:-auto}}" + case "$t1_pj_requested" in + ""|1) printf '1\n'; return ;; + auto) + t1_pj_n=$(getconf _NPROCESSORS_ONLN 2>/dev/null || true) + [ -n "$t1_pj_n" ] || t1_pj_n=$(sysctl -n hw.ncpu 2>/dev/null || true) + case "$t1_pj_n" in ''|*[!0-9]*) t1_pj_n=1 ;; esac + [ "$t1_pj_n" -gt 6 ] && t1_pj_n=6 + [ "$t1_pj_n" -lt 1 ] && t1_pj_n=1 + printf '%s\n' "$t1_pj_n" + return ;; + *[!0-9]*) + printf 'invalid KIT_TIER1_JOBS=%s\n' "$t1_pj_requested" >&2 + return 2 ;; + *) + [ "$t1_pj_requested" -lt 1 ] && t1_pj_requested=1 + printf '%s\n' "$t1_pj_requested" ;; + esac +} + +t1_parallel_running_count() { + jobs -pr | wc -l | tr -d '[:space:]' +} + +t1_parallel_wait_for_slot() { + t1_pws_max=$1 + while [ "$(t1_parallel_running_count)" -ge "$t1_pws_max" ]; do + sleep 0.05 + done +} + +t1_step_id() { + printf '%s' "$1" | tr -c 'A-Za-z0-9_.-' '_' +}