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.
digest_hex_bytesmaximum_argumentsmaximum_environment_entriesmaximum_oracle_filesmaximum_plan_bytesmaximum_resolved_string_bytesmaximum_string_bytesschema
Source
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.
tiny.profiling.json.object[function] atsrc/profiling/json.zig:3src.profiling.scenario.optionalPath[function] — private; no exact target atsrc/profiling/scenario.zig:227in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.parseArguments[function] — private; no exact target atsrc/profiling/scenario.zig:121in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.parseCommand[function] — private; no exact target atsrc/profiling/scenario.zig:167in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.parseEnvironment[function] — private; no exact target atsrc/profiling/scenario.zig:145in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.parseOracle[function] — private; no exact target atsrc/profiling/scenario.zig:179in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.requireSchema[function] — private; no exact target atsrc/profiling/scenario.zig:108in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.requiredString[function] — private; no exact target atsrc/profiling/scenario.zig:114in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.validateDefinition[function] — private; no exact target atsrc/profiling/scenario.zig:257in nearest public ownertiny.profiling.scenariosrc.profiling.scenario.validateString[function] — private; no exact target atsrc/profiling/scenario.zig:235in nearest public ownertiny.profiling.scenario
Audit
| Definitions | 22 |
|---|---|
| Public names | 22 |
| Members | 24 |
| Version | 26.7.0 |
| Revision | daab053ee433 |