Print how a --profile-use build's profile matched

`scriptc build --profile-use` prints one line on stderr with the number of
functions the profile annotated, the number whose control flow changed since
it was collected (mismatched), and the number it has no record of. When more
than 10% of the functions the profile describes are mismatched, it also prints
a warning to collect the profile again. The build still succeeds.

Both the compiler library's build and the native compiler's build report the
match. Cached builds report the match of the build that produced them: the
native codegen and object caches store it beside the cached objects, and the
executable cache stores it in its entry.
This commit is contained in:
Chris Tate
2026-10-25 22:14:25 -05:00
parent 828ea84dcc
commit f591e8e6d7
13 changed files with 416 additions and 31 deletions
@@ -12,6 +12,7 @@ import {
} from "node:fs/promises";
import { basename, dirname, join, resolve } from "node:path";
import type { ScrDiagnostic } from "../diagnostics/diagnostic.js";
import type { NativeProfileMatch } from "../backend/native-profile.js";
import type { FrontendInputSnapshot } from "../frontend/input-tracker.js";
import { frontendInputsStillMatch, validFrontendInputSnapshot } from "../frontend/input-tracker.js";
import { compilerReleaseVersion } from "../library/sidecar.js";
@@ -129,6 +130,8 @@ interface EarlyExecutableCacheStamp {
native: EarlyExecutableNativeFeatures;
/** The build's performance warnings (SC61xx), which a cache hit repeats. */
performance?: ScrDiagnostic[];
/** How a --profile-use build's profile matched, which a cache hit reports. */
profileMatch?: NativeProfileMatch;
integrity: string;
}
@@ -213,12 +216,23 @@ export interface EarlyExecutableCacheHit {
frontend: FrontendInputSnapshot;
/** The performance warnings the build reported (SC61xx), repeated on a hit. */
performance?: ScrDiagnostic[];
/** How a --profile-use build's profile matched, reported again on a hit. */
profileMatch?: NativeProfileMatch;
}
export interface EarlyExecutableCachePublish extends EarlyExecutableCacheHit {
nativeDependencies?: NativeArtifactDependency[];
}
function isProfileMatch(value: unknown): value is NativeProfileMatch {
if (typeof value !== "object" || value === null) return false;
const match = value as Record<string, unknown>;
return ["annotated", "mismatched", "missing", "unknownPartitions"].every((key) => {
const count = match[key];
return typeof count === "number" && Number.isInteger(count) && count >= 0;
});
}
const BOOLEAN_NATIVE_KEYS = [
"regex",
"copying",
@@ -671,6 +685,7 @@ async function readExecutableCache(
...(Array.isArray(stamp.performance) && stamp.performance.length > 0
? { performance: stamp.performance }
: {}),
...(isProfileMatch(stamp.profileMatch) ? { profileMatch: stamp.profileMatch } : {}),
...(ir === null ? {} : { irPath: paths.irPath }),
...(llvmSourcePath === undefined ? {} : { llvmSourcePath }),
...(irSourcePath === undefined ? {} : { irSourcePath }),
@@ -861,6 +876,7 @@ export async function publishEarlyExecutableCache(
...(result.performance === undefined || result.performance.length === 0
? {}
: { performance: result.performance }),
...(result.profileMatch === undefined ? {} : { profileMatch: result.profileMatch }),
};
const stamp: EarlyExecutableCacheStamp = { ...unsigned, integrity: stampIntegrity(unsigned) };
await writeFile(join(stage, "stamp.json"), `${JSON.stringify(stamp)}\n`, { mode: 0o600 });