Files

119 lines
3.6 KiB
Zig

const std = @import("std");
const TraceEvent = struct {
time: u64,
event: []const u8,
id: ?u64 = null,
node: ?u32 = null,
task: ?u64 = null,
op: ?[]const u8 = null,
resource: ?[]const u8 = null,
start: ?u64 = null,
end: ?u64 = null,
result: ?[]const u8 = null,
};
const Start = struct {
time: u64,
};
const Operation = struct {
id: u64,
start: u64,
end: u64,
duration: u64,
node: u32,
task: u64,
op: []const u8,
resource: []const u8,
result: []const u8,
};
pub fn renderFile(io: std.Io, gpa: std.mem.Allocator, trace_path: []const u8, output_path: []const u8) !void {
const file = try std.Io.Dir.cwd().createFile(io, output_path, .{});
defer file.close(io);
var writer = file.writerStreaming(io, &.{});
try render(io, gpa, trace_path, &writer.interface);
try writer.interface.flush();
}
pub fn render(io: std.Io, gpa: std.mem.Allocator, trace_path: []const u8, writer: *std.Io.Writer) !void {
const trace_bytes = try std.Io.Dir.cwd().readFileAlloc(io, trace_path, gpa, .limited(64 * 1024 * 1024));
defer gpa.free(trace_bytes);
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
var starts: std.AutoHashMap(u64, Start) = .init(arena.allocator());
var operations: std.ArrayList(Operation) = .empty;
var lines = std.mem.splitScalar(u8, trace_bytes, '\n');
while (lines.next()) |line| {
const trimmed = std.mem.trim(u8, line, " \t\r");
if (trimmed.len == 0) continue;
var parsed = try std.json.parseFromSlice(TraceEvent, arena.allocator(), trimmed, .{ .ignore_unknown_fields = true });
defer parsed.deinit();
const event = parsed.value;
if (std.mem.eql(u8, event.event, "io_start")) {
try starts.put(event.id.?, .{ .time = event.time });
continue;
}
if (std.mem.eql(u8, event.event, "io_complete")) {
const start_entry = starts.fetchRemove(event.id.?);
const start = event.start orelse if (start_entry) |entry| entry.value.time else event.time;
const end = event.end orelse event.time;
try operations.append(arena.allocator(), .{
.id = event.id.?,
.start = start,
.end = end,
.duration = end -| start,
.node = event.node.?,
.task = event.task.?,
.op = try arena.allocator().dupe(u8, event.op orelse ""),
.resource = try arena.allocator().dupe(u8, event.resource orelse ""),
.result = try arena.allocator().dupe(u8, event.result orelse ""),
});
}
}
sortOperations(operations.items);
for (operations.items) |operation| {
try writer.print(
"{d}.{d:0>3} n{d} t{d} {s} = {s}\n",
.{
operation.start / 1000,
operation.start % 1000,
operation.node,
operation.task,
operation.op,
operation.result,
},
);
}
}
fn sortOperations(operations: []Operation) void {
if (operations.len < 2) return;
var i: usize = 1;
while (i < operations.len) : (i += 1) {
const current = operations[i];
var j = i;
while (j > 0 and operationLess(current, operations[j - 1])) : (j -= 1) {
operations[j] = operations[j - 1];
}
operations[j] = current;
}
}
fn operationLess(a: Operation, b: Operation) bool {
if (a.start != b.start) return a.start < b.start;
return a.id < b.id;
}