Something went wrong. Try again.
An eBPF-based profiler for process-lifecycle events.
zig ebpf profilers
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788const std = @import("std");const c = @cImport({ @cInclude("btf.h");});const print = std.debug.print;const assert = std.debug.assert;
var debug = false;
fn dprint(comptime fmt: []const u8, args: anytype) void { if (debug) { print(fmt, args); }}
// gen_vmlinux_offset_tests [-vmlinux/path/to/vmlinux] [-o/path/to/output_file] [-debug]pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit();
const gpa = arena.allocator();
var it = std.process.args(); _ = it.skip(); // skip process name var output: std.fs.File = std.fs.File.stdout(); var vmlinux_arg: ?[:0]const u8 = null; while (it.next()) |arg| { if (std.mem.startsWith(u8, arg, "-o")) { output = try std.fs.createFileAbsolute(arg["-o".len..], .{ .truncate = true }); } else if (std.mem.startsWith(u8, arg, "-vmlinux")) { vmlinux_arg = vmlinux_arg orelse arg["-vmlinux".len..]; } else if (std.mem.startsWith(u8, arg, "-debug")) { debug = true; } else { print("unknown argument: {s}\n", .{arg}); std.process.exit(1); } }
const btf = if (vmlinux_arg) |vmlinux| c.btf__parse(vmlinux, null) else c.btf__load_vmlinux_btf(); if (btf == null) { print("failed to get BTF: {}\n", .{std.posix.errno(-1)}); return error.PARSE; }
defer output.close(); var w = output.writer(&.{}); try w.interface.writeAll("const vmlinux = @import(\"vmlinux\");\n"); try w.interface.writeAll("const std = @import(\"std\");\n");
// Check structure size and non-bitfield offsets are consistent with btf for (1..c.btf__type_cnt(btf)) |i| { const t: *const c.btf_type = c.btf__type_by_id(btf, @intCast(i)); if (c.btf_kind(t) == c.BTF_KIND_STRUCT) { const struct_sz = t.unnamed_0.size; const m: [*c]const c.struct_btf_member = c.btf_members(t); const vlen: u16 = c.btf_vlen(t); const btf_name: [:0]const u8 = std.mem.sliceTo(c.btf__name_by_offset(btf, t.name_off), 0); const struct_name = if (btf_name.len != 0) try gpa.dupe(u8, btf_name) else try std.fmt.allocPrint(gpa, "struct_{d}", .{i});
const uniq_name = try std.fmt.allocPrint(gpa, "{s}__{d}", .{ struct_name, i });
try w.interface.print("test \"{s}\" {{\n", .{uniq_name}); try w.interface.print("const name = if (@hasDecl(vmlinux, \"{s}\")) \"{s}\" else \"{s}\";\n", .{ uniq_name, uniq_name, struct_name }); defer w.interface.writeAll("}\n") catch unreachable;
try w.interface.print("try std.testing.expectEqual({}, @sizeOf(@field(vmlinux, name)));\n", .{struct_sz}); for (0..vlen) |vi| { const m_sz = c.btf_member_bitfield_size(t, @intCast(vi)); const m_off = c.btf_member_bit_offset(t, @intCast(vi));
// TODO: skip bitfield right now if (m_sz != 0) continue;
const field_name = if (m[vi].name_off != 0) try gpa.dupe(u8, std.mem.sliceTo(c.btf__name_by_offset(btf, m[vi].name_off), 0)) else try std.fmt.allocPrint(gpa, "field{}", .{vi});
try w.interface.print("try std.testing.expectEqual({}, @bitOffsetOf(@field(vmlinux, name), \"{s}\"));\n", .{ m_off, field_name }); } } }}