Skip to content

Commit 3f1a166

Browse files
committed
feat: add shared runner fifo struct
1 parent 1134895 commit 3f1a166

File tree

3 files changed

+174
-167
lines changed

3 files changed

+174
-167
lines changed

src/c.zig

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ const utils = @import("./utils.zig");
99
pub const panic = if (builtin.is_test) std.debug.FullPanic(std.debug.defaultPanic) else std.debug.no_panic;
1010
const allocator = if (builtin.is_test) std.testing.allocator else std.heap.c_allocator;
1111

12-
pub const PROTOCOL_VERSION: u64 = 1;
13-
1412
pub export fn instrument_hooks_set_feature(feature: u64, enabled: bool) void {
1513
const feature_enum = @as(features.Feature, @enumFromInt(feature));
1614
features.set_feature(feature_enum, enabled);
@@ -26,17 +24,6 @@ pub export fn instrument_hooks_init() ?*InstrumentHooks {
2624
return null;
2725
};
2826

29-
if (hooks.* == .perf) {
30-
hooks.perf.send_version(PROTOCOL_VERSION) catch {
31-
utils.print("[ERROR] instrument-hooks: failed to communicate with CodSpeed runner\n", .{});
32-
utils.print("[ERROR] instrument-hooks: please update the CodSpeed action to the latest version\n", .{});
33-
34-
hooks.deinit();
35-
allocator.destroy(hooks);
36-
std.posix.exit(1);
37-
};
38-
}
39-
4027
return hooks;
4128
}
4229

src/instruments/perf.zig

Lines changed: 2 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,6 @@
11
const std = @import("std");
22
const fifo = @import("../fifo.zig");
33
const shared = @import("../shared.zig");
4+
const runner_fifo = @import("../runner_fifo.zig");
45

