Skip to documentation
SLOP

tiny.profiling.scenario

Reference tiny.profiling scenario

Defined in tiny.profiling.

API (21)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.profilingscenario
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callersscenarioparsescenarioload
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsexperiment.parseparsescenarioloadtest; no linksrc.profiling.scenariotest: profiling scenario parses bound...test; no linksrc.profiling.scenariotest: profiling scenario rejects ambi...jsonobjectprivate; no linksrc.profiling.scenariooptionalPathprivate; no linksrc.profiling.scenarioparseArgumentsprivate; no linksrc.profiling.scenarioparseCommandprivate; no linksrc.profiling.scenarioparseEnvironment+5 morescenarioparse
Static calls · unresolved targets: 1 · external targets: 1.
Called byCallsprivate; no linksrc.profiling.experiment.acquireprepareSideexperiment.variantcommandtest; no linksrc.profiling.scenariotest: profiling scenario resolves var...scenariosubstitutescenarioresolveCommand
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate; no linksrc.profiling.experiment.acquireprepareSidescenariosubstitutescenarioresolveEnvironment
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callsprivate; no linksrc.profiling.experiment.oracleverifyFilesexperiment.variantcommandprivate; no linksrc.profiling.experiment.variantexplicitBuildscenarioresolveCommandscenarioresolveEnvironmentscenariosubstitute
Static calls · unresolved targets: 0 · external targets: 1.

Source: src/profiling/root.zig:41

zig
pub const scenario = @import("scenario.zig");

Source: src/profiling/scenario.zig

