Skip to documentation
SLOP

tiny.profiling.host.offcpu

Reference tiny.profiling host offcpu

Defined in host.

API (11)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

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

Source

Called byCallsNo direct callsoptionsparseRunArgshost.offcpu.Modeparse
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstest; no linksrc.profiling.host.offcputest: profiling offcpu classifies per...test; no linksrc.profiling.host.offcputest: profiling offcpu marks missing ...test; no linksrc.profiling.host.offcputest: profiling offcpu reports missin...test; no linksrc.profiling.host.offcputest: profiling offcpu retains rows a...test; no linksrc.profiling.host.offcputest: profiling offcpu summarizes cap...+5 morehost.offcpuplan
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest; no linksrc.profiling.host.offcputest: profiling offcpu classifies per...test; no linksrc.profiling.host.offcputest: profiling offcpu marks missing ...test; no linksrc.profiling.host.offcputest: profiling offcpu reports missin...test; no linksrc.profiling.host.offcputest: profiling offcpu retains rows a...test; no linksrc.profiling.host.offcputest: profiling offcpu summarizes cap...+2 moreprivate; no linksrc.profiling.host.offcpufailedprivate; no linksrc.profiling.host.offcpuloadBlockedStacksprivate; no linksrc.profiling.host.offcpuloadBlockedTimeprivate; no linksrc.profiling.host.offcpumissingToolprivate; no linksrc.profiling.host.offcpusummarizedhost.offcpusummarize
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsNo direct callstest; no linksrc.profiling.host.offcputest: profiling offcpu wraps direct b...test; no linksrc.profiling.host.offcputest: profiling offcpu wraps direct b...hostwraphost.offcpuwrapArgv
Static calls · unresolved targets: 0 · external targets: 2.

Source: src/profiling/host/offcpu.zig