5-
pub const PerfInstrument = struct {
6-
allocator: std.mem.Allocator,
7-
writer: fifo.UnixPipe.Writer,
8-
reader: fifo.UnixPipe.Reader,
9-
10-
const Self = @This();
11-
12-
pub fn init(allocator: std.mem.Allocator) !Self {
13-
return .{
14-
.allocator = allocator,
15-
.writer = try fifo.UnixPipe.openWrite(allocator, shared.RUNNER_CTL_FIFO),
16-
.reader = try fifo.UnixPipe.openRead(allocator, shared.RUNNER_ACK_FIFO),
17-
};
18-
}
19-
20-
pub fn deinit(self: *Self) void {
21-
self.writer.deinit();
22-
self.reader.deinit();
23-
}
24-
25-
pub fn send_cmd(self: *Self, cmd: fifo.Command) !void {
26-
try self.writer.sendCmd(cmd);
27-
try self.reader.waitForAck(null);
28-
}
29-
30-
pub fn is_instrumented(self: *Self) bool {
31-
self.send_cmd(fifo.Command.PingPerf) catch {
32-
return false;
33-
};
34-
35-
return true;
36-
}
37-
38-
pub noinline fn start_benchmark(self: *Self) !void {
39-
@branchHint(.cold); // Prevent inline
40-
41-
try self.writer.sendCmd(fifo.Command.StartBenchmark);
42-
try self.reader.waitForAck(null);
43-
}
44-
45-
pub noinline fn stop_benchmark(self: *Self) !void {
46-
@branchHint(.cold); // Prevent inline
47-
48-
try self.writer.sendCmd(fifo.Command.StopBenchmark);
49-
try self.reader.waitForAck(null);
50-
}
51-
52-
pub fn set_executed_benchmark(self: *Self, pid: u32, uri: [*c]const u8) !void {
53-
try self.writer.sendCmd(fifo.Command{ .ExecutedBenchmark = .{
54-
.pid = pid,
55-
.uri = std.mem.span(uri),
56-
} });
57-
try self.reader.waitForAck(null);
58-
}
59-
60-
pub fn set_integration(self: *Self, name: [*c]const u8, version: [*c]const u8) !void {
61-
try self.writer.sendCmd(fifo.Command{ .SetIntegration = .{
62-
.name = std.mem.span(name),
63-
.version = std.mem.span(version),
64-
} });
65-
try self.reader.waitForAck(null);
66-
}
67-
68-
pub fn add_marker(self: *Self, pid: u32, marker: shared.MarkerType) !void {
69-
try self.writer.sendCmd(fifo.Command{ .AddMarker = .{
70-
.pid = pid,
71-
.marker = marker,
72-
} });
73-
try self.reader.waitForAck(null);
74-
}
75-
76-
pub fn send_version(self: *Self, protocol_version: u64) !void {
77-
try self.writer.sendCmd(fifo.Command{ .SetVersion = protocol_version });
78-
try self.reader.waitForAck(null);
79-
}
80-
};
81-
82-
test "perf integration" {
83-
const allocator = std.testing.allocator;
84-
85-
try fifo.UnixPipe.create(shared.RUNNER_ACK_FIFO);
86-
try fifo.UnixPipe.create(shared.RUNNER_CTL_FIFO);
87-
88-
var ctl_fifo = try fifo.UnixPipe.openRead(allocator, shared.RUNNER_CTL_FIFO);
89-
defer ctl_fifo.deinit();
90-
91-
var ack_fifo = try fifo.UnixPipe.openWrite(allocator, shared.RUNNER_ACK_FIFO);
92-
defer ack_fifo.deinit();
93-
94-
const FifoTester = struct {
95-
allocator: std.mem.Allocator,
96-
ctl_pipe: *fifo.UnixPipe.Reader,
97-
ack_pipe: *fifo.UnixPipe.Writer,
98-
99-
received_cmd: ?fifo.Command = null,
100-
error_occurred: bool = false,
101-
102-
pub fn func(ctx: *@This()) void {
103-
const received_cmd = ctx.ctl_pipe.recvCmd() catch |err| {
104-
std.debug.print("Failed to receive command: {}\n", .{err});
105-
ctx.error_occurred = true;
106-
return;
107-
};
108-
ctx.received_cmd = received_cmd;
109-
110-
ctx.ack_pipe.sendCmd(fifo.Command.Ack) catch |err| {
111-
std.debug.print("Failed to send ACK: {}\n", .{err});
112-
ctx.error_occurred = true;
113-
};
114-
}
115-
116-
pub fn send(self: *@This(), comptime f: anytype, args: anytype) !fifo.Command {
117-
// 1. Create the thread which handles the command
118-
// 2. Execute the callback
119-
// 3. Wait for the thread to finish
120-
//
121-
const receiver_thread = try std.Thread.spawn(.{}, @This().func, .{self});
122-
try @call(.auto, f, args);
123-
receiver_thread.join();
124-
125-
if (self.error_occurred) {
126-
return error.IntegrationError;
127-
}
128-
self.error_occurred = false;
129-
130-
return self.received_cmd.?;
131-
}
132-
};
133-
134-
var tester = FifoTester{
135-
.allocator = allocator,
136-
.ctl_pipe = &ctl_fifo,
137-
.ack_pipe = &ack_fifo,
138-
};
139-
140-
var perf = try PerfInstrument.init(allocator);
141-
defer perf.deinit();
142-
143-
const si_result = try tester.send(PerfInstrument.set_integration, .{ &perf, "zig", "0.10.0" });
144-
try std.testing.expect(si_result.equal(fifo.Command{ .SetIntegration = .{ .name = "zig", .version = "0.10.0" } }));
145-
si_result.deinit(allocator);
146-
147-
const cb_result = try tester.send(PerfInstrument.set_executed_benchmark, .{ &perf, 42, "foo" });
148-
try std.testing.expect(cb_result.equal(fifo.Command{ .ExecutedBenchmark = .{ .pid = 42, .uri = "foo" } }));
149-
cb_result.deinit(allocator);
150-
151-
const start_result = try tester.send(PerfInstrument.start_benchmark, .{&perf});
152-
try std.testing.expect(start_result.equal(fifo.Command.StartBenchmark));
153-
start_result.deinit(allocator);
154-
155-
const stop_result = try tester.send(PerfInstrument.stop_benchmark, .{&perf});
156-
try std.testing.expect(stop_result.equal(fifo.Command.StopBenchmark));
157-
stop_result.deinit(allocator);
158-
}
6+
pub const PerfInstrument = runner_fifo.RunnerFifo;

