tiny.profiling.capture.coz
Defined in capture.
API (13)
Actions
Public operations.
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
Source
Source: src/profiling/capture/coz.zig
zig
const std = @import("std");const coz = @import("coz");const sys = @import("sys");const host = @import("../root.zig").host;const json = @import("../root.zig").json;const fs_io = sys.fs.debugIo();pub const analysis_schema = coz.analysis.schema;pub const support_method = coz.analysis.support_method;pub const capture_kind = "causal_profile";pub const capture_tool = "lib/coz";const max_analysis_bytes = 128 * 1024 * 1024;pub const CaptureOptions = struct { enabled: bool, exit_code: i64, capture_path: []const u8, summary_path: []const u8,};const Integrity = struct { status: []const u8, message: ?[]const u8,};pub const Measurement = struct { virtual_speedup: f64, program_speedup: f64, experiment_count: u64,};pub const SupportStatus = coz.analysis.SupportStatus;pub const Support = coz.analysis.Support;pub const supportAction = coz.analysis.supportAction;pub const Result = struct { workload: []const u8, kind: []const u8, file: []const u8, line: u64, progress_point: []const u8, min_program_speedup: f64, max_program_speedup: f64, slope: ?f64, total_selected_samples: u64, support: Support, measurements: []const Measurement = &.{},};pub fn load(allocator: std.mem.Allocator, workload_name: []const u8, path: ?[]const u8) ![]const Result { const actual_path = path orelse return &.{}; const text = sys.fs.readFileAlloc(allocator, actual_path, max_analysis_bytes) catch |err| switch (err) { error.FileNotFound => return &.{}, else => |actual| return actual, }; defer allocator.free(text); const trimmed = std.mem.trim(u8, text, " \t\r\n"); if (trimmed.len == 0) return &.{}; var parsed = std.json.parseFromSlice(std.json.Value, allocator, trimmed, .{}) catch return &.{}; defer parsed.deinit(); const object = json.object(parsed.value) catch return &.{}; const schema_name = json.string(object.get("schema")) orelse return &.{}; if (!std.mem.eql(u8, schema_name, analysis_schema)) return &.{}; const results_value = object.get("results") orelse return &.{}; const results = json.array(results_value) catch return &.{}; var rows: std.ArrayList(Result) = .empty; for (results.items) |item| { const row = json.object(item) catch continue; if (try parseResult(allocator, workload_name, row)) |result| try rows.append(allocator, result); } std.mem.sort(Result, rows.items, {}, resultDescending); return try rows.toOwnedSlice(allocator);}fn resultDescending(_: void, left: Result, right: Result) bool { const left_supported = left.support.status == .within_run_repeated_curve; const right_supported = right.support.status == .within_run_repeated_curve; if (left_supported != right_supported) return left_supported; if (left.max_program_speedup != right.max_program_speedup) { return left.max_program_speedup > right.max_program_speedup; } const file_order = std.mem.order(u8, left.file, right.file); if (file_order != .eq) return file_order == .lt; if (left.line != right.line) return left.line < right.line; return std.mem.lessThan(u8, left.progress_point, right.progress_point);}pub fn bestSupportedProgramSpeedup(results: []const Result, workload_name: []const u8) ?f64 { var best: ?f64 = null; for (results) |result| { if (!std.mem.eql(u8, result.workload, workload_name)) continue; if (result.support.status != .within_run_repeated_curve) continue; if (result.max_program_speedup <= 0) continue; if (best == null or result.max_program_speedup > best.?) best = result.max_program_speedup; } return best;}pub fn appendCapture( allocator: std.mem.Allocator, captures: []const host.Capture, options: CaptureOptions,) ![]const host.Capture { if (!options.enabled or options.exit_code != 0) return captures; const integrity = readIntegrity(allocator, options.summary_path) catch |err| switch (err) { error.OutOfMemory => return err, error.FileNotFound => return try appendCaptureRow( allocator, captures, missingCapture(options), ), else => return try appendCaptureRow(allocator, captures, invalidCapture(options)), }; return try appendCaptureRow(allocator, captures, captureForIntegrity(options, integrity));}fn readIntegrity(allocator: std.mem.Allocator, path: []const u8) !Integrity { const text = try sys.fs.readFileAlloc(allocator, path, max_analysis_bytes); defer allocator.free(text); const trimmed = std.mem.trim(u8, text, " \t\r\n"); if (trimmed.len == 0) return error.InvalidCozAnalysis; var parsed = std.json.parseFromSlice(std.json.Value, allocator, trimmed, .{}) catch { return error.InvalidCozAnalysis; }; defer parsed.deinit(); const object = json.object(parsed.value) catch return error.InvalidCozAnalysis; const schema_name = json.string(object.get("schema")) orelse return error.InvalidCozAnalysis; if (!std.mem.eql(u8, schema_name, analysis_schema)) return error.InvalidCozAnalysis; const integrity_value = object.get("capture_integrity") orelse { return error.InvalidCozAnalysis; }; const integrity = json.object(integrity_value) catch { return error.InvalidCozAnalysis; }; const status = json.string(integrity.get("status")) orelse return error.InvalidCozAnalysis; const method = json.string(integrity.get("method")) orelse return error.InvalidCozAnalysis; const action = json.string(integrity.get("action")) orelse return error.InvalidCozAnalysis; if (status.len == 0 or method.len == 0 or action.len == 0) return error.InvalidCozAnalysis; const complete = std.mem.eql(u8, status, "complete"); const message = optionalString(integrity.get("message")) catch { return error.InvalidCozAnalysis; }; if (complete and message != null) return error.InvalidCozAnalysis; if (!complete and message == null) return error.InvalidCozAnalysis; const owned_status = try allocator.dupe(u8, status); errdefer allocator.free(owned_status); return .{ .status = owned_status, .message = if (message) |value| try allocator.dupe(u8, value) else null, };}fn optionalString(value: ?std.json.Value) !?[]const u8 { const actual = value orelse return error.InvalidCozAnalysis; return switch (actual) { .null => null, .string => |text| text, else => error.InvalidCozAnalysis, };}fn appendCaptureRow( allocator: std.mem.Allocator, captures: []const host.Capture, row: host.Capture,) ![]const host.Capture { const result = try allocator.alloc(host.Capture, captures.len + 1); @memcpy(result[0..captures.len], captures); result[captures.len] = row; return result;}fn captureForIntegrity(options: CaptureOptions, integrity: Integrity) host.Capture { const complete = std.mem.eql(u8, integrity.status, "complete"); return .{ .kind = capture_kind, .tool = capture_tool, .scope = host.direct_scope, .capture_path = options.capture_path, .summary_path = options.summary_path, .state = "summary_written", .caveat_kind = if (complete) null else integrity.status, .caveat_message = if (complete) null else integrity.message, };}fn missingCapture(options: CaptureOptions) host.Capture { return .{ .kind = capture_kind, .tool = capture_tool, .scope = host.direct_scope, .capture_path = options.capture_path, .summary_path = options.summary_path, .state = "capture_missing", .caveat_kind = "causal_profile_missing", .caveat_message = "causal profiling was requested but produced no analysis artifact", };}fn invalidCapture(options: CaptureOptions) host.Capture { return .{ .kind = capture_kind, .tool = capture_tool, .scope = host.direct_scope, .capture_path = options.capture_path, .summary_path = options.summary_path, .state = "summary_invalid", .caveat_kind = "invalid_causal_profile", .caveat_message = "causal profile analysis does not contain valid " ++ "sampling integrity evidence", };}fn parseResult(allocator: std.mem.Allocator, workload_name: []const u8, row: std.json.ObjectMap) !?Result { const selected = json.object(row.get("selected") orelse return null) catch return null; const file = json.string(selected.get("file")) orelse return null; const progress_point = json.string(row.get("progress_point")) orelse return null; const max_program_speedup = json.asF64(row.get("max_program_speedup")) orelse return null; const baseline_virtual_speedup = json.asF64( row.get("baseline_virtual_speedup"), ) orelse return null; const support = parseSupport(row.get("support") orelse return null) orelse return null; var measurements: std.ArrayList(Measurement) = .empty; defer measurements.deinit(allocator); if (row.get("measurements")) |value| { if (json.array(value) catch null) |array| { for (array.items) |item| { const measurement = json.object(item) catch continue; try measurements.append(allocator, .{ .virtual_speedup = json.asF64(measurement.get("virtual_speedup")) orelse continue, .program_speedup = json.asF64(measurement.get("program_speedup")) orelse continue, .experiment_count = json.asU64( measurement.get("experiment_count"), ) orelse continue, }); } } } if (!supportMatchesMeasurements(support, baseline_virtual_speedup, measurements.items)) { return null; } return .{ .workload = try allocator.dupe(u8, workload_name), .kind = try allocator.dupe(u8, json.string(row.get("kind")) orelse "throughput"), .file = try allocator.dupe(u8, file), .line = json.asU64(selected.get("line")) orelse 0, .progress_point = try allocator.dupe(u8, progress_point), .min_program_speedup = json.asF64(row.get("min_program_speedup")) orelse 0, .max_program_speedup = max_program_speedup, .slope = json.asF64(row.get("slope")), .total_selected_samples = json.asU64(row.get("total_selected_samples")) orelse 0, .support = support, .measurements = try measurements.toOwnedSlice(allocator), };}fn supportMatchesMeasurements( support: Support, baseline_virtual_speedup: f64, measurements: []const Measurement,) bool { if (support.speedup_point_count != @as(u64, @intCast(measurements.len))) return false; var experiment_count: u64 = 0; var baseline_experiment_count: u64 = 0; var minimum_experiments_per_point: u64 = if (measurements.len == 0) 0 else std.math.maxInt(u64); for (measurements) |measurement| { if (measurement.experiment_count == 0) return false; experiment_count +|= measurement.experiment_count; minimum_experiments_per_point = @min( minimum_experiments_per_point, measurement.experiment_count, ); if (measurement.virtual_speedup == baseline_virtual_speedup) { baseline_experiment_count = measurement.experiment_count; } } const expected_status: SupportStatus = if (measurements.len < 2) .point_only else if (minimum_experiments_per_point < 2) .unreplicated_curve else .within_run_repeated_curve; return support.status == expected_status and support.experiment_count == experiment_count and support.baseline_experiment_count == baseline_experiment_count and support.minimum_experiments_per_point == minimum_experiments_per_point;}fn parseSupport(value: std.json.Value) ?Support { const object = json.object(value) catch return null; const status_name = json.string(object.get("status")) orelse return null; const status = std.meta.stringToEnum(SupportStatus, status_name) orelse return null; const method = json.string(object.get("method")) orelse return null; if (!std.mem.eql(u8, method, support_method)) return null; const action = json.string(object.get("action")) orelse return null; if (!std.mem.eql(u8, action, supportAction(status))) return null; return .{ .status = status, .speedup_point_count = json.asU64(object.get("speedup_point_count")) orelse return null, .experiment_count = json.asU64(object.get("experiment_count")) orelse return null, .baseline_experiment_count = json.asU64( object.get("baseline_experiment_count"), ) orelse return null, .minimum_experiments_per_point = json.asU64( object.get("minimum_experiments_per_point"), ) orelse return null, };}const supported_analysis_fixture = \\{"schema":"coz.analysis/v2","results":[ \\ {"selected":{"file":"src/hot.zig","line":9}, \\ "progress_point":"bench.sample","kind":"throughput", \\ "baseline_virtual_speedup":0,"min_program_speedup":0, \\ "max_program_speedup":0.9,"total_selected_samples":512,"slope":1.8, \\ "support":{"status":"unreplicated_curve", \\ "method":"within_run_experiment_counts", \\ "action":"collect_repeated_experiments_per_point","speedup_point_count":2, \\ "experiment_count":2,"baseline_experiment_count":1, \\ "minimum_experiments_per_point":1}, \\ "measurements":[ \\ {"virtual_speedup":0,"program_speedup":0,"experiment_count":1}, \\ {"virtual_speedup":0.5,"program_speedup":0.9,"experiment_count":1}]}, \\ {"selected":{"file":"src/eval.zig","line":42}, \\ "progress_point":"bench.sample","kind":"throughput", \\ "baseline_virtual_speedup":0,"min_program_speedup":-0.02, \\ "max_program_speedup":0.35,"total_selected_samples":128,"slope":0.7, \\ "support":{"status":"within_run_repeated_curve", \\ "method":"within_run_experiment_counts", \\ "action":"validate_with_independent_runs","speedup_point_count":2, \\ "experiment_count":4,"baseline_experiment_count":2, \\ "minimum_experiments_per_point":2}, \\ "measurements":[ \\ {"virtual_speedup":0,"program_speedup":0,"experiment_count":2}, \\ {"virtual_speedup":0.2,"program_speedup":0.2,"experiment_count":2}]} \\]};const inconsistent_support_fixture = \\{"schema":"coz.analysis/v2","results":[{ \\ "selected":{"file":"src/eval.zig","line":42}, \\ "progress_point":"bench.sample","kind":"throughput", \\ "baseline_virtual_speedup":0,"max_program_speedup":0.9, \\ "support":{"status":"within_run_repeated_curve", \\ "method":"within_run_experiment_counts", \\ "action":"validate_with_independent_runs","speedup_point_count":2, \\ "experiment_count":2,"baseline_experiment_count":1, \\ "minimum_experiments_per_point":1}, \\ "measurements":[ \\ {"virtual_speedup":0,"program_speedup":0,"experiment_count":1}, \\ {"virtual_speedup":0.5,"program_speedup":0.9,"experiment_count":1}] \\}]};test "profiling coz parses analysis results" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); try sys.fs.writeFile(".zig-cache/profile-coz-test.json", supported_analysis_fixture); const results = try load(allocator, "fixture.accy", ".zig-cache/profile-coz-test.json"); try std.testing.expectEqual(@as(usize, 2), results.len); try std.testing.expectEqualStrings("fixture.accy", results[0].workload); try std.testing.expectEqualStrings("src/eval.zig", results[0].file); try std.testing.expectEqual(@as(u64, 42), results[0].line); try std.testing.expectEqual(@as(f64, 0.35), results[0].max_program_speedup); try std.testing.expectEqual(@as(usize, 2), results[0].measurements.len); try std.testing.expectEqual(@as(f64, 0), results[0].measurements[0].virtual_speedup); try std.testing.expectEqual(@as(f64, 0.2), results[0].measurements[1].program_speedup); try std.testing.expectEqual(@as(u64, 2), results[0].measurements[0].experiment_count); try std.testing.expectEqual(SupportStatus.within_run_repeated_curve, results[0].support.status); try std.testing.expectEqual(SupportStatus.unreplicated_curve, results[1].support.status); try std.testing.expectEqual(@as(f64, 0.9), results[1].max_program_speedup); try std.testing.expectEqual( @as(f64, 0.35), bestSupportedProgramSpeedup(results, "fixture.accy").?, ); try std.testing.expect(bestSupportedProgramSpeedup(results, "other") == null); sys.fs.deleteFile(".zig-cache/profile-coz-test.json") catch {};}test "profiling coz rejects a repeated label without matching experiment counts" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); const path = ".zig-cache/profile-coz-inconsistent.json"; try sys.fs.writeFile(path, inconsistent_support_fixture); defer sys.fs.deleteFile(path) catch {}; try std.testing.expectEqual(@as(usize, 0), (try load(allocator, "w", path)).len);}test "profiling coz tolerates empty and missing analyses" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); try std.testing.expectEqual(@as(usize, 0), (try load(allocator, "w", null)).len); try std.testing.expectEqual(@as(usize, 0), (try load(allocator, "w", ".zig-cache/profile-coz-missing.json")).len); try sys.fs.writeFile(".zig-cache/profile-coz-empty.json", \\{"schema":"coz.analysis/v2","results":[]} ); try std.testing.expectEqual(@as(usize, 0), (try load(allocator, "w", ".zig-cache/profile-coz-empty.json")).len); try sys.fs.writeFile(".zig-cache/profile-coz-junk.json", "not json"); try std.testing.expectEqual(@as(usize, 0), (try load(allocator, "w", ".zig-cache/profile-coz-junk.json")).len); sys.fs.deleteFile(".zig-cache/profile-coz-empty.json") catch {}; sys.fs.deleteFile(".zig-cache/profile-coz-junk.json") catch {};}test "profiling coz maps complete and partial capture integrity" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.writeFile(fs_io, .{ .sub_path = "complete.json", .data = "{\"schema\":\"coz.analysis/v2\",\"capture_integrity\":{" ++ "\"status\":\"complete\",\"method\":\"audit\"," ++ "\"action\":\"use_profile\",\"message\":null},\"results\":[]}", }); try tmp.dir.writeFile(fs_io, .{ .sub_path = "partial.json", .data = "{\"schema\":\"coz.analysis/v2\",\"capture_integrity\":{" ++ "\"status\":\"sample_loss\",\"method\":\"audit\"," ++ "\"action\":\"rerun\",\"message\":\"repeat at a lower rate\"}," ++ "\"results\":[]}", }); const root = try tmp.dir.realPathFileAlloc(fs_io, ".", allocator); const complete_path = try std.fs.path.join(allocator, &.{ root, "complete.json" }); const partial_path = try std.fs.path.join(allocator, &.{ root, "partial.json" }); const complete = try appendCapture(allocator, &.{}, .{ .enabled = true, .exit_code = 0, .capture_path = "profile.jsonl", .summary_path = complete_path, }); const partial = try appendCapture(allocator, complete, .{ .enabled = true, .exit_code = 0, .capture_path = "profile.jsonl", .summary_path = partial_path, }); try std.testing.expectEqualStrings("summary_written", complete[0].state); try std.testing.expect(complete[0].caveat_kind == null); try std.testing.expectEqualStrings("sample_loss", partial[1].caveat_kind.?); try std.testing.expect(std.mem.indexOf( u8, partial[1].caveat_message.?, "lower rate", ) != null);}test "profiling coz marks missing and invalid capture summaries" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); try tmp.dir.writeFile(fs_io, .{ .sub_path = "invalid.json", .data = "{}" }); const root = try tmp.dir.realPathFileAlloc(fs_io, ".", allocator); const invalid_path = try std.fs.path.join(allocator, &.{ root, "invalid.json" }); const missing_path = try std.fs.path.join(allocator, &.{ root, "missing.json" }); const invalid = try appendCapture(allocator, &.{}, .{ .enabled = true, .exit_code = 0, .capture_path = "profile.jsonl", .summary_path = invalid_path, }); const missing = try appendCapture(allocator, &.{}, .{ .enabled = true, .exit_code = 0, .capture_path = "profile.jsonl", .summary_path = missing_path, }); try std.testing.expectEqualStrings("summary_invalid", invalid[0].state); try std.testing.expectEqualStrings("capture_missing", missing[0].state);}Source: src/profiling/capture/root.zig:1
zig
pub const coz = @import("coz.zig");Audit
| Definitions | 14 |
|---|---|
| Public names | 14 |
| Members | 18 |
| Version | 26.7.0 |
| Revision | daab053ee433 |