zig
const std = @import("std");const capture = @import("capture");const sys = @import("sys");const root = @import("root.zig");pub const default_time_tool = "offcputime-bpfcc";pub const default_stacks_tool = "bpftrace";pub const kind = "offcpu_time";pub const Mode = enum {    time,    stacks,    pub fn parse(value: []const u8) ?Mode {        if (std.mem.eql(u8, value, "time")) return .time;        if (std.mem.eql(u8, value, "stacks")) return .stacks;        return null;    }};pub const Options = struct {    tool: ?[]const u8 = null,    mode: Mode = .time,    pub fn toolName(self: Options) []const u8 {        return self.tool orelse switch (self.mode) {            .time => default_time_tool,            .stacks => default_stacks_tool,        };    }};pub const Lane = struct {    tool: []const u8,    mode: Mode,    capture_path: []const u8,    diagnostics_path: []const u8,    summary_path: []const u8,};pub fn plan(allocator: std.mem.Allocator, options: Options, workload_root: []const u8) !Lane {    const capture_name = switch (options.mode) {        .time => "offcpu.time.txt",        .stacks => "offcpu.stacks.txt",    };    return .{        .tool = options.toolName(),        .mode = options.mode,        .capture_path = try std.fs.path.join(allocator, &.{ workload_root, capture_name }),        .diagnostics_path = try std.fs.path.join(            allocator,            &.{ workload_root, "offcpu.diagnostics.txt" },        ),        .summary_path = try std.fs.path.join(allocator, &.{ workload_root, "offcpu.summary.json" }),    };}pub fn wrapArgv(allocator: std.mem.Allocator, options: Options, argv: []const []const u8, lane: Lane) ![]const []const u8 {    _ = options;    switch (lane.mode) {        .time => return try capture.offcpu.timeCommand(            allocator,            lane.tool,            argv,            lane.capture_path,            lane.diagnostics_path,        ),        .stacks => {            const command = try std.mem.join(allocator, " ", argv);            return try capture.offcpu.stacksCommand(allocator, lane.tool, command, lane.capture_path);        },    }}pub fn summarize(    allocator: std.mem.Allocator,    lane: Lane,    exit_code: i64,    stderr_path: []const u8,    tool_available: bool,) !root.Capture {    if (!tool_available) return missingTool(lane);    var blocked = switch (lane.mode) {        .time => try loadBlockedTime(allocator, lane.capture_path),        .stacks => try loadBlockedStacks(allocator, lane.capture_path),    } orelse {        return try failed(allocator, lane, exit_code, stderr_path);    };    if (lane.mode == .time) {        const integrity = capture.offcpu.summarizeCollectorDiagnosticsFile(            allocator,            lane.diagnostics_path,        ) catch |err| switch (err) {            error.FileNotFound => capture.offcpu.missingCaptureIntegrity(),            else => return err,        };        blocked.diagnostics_path = lane.diagnostics_path;        blocked.capture_integrity = integrity;    }    try capture.offcpu.writeBlockedSummaryFile(lane.summary_path, blocked);    return summarized(lane, blocked.capture_integrity);}fn loadBlockedTime(    allocator: std.mem.Allocator,    capture_path: []const u8,) !?capture.offcpu.BlockedSummary {    const summary = capture.offcpu.summarizeTimeFile(        allocator,        capture_path,    ) catch |err| switch (err) {        error.FileNotFound => return null,        else => |actual| return actual,    };    if (summary.rows.len == 0) return null;    return try capture.offcpu.blockedFromTimeSummary(allocator, summary);}fn loadBlockedStacks(    allocator: std.mem.Allocator,    capture_path: []const u8,) !?capture.offcpu.BlockedSummary {    const summary = capture.offcpu.summarizeStacksFile(        allocator,        capture_path,    ) catch |err| switch (err) {        error.FileNotFound => return null,        else => |actual| return actual,    };    if (summary.rows.len == 0) return null;    return try capture.offcpu.blockedFromStackSummary(allocator, summary);}fn summarized(    lane: Lane,    integrity: ?capture.offcpu.CaptureIntegrity,) root.Capture {    var result: root.Capture = .{        .kind = kind,        .tool = lane.tool,        .capture_path = lane.capture_path,        .summary_path = lane.summary_path,        .state = "summary_written",    };    const actual = integrity orelse return result;    if (std.mem.eql(u8, actual.status, "complete")) return result;    result.caveat_kind = if (std.mem.eql(u8, actual.status, "partial"))        "offcpu_capture_partial"    else        "offcpu_capture_integrity_unknown";    result.caveat_message = actual.message;    return result;}fn missingTool(lane: Lane) root.Capture {    return .{        .kind = kind,        .tool = lane.tool,        .capture_path = lane.capture_path,        .summary_path = lane.summary_path,        .state = "tool_missing",        .caveat_kind = "tool_unavailable",        .caveat_message = "the off-CPU profiler is not runnable on this host; blocked time was not captured",    };}fn failed(allocator: std.mem.Allocator, lane: Lane, exit_code: i64, stderr_path: []const u8) !root.Capture {    const failure_path = try failureCapturePath(        allocator,        lane.diagnostics_path,        stderr_path,    );    const row = try capture.caveat.classifyCommandCapture(allocator, .{        .phase = kind,        .exit_code = if (exit_code == 0) 1 else exit_code,        .timed_out = false,        .capture_path = failure_path,    }) orelse capture.caveat.classifyCommandText(.{        .phase = kind,        .exit_code = exit_code,        .timed_out = false,        .capture_path = failure_path,    }, "");    return .{        .kind = kind,        .tool = lane.tool,        .capture_path = lane.capture_path,        .summary_path = lane.summary_path,        .state = "tool_failed",        .caveat_kind = row.kind,        .caveat_message = row.message,    };}fn failureCapturePath(    allocator: std.mem.Allocator,    diagnostics_path: []const u8,    stderr_path: []const u8,) ![]const u8 {    const diagnostics = sys.fs.readFileAlloc(        allocator,        diagnostics_path,        64 * 1024 * 1024,    ) catch |err| switch (err) {        error.OutOfMemory => return err,        else => return stderr_path,    };    defer allocator.free(diagnostics);    if (std.mem.trim(u8, diagnostics, " \t\r\n").len == 0) return stderr_path;    return diagnostics_path;}test "profiling offcpu wraps direct binaries in the offcputime sidecar" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const lane = try plan(allocator, .{}, "zig-out/profiling/run/workloads/w");    const argv = [_][]const u8{ "zig-out/bin/mprompt-bench", "--workers", "128" };    const wrapped = try wrapArgv(allocator, .{}, &argv, lane);    try std.testing.expectEqualStrings("bash", wrapped[0]);    try std.testing.expectEqualStrings("-c", wrapped[1]);    try std.testing.expectEqualStrings("offcputime-bpfcc", wrapped[4]);    try std.testing.expectEqualStrings("zig-out/profiling/run/workloads/w/offcpu.time.txt", wrapped[5]);    try std.testing.expectEqualStrings(        "zig-out/profiling/run/workloads/w/offcpu.diagnostics.txt",        wrapped[6],    );    try std.testing.expectEqualStrings("zig-out/bin/mprompt-bench", wrapped[7]);    try std.testing.expectEqualStrings("--workers", wrapped[8]);}test "profiling offcpu wraps direct binaries in the bpftrace stacks tracer" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const lane = try plan(allocator, .{ .mode = .stacks }, "zig-out/profiling/run/workloads/w");    try std.testing.expectEqualStrings("bpftrace", lane.tool);    const argv = [_][]const u8{ "zig-out/bin/mprompt-bench", "--workers", "128" };    const wrapped = try wrapArgv(allocator, .{ .mode = .stacks }, &argv, lane);    try std.testing.expectEqualStrings("bpftrace", wrapped[0]);    try std.testing.expectEqualStrings("zig-out/profiling/run/workloads/w/offcpu.stacks.txt", wrapped[3]);    try std.testing.expectEqualStrings("zig-out/bin/mprompt-bench --workers 128", wrapped[wrapped.len - 1]);}test "profiling offcpu summarizes captured blocked time" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{}, dir);    try sys.fs.writeFile(lane.capture_path,        \\bench-worker;futex_wait;do_futex 4200        \\bench-main;epoll_wait 1300        \\    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    try sys.fs.writeFile(lane.diagnostics_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expect(row.caveat_kind == null);    try std.testing.expect(sys.fs.exists(lane.summary_path));}test "profiling offcpu retains rows and caveats collector loss" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const dir = try std.fs.path.join(        allocator,        &.{ ".zig-cache", "tmp", tmp.sub_path[0..] },    );    const lane = try plan(allocator, .{}, dir);    try sys.fs.writeFile(lane.capture_path, "bench;futex_wait;do_futex 4200\n");    try sys.fs.writeFile(        lane.diagnostics_path,        "WARNING: 2 stack traces lost and could not be displayed.\n",    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "workload warning that is not collector evidence\n");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expectEqualStrings("offcpu_capture_partial", row.caveat_kind.?);    try std.testing.expect(std.mem.indexOf(        u8,        row.caveat_message.?,        "2 stack trace(s) lost",    ) != null);    const text = try sys.fs.readFileAlloc(allocator, lane.summary_path, 64 * 1024);    try std.testing.expect(std.mem.indexOf(u8, text, "\"lost_stack_trace_count\": 2") != null);    try std.testing.expect(std.mem.indexOf(u8, text, "workload warning") == null);}test "profiling offcpu marks missing collector diagnostics unknown" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const dir = try std.fs.path.join(        allocator,        &.{ ".zig-cache", "tmp", tmp.sub_path[0..] },    );    const lane = try plan(allocator, .{}, dir);    try sys.fs.writeFile(lane.capture_path, "bench;futex_wait;do_futex 4200\n");    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expectEqualStrings(        "offcpu_capture_integrity_unknown",        row.caveat_kind.?,    );}test "profiling offcpu summarizes captured blocked stacks" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{ .mode = .stacks }, dir);    try sys.fs.writeFile(lane.capture_path,        \\@offcpu_kernel[        \\    finish_task_switch+1        \\    schedule+108        \\]: 42        \\    );    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "");    const row = try summarize(allocator, lane, 0, stderr_path, true);    try std.testing.expectEqualStrings("summary_written", row.state);    try std.testing.expect(sys.fs.exists(lane.summary_path));}test "profiling offcpu classifies permission failures from stderr" {    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const dir = try std.fs.path.join(allocator, &.{ ".zig-cache", "tmp", tmp.sub_path[0..] });    const lane = try plan(allocator, .{}, dir);    const stderr_path = try std.fs.path.join(allocator, &.{ dir, "stderr.txt" });    try sys.fs.writeFile(stderr_path, "could not open bpf map: Operation not permitted\n");    const row = try summarize(allocator, lane, 1, stderr_path, true);    try std.testing.expectEqualStrings("tool_failed", row.state);    try std.testing.expectEqualStrings("permission_denied", row.caveat_kind.?);}test "profiling offcpu reports missing tools as caveats" {    var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena_state.deinit();    const allocator = arena_state.allocator();    const lane = try plan(allocator, .{}, "zig-out/profiling/run/workloads/w");    const row = try summarize(allocator, lane, 0, "missing-stderr.txt", false);    try std.testing.expectEqualStrings("tool_missing", row.state);    try std.testing.expectEqualStrings("tool_unavailable", row.caveat_kind.?);}

Source: src/profiling/host/root.zig:6

zig
pub const offcpu = runtime.offcpu;

Complete caller list for host.offcpu.plan

10 direct callers.

Complete caller list for host.offcpu.summarize

7 direct callers.

Audit

Definitions12
Public names12
Members9
Version26.7.0
Revisiondaab053ee433