src/runner_fifo.zig

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
const std = @import("std");
2+
const fifo = @import("fifo.zig");
3+
const shared = @import("shared.zig");
4+
5+
pub const PROTOCOL_VERSION: u64 = 1;
6+
7+
// Note: Using printf to avoid the extra code from std.log/std.debug. Those won't
8+
// compile because they are internally using syscalls (for Mutexes) which aren't cross-platform.
9+
extern "c" fn printf(format: [*c]const c_char, ...) c_int;
10+
11+
pub const RunnerFifo = struct {
12+
allocator: std.mem.Allocator,
13+
writer: fifo.UnixPipe.Writer,
14+
reader: fifo.UnixPipe.Reader,
15+
16+
const Self = @This();
17+
18+
pub fn init(allocator: std.mem.Allocator) !Self {
19+
return .{
20+
.allocator = allocator,
21+
.writer = try fifo.UnixPipe.openWrite(allocator, shared.RUNNER_CTL_FIFO),
22+
.reader = try fifo.UnixPipe.openRead(allocator, shared.RUNNER_ACK_FIFO),
23+
};
24+
}
25+
26+
pub fn validate_protocol_version(self: *Self) !void {
27+
self.send_version(PROTOCOL_VERSION) catch {
28+
_ = printf(@as([*c]const c_char, @ptrCast("[ERROR] instrument-hooks: failed to communicate with CodSpeed runner\n")));
29+
_ = printf(@as([*c]const c_char, @ptrCast("[ERROR] instrument-hooks: please update the CodSpeed action to the latest version\n")));
30+
std.posix.exit(1);
31+
};
32+
}
33+
34+
pub fn deinit(self: *Self) void {
35+
self.writer.deinit();
36+
self.reader.deinit();
37+
}
38+
39+
pub fn send_cmd(self: *Self, cmd: fifo.Command) !void {
40+
try self.writer.sendCmd(cmd);
41+
try self.reader.waitForAck(null);
42+
}
43+
44+
pub fn ping_perf(self: *Self) bool {
45+
self.send_cmd(fifo.Command.PingPerf) catch {
46+
return false;
47+
};
48+
49+
return true;
50+
}
51+
52+
pub noinline fn start_benchmark(self: *Self) !void {
53+
@branchHint(.cold); // Prevent inline
54+
55+
try self.writer.sendCmd(fifo.Command.StartBenchmark);
56+
try self.reader.waitForAck(null);
57+
}
58+
59+
pub noinline fn stop_benchmark(self: *Self) !void {
60+
@branchHint(.cold); // Prevent inline
61+
62+
try self.writer.sendCmd(fifo.Command.StopBenchmark);
63+
try self.reader.waitForAck(null);
64+
}
65+
66+
pub fn set_executed_benchmark(self: *Self, pid: u32, uri: [*c]const u8) !void {
67+
try self.writer.sendCmd(fifo.Command{ .ExecutedBenchmark = .{
68+
.pid = pid,
69+
.uri = std.mem.span(uri),
70+
} });
71+
try self.reader.waitForAck(null);
72+
}
73+
74+
pub fn set_integration(self: *Self, name: [*c]const u8, version: [*c]const u8) !void {
75+
try self.writer.sendCmd(fifo.Command{ .SetIntegration = .{
76+
.name = std.mem.span(name),
77+
.version = std.mem.span(version),
78+
} });
79+
try self.reader.waitForAck(null);
80+
}
81+
82+
pub fn add_marker(self: *Self, pid: u32, marker: shared.MarkerType) !void {
83+
try self.writer.sendCmd(fifo.Command{ .AddMarker = .{
84+
.pid = pid,
85+
.marker = marker,
86+
} });
87+
try self.reader.waitForAck(null);
88+
}
89+
90+
pub fn send_version(self: *Self, protocol_version: u64) !void {
91+
try self.writer.sendCmd(fifo.Command{ .SetVersion = protocol_version });
92+
try self.reader.waitForAck(null);
93+
}
94+
};
95+
96+
test "test runner fifo" {
97+
const allocator = std.testing.allocator;
98+
99+
try fifo.UnixPipe.create(shared.RUNNER_ACK_FIFO);
100+
try fifo.UnixPipe.create(shared.RUNNER_CTL_FIFO);
101+
102+
var ctl_fifo = try fifo.UnixPipe.openRead(allocator, shared.RUNNER_CTL_FIFO);
103+
defer ctl_fifo.deinit();
104+
105+
var ack_fifo = try fifo.UnixPipe.openWrite(allocator, shared.RUNNER_ACK_FIFO);
106+
defer ack_fifo.deinit();
107+
108+
const FifoTester = struct {
109+
allocator: std.mem.Allocator,
110+
ctl_pipe: *fifo.UnixPipe.Reader,
111+
ack_pipe: *fifo.UnixPipe.Writer,
112+
113+
received_cmd: ?fifo.Command = null,
114+
error_occurred: bool = false,
115+
116+
pub fn func(ctx: *@This()) void {
117+
const received_cmd = ctx.ctl_pipe.recvCmd() catch |err| {
118+
std.debug.print("Failed to receive command: {}\n", .{err});
119+
ctx.error_occurred = true;
120+
return;
121+
};
122+
ctx.received_cmd = received_cmd;
123+
124+
ctx.ack_pipe.sendCmd(fifo.Command.Ack) catch |err| {
125+
std.debug.print("Failed to send ACK: {}\n", .{err});
126+
ctx.error_occurred = true;
127+
};
128+
}
129+
130+
pub fn send(self: *@This(), comptime f: anytype, args: anytype) !fifo.Command {
131+
// 1. Create the thread which handles the command
132+
// 2. Execute the callback
133+
// 3. Wait for the thread to finish
134+
//
135+
const receiver_thread = try std.Thread.spawn(.{}, @This().func, .{self});
136+
try @call(.auto, f, args);
137+
receiver_thread.join();
138+
139+
if (self.error_occurred) {
140+
return error.IntegrationError;
141+
}
142+
self.error_occurred = false;
143+
144+
return self.received_cmd.?;
145+
}
146+
};
147+
148+
var tester = FifoTester{
149+
.allocator = allocator,
150+
.ctl_pipe = &ctl_fifo,
151+
.ack_pipe = &ack_fifo,
152+
};
153+
154+
var runner_fifo = try RunnerFifo.init(allocator);
155+
defer runner_fifo.deinit();
156+
157+
const si_result = try tester.send(RunnerFifo.set_integration, .{ &runner_fifo, "zig", "0.10.0" });
158+
try std.testing.expect(si_result.equal(fifo.Command{ .SetIntegration = .{ .name = "zig", .version = "0.10.0" } }));
159+
si_result.deinit(allocator);
160+
161+
const cb_result = try tester.send(RunnerFifo.set_executed_benchmark, .{ &runner_fifo, 42, "foo" });
162+
try std.testing.expect(cb_result.equal(fifo.Command{ .ExecutedBenchmark = .{ .pid = 42, .uri = "foo" } }));
163+
cb_result.deinit(allocator);
164+
165+
const start_result = try tester.send(RunnerFifo.start_benchmark, .{&runner_fifo});
166+
try std.testing.expect(start_result.equal(fifo.Command.StartBenchmark));
167+
start_result.deinit(allocator);
168+
169+
const stop_result = try tester.send(RunnerFifo.stop_benchmark, .{&runner_fifo});
170+
try std.testing.expect(stop_result.equal(fifo.Command.StopBenchmark));
171+
stop_result.deinit(allocator);
172+
}

0 commit comments

Comments
 (0)