zig
const std = @import("std");const sys = @import("sys");const json = @import("json.zig");const substitution = @import("substitute.zig");pub const schema = "tiny.profiling.scenario/v1";pub const maximum_plan_bytes: usize = 1024 * 1024;pub const maximum_arguments: usize = 128;pub const maximum_environment_entries: usize = 64;pub const maximum_oracle_files: usize = 16;pub const maximum_string_bytes: usize = 4096;pub const maximum_resolved_string_bytes: usize = 16 * 1024;pub const digest_hex_bytes: usize = std.crypto.hash.sha2.Sha256.digest_length * 2;pub const Environment = struct {    name: []const u8,    value: []const u8,};pub const Command = struct {    argv: []const []const u8,    cwd: ?[]const u8 = null,};pub const FileOracle = struct {    path: []const u8,    sha256: []const u8,};pub const Oracle = struct {    exit_code: i64 = 0,    stdout_sha256: ?[]const u8 = null,    stderr_sha256: ?[]const u8 = null,    files: []const FileOracle = &.{},};pub const CatalogInput = struct {    workload: []const u8,    args: []const []const u8 = &.{},};pub const Input = union(enum) {    catalog: CatalogInput,    command: []const []const u8,};pub const Definition = struct {    name: []const u8,    input: Input,    cwd: ?[]const u8 = null,    environment: []const Environment = &.{},    setup: ?Command = null,    reset: ?Command = null,    oracle: Oracle = .{},};pub const Substitutions = struct {    binary: []const u8,    root: []const u8,    side: []const u8,};pub fn load(    allocator: std.mem.Allocator,    path: []const u8,) !Definition {    const text = try sys.fs.readFileAlloc(allocator, path, maximum_plan_bytes);    const value = try std.json.parseFromSliceLeaky(        std.json.Value,        allocator,        text,        .{},    );    return try parse(allocator, value);}pub fn parse(    allocator: std.mem.Allocator,    value: std.json.Value,) !Definition {    const object = try json.object(value);    try requireSchema(object);    const name = try requiredString(object, "name");    const argv_value = object.get("argv");    const workload = json.string(object.get("workload"));    if ((argv_value == null) == (workload == null)) return error.InvalidScenarioInput;    const input = if (workload) |workload_name|        Input{ .catalog = .{            .workload = try validateString(workload_name),            .args = try parseArguments(allocator, object.get("args"), true),        } }    else        Input{ .command = try parseArguments(allocator, argv_value, false) };    const definition = Definition{        .name = try validateString(name),        .input = input,        .cwd = try optionalPath(object.get("cwd")),        .environment = try parseEnvironment(allocator, object.get("env")),        .setup = try parseCommand(allocator, object.get("setup")),        .reset = try parseCommand(allocator, object.get("reset")),        .oracle = try parseOracle(allocator, object.get("oracle")),    };    try validateDefinition(definition);    return definition;}fn requireSchema(object: std.json.ObjectMap) !void {    const actual = json.string(object.get("schema")) orelse        return error.InvalidScenarioSchema;    if (!std.mem.eql(u8, actual, schema)) return error.InvalidScenarioSchema;}fn requiredString(    object: std.json.ObjectMap,    field: []const u8,) ![]const u8 {    return json.string(object.get(field)) orelse error.InvalidScenarioInput;}fn parseArguments(    allocator: std.mem.Allocator,    value: ?std.json.Value,    empty_allowed: bool,) ![]const []const u8 {    const actual = value orelse {        if (empty_allowed) return &.{};        return error.InvalidScenarioArguments;    };    const rows = try json.array(actual);    if ((!empty_allowed and rows.items.len == 0) or        rows.items.len > maximum_arguments)    {        return error.InvalidScenarioArguments;    }    const result = try allocator.alloc([]const u8, rows.items.len);    for (rows.items, 0..) |item, index| {        result[index] = try validateString(            json.string(item) orelse return error.InvalidScenarioArguments,        );    }    return result;}fn parseEnvironment(    allocator: std.mem.Allocator,    value: ?std.json.Value,) ![]const Environment {    const actual = value orelse return &.{};    const rows = try json.array(actual);    if (rows.items.len > maximum_environment_entries) {        return error.InvalidScenarioEnvironment;    }    const result = try allocator.alloc(Environment, rows.items.len);    for (rows.items, 0..) |item, index| {        const object = try json.object(item);        const name = try requiredString(object, "name");        if (!validEnvironmentName(name)) return error.InvalidScenarioEnvironment;        result[index] = .{            .name = name,            .value = try validateString(try requiredString(object, "value")),        };    }    return result;}fn parseCommand(    allocator: std.mem.Allocator,    value: ?std.json.Value,) !?Command {    const actual = value orelse return null;    const object = try json.object(actual);    return .{        .argv = try parseArguments(allocator, object.get("argv"), false),        .cwd = try optionalPath(object.get("cwd")),    };}fn parseOracle(    allocator: std.mem.Allocator,    value: ?std.json.Value,) !Oracle {    const actual = value orelse return .{};    const object = try json.object(actual);    const exit_code = if (object.get("exit_code")) |exit_value|        json.asI64(exit_value) orelse return error.InvalidScenarioOracle    else        0;    return .{        .exit_code = exit_code,        .stdout_sha256 = try optionalDigest(object.get("stdout_sha256")),        .stderr_sha256 = try optionalDigest(object.get("stderr_sha256")),        .files = try parseFileOracles(allocator, object.get("files")),    };}fn parseFileOracles(    allocator: std.mem.Allocator,    value: ?std.json.Value,) ![]const FileOracle {    const actual = value orelse return &.{};    const rows = try json.array(actual);    if (rows.items.len > maximum_oracle_files) {        return error.InvalidScenarioOracle;    }    const result = try allocator.alloc(FileOracle, rows.items.len);    for (rows.items, 0..) |item, index| {        const object = try json.object(item);        result[index] = .{            .path = try validateString(try requiredString(object, "path")),            .sha256 = (try optionalDigest(object.get("sha256"))) orelse                return error.InvalidScenarioOracle,        };    }    return result;}fn optionalDigest(value: ?std.json.Value) !?[]const u8 {    const digest = json.string(value) orelse {        if (value == null) return null;        return error.InvalidScenarioOracle;    };    if (!validDigest(digest)) return error.InvalidScenarioOracle;    return digest;}fn optionalPath(value: ?std.json.Value) !?[]const u8 {    const path = json.string(value) orelse {        if (value == null) return null;        return error.InvalidScenarioPath;    };    return try validateString(path);}fn validateString(value: []const u8) ![]const u8 {    if (value.len == 0 or value.len > maximum_string_bytes or        std.mem.indexOfScalar(u8, value, 0) != null)    {        return error.InvalidScenarioString;    }    return value;}fn validEnvironmentName(value: []const u8) bool {    if (value.len == 0 or value.len > maximum_string_bytes) return false;    return std.mem.indexOfAny(u8, value, "=\x00") == null;}fn validDigest(value: []const u8) bool {    if (value.len != digest_hex_bytes) return false;    for (value) |byte| {        if (!std.ascii.isDigit(byte) and (byte < 'a' or byte > 'f')) return false;    }    return true;}fn validateDefinition(definition: Definition) !void {    if (definition.environment.len > maximum_environment_entries) {        return error.InvalidScenarioEnvironment;    }    for (definition.environment, 0..) |left, left_index| {        for (definition.environment[left_index + 1 ..]) |right| {            if (std.mem.eql(u8, left.name, right.name)) {                return error.DuplicateScenarioEnvironment;            }        }    }    if (definition.reset) |reset| {        if (reset.argv.len == 0) return error.InvalidScenarioArguments;    }}pub fn substitute(    allocator: std.mem.Allocator,    template: []const u8,    values: Substitutions,) ![]const u8 {    const result = substitution.resolve(allocator, template, &.{        .{ .token = "{binary}", .value = values.binary },        .{ .token = "{root}", .value = values.root },        .{ .token = "{side}", .value = values.side },    }, maximum_resolved_string_bytes) catch |err| switch (err) {        error.InvalidResolvedString => return error.InvalidResolvedScenarioString,        else => |other| return other,    };    if (result.len == 0) return error.InvalidResolvedScenarioString;    return result;}pub fn resolveCommand(    allocator: std.mem.Allocator,    command: Command,    default_cwd: ?[]const u8,    values: Substitutions,) !Command {    const argv = try allocator.alloc([]const u8, command.argv.len);    for (command.argv, 0..) |argument, index| {        argv[index] = try substitute(allocator, argument, values);    }    const cwd_template = command.cwd orelse default_cwd;    return .{        .argv = argv,        .cwd = if (cwd_template) |cwd|            try substitute(allocator, cwd, values)        else            null,    };}pub fn resolveEnvironment(    allocator: std.mem.Allocator,    environment: []const Environment,    values: Substitutions,) ![]const Environment {    const result = try allocator.alloc(Environment, environment.len);    for (environment, 0..) |entry, index| {        result[index] = .{            .name = entry.name,            .value = try substitute(allocator, entry.value, values),        };    }    return result;}test "profiling scenario parses bounded catalog inputs" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const value = try std.json.parseFromSliceLeaky(        std.json.Value,        allocator,        \\{"schema":"tiny.profiling.scenario/v1","name":"cli startup",        \\ "workload":"smg.graph","args":["--small"],"cwd":".",        \\ "env":[{"name":"MODE","value":"bench"}],        \\ "reset":{"argv":["fixture","reset"]},        \\ "oracle":{"exit_code":0}}    ,        .{},    );    const definition = try parse(allocator, value);    try std.testing.expectEqualStrings("cli startup", definition.name);    try std.testing.expectEqualStrings(        "smg.graph",        definition.input.catalog.workload,    );    try std.testing.expectEqualStrings("--small", definition.input.catalog.args[0]);    try std.testing.expectEqualStrings("MODE", definition.environment[0].name);    try std.testing.expectEqualStrings("fixture", definition.reset.?.argv[0]);}test "profiling scenario rejects ambiguous inputs and malformed digests" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const ambiguous = try std.json.parseFromSliceLeaky(        std.json.Value,        allocator,        \\{"schema":"tiny.profiling.scenario/v1","name":"bad",        \\ "workload":"smg.graph","argv":["smg"]}    ,        .{},    );    try std.testing.expectError(        error.InvalidScenarioInput,        parse(allocator, ambiguous),    );    const digest = try std.json.parseFromSliceLeaky(        std.json.Value,        allocator,        \\{"schema":"tiny.profiling.scenario/v1","name":"bad",        \\ "argv":["smg"],"oracle":{"stdout_sha256":"ABC"}}    ,        .{},    );    try std.testing.expectError(        error.InvalidScenarioOracle,        parse(allocator, digest),    );}test "profiling scenario resolves variant tokens without a shell" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const command = try resolveCommand(        allocator,        .{            .argv = &.{ "{binary}", "--root={root}", "{side}" },            .cwd = "{root}/fixture",        },        null,        .{            .binary = "/tmp/base/bin",            .root = "/tmp/base",            .side = "baseline",        },    );    try std.testing.expectEqualStrings("/tmp/base/bin", command.argv[0]);    try std.testing.expectEqualStrings("--root=/tmp/base", command.argv[1]);    try std.testing.expectEqualStrings(        "/tmp/base/fixture",        command.cwd.?,    );}

Complete call list for scenario.parse

10 direct calls.

Audit

Definitions22
Public names22
Members24
Version26.7.0
Revisiondaab053ee433