commit 82ef995c9c7a61e8fed52f30e3eb019bd08bf95d
parent b0b93cc1caf3e43f8dfc363912764803e9c9dd25
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 18 Jun 2026 02:43:41 -0700
build: validate trace hit stats
Diffstat:
2 files changed, 148 insertions(+), 7 deletions(-)
diff --git a/driver/cmd/build_coord.c b/driver/cmd/build_coord.c
@@ -27,6 +27,7 @@ typedef struct BuildCli {
KitSlice args[BUILD_MAX_ARGS];
size_t nargs;
int verify;
+ int stats;
} BuildCli;
void driver_help_build(void) {
@@ -35,19 +36,20 @@ void driver_help_build(void) {
"\n"
"USAGE\n"
" kit build [--store DIR] [--root DIR] [--def FILE]\n"
- " [--config K=V]... [--arg VALUE]... [--verify] TARGET\n"
+ " [--config K=V]... [--verify] [--stats] TARGET [-- ARG...]\n"
" kit build config-get KEY # inside a build recipe\n"
" kit build source PATH # inside a build recipe\n"
" kit build glob PATTERN # inside a build recipe\n"
- " kit build need [--config K=V]... TARGET # inside a build recipe\n"
+ " kit build need [--config K=V]... TARGET [-- ARG...]\n"
+ " # inside a build recipe\n"
"\n"
"OPTIONS\n"
" --store DIR Build store root (default: $KIT cache/build)\n"
" --root DIR Workspace root (default: .)\n"
" --def FILE Build definition path (default: BUILD.kit)\n"
" --config K=V Seed propagated configuration\n"
- " --arg VALUE Add one target-local argv value\n"
- " --verify Verify the returned output tree in the CAS\n");
+ " --verify Verify the returned output tree in the CAS\n"
+ " --stats Print build-resolution counters to stderr\n");
}
static const char* build_status_name(KitStatus st) {
@@ -115,6 +117,19 @@ static int build_client_need_parse(BuildCli* cli, int argc, char** argv,
driver_errf(BUILD_TOOL, "bad --config, expected K=V");
return 2;
}
+ } else if (driver_streq(a, "--")) {
+ if (!cli->target) {
+ driver_errf(BUILD_TOOL, "missing client target before --");
+ return 2;
+ }
+ while (++i < argc) {
+ if (cli->nargs >= BUILD_MAX_ARGS) {
+ driver_errf(BUILD_TOOL, "too many recipe arguments");
+ return 2;
+ }
+ cli->args[cli->nargs++] = kit_slice_cstr(argv[i]);
+ }
+ break;
} else if (driver_streq(a, "--arg") && i + 1 < argc) {
if (cli->nargs >= BUILD_MAX_ARGS) {
driver_errf(BUILD_TOOL, "too many --arg values");
@@ -265,12 +280,27 @@ static int build_parse_args(BuildCli* cli, int argc, char** argv) {
driver_errf(BUILD_TOOL, "bad --config, expected K=V");
return 2;
}
+ } else if (driver_streq(a, "--")) {
+ if (!cli->target) {
+ driver_errf(BUILD_TOOL, "missing target before --");
+ return 2;
+ }
+ while (++i < argc) {
+ if (cli->nargs >= BUILD_MAX_ARGS) {
+ driver_errf(BUILD_TOOL, "too many recipe arguments");
+ return 2;
+ }
+ cli->args[cli->nargs++] = kit_slice_cstr(argv[i]);
+ }
+ break;
} else if (driver_streq(a, "--arg") && i + 1 < argc) {
if (cli->nargs >= BUILD_MAX_ARGS) {
driver_errf(BUILD_TOOL, "too many --arg values");
return 2;
}
cli->args[cli->nargs++] = kit_slice_cstr(argv[++i]);
+ } else if (driver_streq(a, "--stats")) {
+ cli->stats = 1;
} else if (driver_streq(a, "--verify")) {
cli->verify = 1;
} else if (a[0] == '-') {
@@ -290,6 +320,19 @@ static int build_parse_args(BuildCli* cli, int argc, char** argv) {
return 0;
}
+static void build_print_stats(const KitBuildStats* s) {
+ if (!s) return;
+ driver_errf(BUILD_TOOL,
+ "stats deep_hits=%llu shallow_hits=%llu recipes_run=%llu "
+ "materialize_misses=%llu object_fetches=%llu trace_pulls=%llu",
+ (unsigned long long)s->deep_hits,
+ (unsigned long long)s->shallow_hits,
+ (unsigned long long)s->recipes_run,
+ (unsigned long long)s->materialize_misses,
+ (unsigned long long)s->object_fetches,
+ (unsigned long long)s->trace_pulls);
+}
+
static int build_verify_tree(DriverEnv* env, const char* store,
const uint8_t tree[KIT_BUILD_HASH_LEN]) {
KitContext ctx = driver_env_to_context(env);
@@ -398,6 +441,11 @@ int driver_build(int argc, char** argv) {
rc = 1;
goto out;
}
+ if (cli.stats) {
+ KitBuildStats stats;
+ kit_build_stats(coord, &stats);
+ build_print_stats(&stats);
+ }
if (cli.verify && build_verify_tree(&env, abs_store, result.output_tree) !=
0) {
diff --git a/test/buildcoord/run.sh b/test/buildcoord/run.sh
@@ -32,12 +32,18 @@ recipe recipes/absent.sh
recipe recipes/app.sh
[target //argv:echo]
recipe recipes/argv.sh
+[target //cfg:probe]
+recipe recipes/cfg.sh
[target //lib:data]
recipe recipes/lib.sh
[target //needargv:probe]
recipe recipes/needargv.sh
[target //recipe:stamp]
recipe recipes/recipe.sh
+[target //stable:dep]
+recipe recipes/stable_dep.sh
+[target //stable:parent]
+recipe recipes/stable_parent.sh
EOF
cat > "$ws/src/a.txt" <<'EOF'
@@ -46,6 +52,9 @@ EOF
cat > "$ws/src/b.txt" <<'EOF'
beta
EOF
+cat > "$ws/src/noop.txt" <<'EOF'
+first
+EOF
cat > "$ws/recipes/lib.sh" <<'EOF'
#!/bin/sh
@@ -86,11 +95,19 @@ mkdir -p "$KIT_BUILD_OUT"
printf '%s\n' "$@" > "$KIT_BUILD_OUT/args.txt"
EOF
+cat > "$ws/recipes/cfg.sh" <<'EOF'
+#!/bin/sh
+set -eu
+mkdir -p "$KIT_BUILD_OUT"
+mode=$("$KIT" build config-get mode || true)
+printf 'mode:%s\n' "$mode" > "$KIT_BUILD_OUT/mode.txt"
+EOF
+
cat > "$ws/recipes/needargv.sh" <<'EOF'
#!/bin/sh
set -eu
mkdir -p "$KIT_BUILD_OUT"
-dep_dir=$("$KIT" build need --arg dep-one --arg dep-two //argv:echo)
+dep_dir=$("$KIT" build need //argv:echo -- dep-one dep-two)
cat "$dep_dir/args.txt" > "$KIT_BUILD_OUT/dep-args.txt"
EOF
@@ -100,6 +117,22 @@ set -eu
mkdir -p "$KIT_BUILD_OUT"
printf 'recipe:v1\n' > "$KIT_BUILD_OUT/stamp.txt"
EOF
+
+cat > "$ws/recipes/stable_dep.sh" <<'EOF'
+#!/bin/sh
+set -eu
+mkdir -p "$KIT_BUILD_OUT"
+"$KIT" build source src/noop.txt >/dev/null
+printf 'stable\n' > "$KIT_BUILD_OUT/stable.txt"
+EOF
+
+cat > "$ws/recipes/stable_parent.sh" <<'EOF'
+#!/bin/sh
+set -eu
+mkdir -p "$KIT_BUILD_OUT"
+dep_dir=$("$KIT" build need //stable:dep)
+cat "$dep_dir/stable.txt" > "$KIT_BUILD_OUT/stable.txt"
+EOF
chmod +x "$ws"/recipes/*.sh
run_build() {
@@ -209,11 +242,11 @@ else
not_ok "buildcoord-absent-created-new-tree" "$work/absent-tree.diag"
fi
-build_assert_ok buildcoord-argv-top --arg one --arg two //argv:echo
+build_assert_ok buildcoord-argv-top //argv:echo -- one two
argv_path=$(tree_path_from "$work/buildcoord-argv-top.out")
contains "buildcoord-argv-top-one" "$argv_path/args.txt" "one"
contains "buildcoord-argv-top-two" "$argv_path/args.txt" "two"
-build_assert_ok buildcoord-argv-changed --arg three //argv:echo
+build_assert_ok buildcoord-argv-changed //argv:echo -- three
argv_changed_path=$(tree_path_from "$work/buildcoord-argv-changed.out")
contains "buildcoord-argv-changed-output" "$argv_changed_path/args.txt" "three"
if [ "$argv_changed_path" != "$argv_path" ]; then
@@ -248,5 +281,65 @@ else
not_ok "buildcoord-recipe-edit-new-tree" "$work/recipe-tree.diag"
fi
+build_assert_ok buildcoord-stats-cold --stats //stable:parent
+contains "buildcoord-stats-cold-recipes" "$work/buildcoord-stats-cold.err" \
+ "recipes_run=2"
+contains "buildcoord-stats-cold-no-deep" "$work/buildcoord-stats-cold.err" \
+ "deep_hits=0"
+contains "buildcoord-stats-cold-no-shallow" "$work/buildcoord-stats-cold.err" \
+ "shallow_hits=0"
+
+build_assert_ok buildcoord-stats-deep --stats //stable:parent
+contains "buildcoord-stats-deep-hit" "$work/buildcoord-stats-deep.err" \
+ "deep_hits=1"
+contains "buildcoord-stats-deep-no-run" "$work/buildcoord-stats-deep.err" \
+ "recipes_run=0"
+
+cat > "$ws/src/noop.txt" <<'EOF'
+second
+EOF
+build_assert_ok buildcoord-stats-shallow --stats //stable:parent
+contains "buildcoord-stats-shallow-hit" "$work/buildcoord-stats-shallow.err" \
+ "shallow_hits=1"
+contains "buildcoord-stats-shallow-child-run" "$work/buildcoord-stats-shallow.err" \
+ "recipes_run=1"
+stable_shallow_path=$(tree_path_from "$work/buildcoord-stats-shallow.out")
+contains "buildcoord-stats-shallow-output" "$stable_shallow_path/stable.txt" \
+ "stable"
+
+build_assert_ok buildcoord-stats-cfg-cold --stats --config mode=debug //cfg:probe
+contains "buildcoord-stats-cfg-cold-run" "$work/buildcoord-stats-cfg-cold.err" \
+ "recipes_run=1"
+build_assert_ok buildcoord-stats-cfg-deep --stats --config mode=debug //cfg:probe
+contains "buildcoord-stats-cfg-deep-hit" "$work/buildcoord-stats-cfg-deep.err" \
+ "deep_hits=1"
+contains "buildcoord-stats-cfg-deep-no-run" "$work/buildcoord-stats-cfg-deep.err" \
+ "recipes_run=0"
+build_assert_ok buildcoord-stats-cfg-change --stats --config mode=release //cfg:probe
+contains "buildcoord-stats-cfg-change-run" "$work/buildcoord-stats-cfg-change.err" \
+ "recipes_run=1"
+contains "buildcoord-stats-cfg-change-no-hit" "$work/buildcoord-stats-cfg-change.err" \
+ "shallow_hits=0"
+cfg_change_path=$(tree_path_from "$work/buildcoord-stats-cfg-change.out")
+contains "buildcoord-stats-cfg-change-output" "$cfg_change_path/mode.txt" \
+ "mode:release"
+
+build_assert_ok buildcoord-stats-argv-cold --stats //argv:echo -- stat-one
+contains "buildcoord-stats-argv-cold-run" "$work/buildcoord-stats-argv-cold.err" \
+ "recipes_run=1"
+build_assert_ok buildcoord-stats-argv-deep --stats //argv:echo -- stat-one
+contains "buildcoord-stats-argv-deep-hit" "$work/buildcoord-stats-argv-deep.err" \
+ "deep_hits=1"
+contains "buildcoord-stats-argv-deep-no-run" "$work/buildcoord-stats-argv-deep.err" \
+ "recipes_run=0"
+build_assert_ok buildcoord-stats-argv-change --stats //argv:echo -- stat-two
+contains "buildcoord-stats-argv-change-run" "$work/buildcoord-stats-argv-change.err" \
+ "recipes_run=1"
+contains "buildcoord-stats-argv-change-no-hit" "$work/buildcoord-stats-argv-change.err" \
+ "shallow_hits=0"
+argv_stat_path=$(tree_path_from "$work/buildcoord-stats-argv-change.out")
+contains "buildcoord-stats-argv-change-output" "$argv_stat_path/args.txt" \
+ "stat-two"
+
kit_summary "buildcoord"
kit_exit