Skip to content

Commit 3aa39b1

Browse files
committed
what is examples
1 parent 66efccb commit 3aa39b1

File tree

5 files changed

+66
-19
lines changed

5 files changed

+66
-19
lines changed

README.md

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,36 @@
11
Eiri
22
=======
33

4-
Minimal self-contained eBPF/uprobe based tracer.
4+
Minimal self-contained eBPF/kprobe based tracer.
55

6-
Work in progress. Currently can attach to USDT probes in ELF files and run
6+
Work in progress. Currently can attach to kprobes as well as USDT probes in ELF files and run
77
simple eBPF programs defined in a custom IR format. ringbuf output gets printed as raw bytes to stderr.
88

9+
Very simple output is supported. in form of a single count (stored as a bpf array map) or dumping byte values in a bpf ringbuf map
10+
11+
Examples
12+
-----
13+
14+
use array map as simple counter:
15+
```
16+
map $count array 4 8 1
17+
18+
func $main MIT
19+
%key = alloc
20+
store [%key] 0
21+
%map = map $count
22+
%aa = call map_lookup_elem %map %key
23+
eq %aa 0 :exit
24+
:doit
25+
xadd [%aa] 1
26+
:exit
27+
ret 0
28+
end
29+
30+
attach $main kprobe __x64_sys_write
31+
```
32+
33+
dump userland stack into ringbuf:
934
```
1035
map $ringbuf ringbuf 0 0 4096
1136
@@ -23,5 +48,5 @@ end
2348
2449
elf $neovim /home/bfredl/dev/neovim/build/bin/nvim
2550
26-
attach $neovim flushy $main
51+
attach usdt $neovim xfree
2752
```

count.ir

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
map $count array 4 8 1
22

3-
elf $neovim /home/bfredl/dev/neovim/build/bin/nvim
4-
53
func $main MIT
64
%key = alloc
75
store [%key] 0
@@ -14,4 +12,4 @@ func $main MIT
1412
ret 0
1513
end
1614

17-
attach $main usdt $neovim xfree
15+
attach $main kprobe __x64_sys_write

src/Parser.zig

+12-1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ pub fn toplevel(self: *Self, exec: bool) !void {
219219
fn get_probe(self: *Self, exec: bool) !fd_t {
220220
_ = self.nonws();
221221
const kind = try self.identifier();
222+
if (mem.eql(u8, kind, "kprobe")) {
223+
_ = self.nonws();
224+
const func = try self.identifier();
225+
_ = self.nonws();
226+
const offset = self.num() orelse 0;
227+
228+
if (!exec) return 55;
229+
// TODO: share this, like a non-savage
230+
const kprobe_type = try bpfUtil.getKprobeType();
231+
return bpfUtil.perf_open_probe_cstr(kprobe_type, func, offset);
232+
}
222233
if (mem.eql(u8, kind, "usdt")) {
223234
const elf_name = try require(try self.objname(), "elf name");
224235
_ = self.nonws();
@@ -229,7 +240,7 @@ fn get_probe(self: *Self, exec: bool) !fd_t {
229240
if (!exec) return 55;
230241
// TODO: share this, like a non-savage
231242
const uprobe_type = try bpfUtil.getUprobeType();
232-
return bpfUtil.perf_open_uprobe(uprobe_type, elf.fname, sdt.h.pc);
243+
return bpfUtil.perf_open_probe_cstr(uprobe_type, elf.fname, sdt.h.pc);
233244
} else {
234245
return error.ParseError;
235246
}

src/bpfUtil.zig

+24-11
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,20 @@ pub fn perf_attach_bpf(target: fd_t, prog: fd_t) !void {
6767
}
6868
}
6969

70-
pub fn perf_open_uprobe(uprobe_type: u32, uprobe_path: []const u8, uprobe_offset: u64) !fd_t {
71-
// TODO: .size should be the default (stage2 bug)
70+
pub fn perf_open_probe(probe_type: u32, config1: u64, config2: u64) !fd_t {
7271
var attr = linux.perf_event_attr{};
7372

7473
// TODO: use /sys/devices/system/cpu/online
7574
// but not needed for uprobe/kprobe???
7675

7776
// the type value is dynamic and might be outside the defined values of
7877
// PERF.TYPE. praxis or zig std correctness issue
79-
attr.type = @intToEnum(PERF.TYPE, uprobe_type);
78+
attr.type = @intToEnum(PERF.TYPE, probe_type);
8079
attr.sample_period_or_freq = 1;
8180
attr.wakeup_events_or_watermark = 1;
82-
var path_buf: [512]u8 = undefined;
83-
if (uprobe_path.len > 511) return error.InvalidProgram;
84-
mem.copy(u8, &path_buf, uprobe_path);
85-
path_buf[uprobe_path.len] = 0;
8681

87-
attr.config1 = @ptrToInt(&path_buf);
88-
attr.config2 = uprobe_offset;
82+
attr.config1 = config1;
83+
attr.config2 = config2;
8984

9085
const rc = linux.perf_event_open(&attr, -1, 0, -1, 0);
9186
return switch (errno(rc)) {
@@ -98,8 +93,18 @@ pub fn perf_open_uprobe(uprobe_type: u32, uprobe_path: []const u8, uprobe_offset
9893
};
9994
}
10095

101-
pub fn getUprobeType() !u32 {
102-
const fil = try std.fs.openFileAbsolute("/sys/bus/event_source/devices/uprobe/type", .{});
96+
// makes a null-terminated copy of config1, maximum size: 511 bytes (+ added nul byte)
97+
pub fn perf_open_probe_cstr(uprobe_type: u32, config1: []const u8, config2: u64) !fd_t {
98+
var config1_buf: [512]u8 = undefined;
99+
if (config1.len > 511) return error.InvalidProgram;
100+
mem.copy(u8, &config1_buf, config1);
101+
config1_buf[config1.len] = 0;
102+
103+
return perf_open_probe(uprobe_type, @ptrToInt(&config1_buf), config2);
104+
}
105+
106+
pub fn getProbeTypeFromPath(path: []const u8) !u32 {
107+
const fil = try std.fs.openFileAbsolute(path, .{});
103108
defer fil.close();
104109

105110
const reader = fil.reader();
@@ -108,6 +113,14 @@ pub fn getUprobeType() !u32 {
108113
return std.fmt.parseInt(u32, line, 10);
109114
}
110115

116+
pub fn getKprobeType() !u32 {
117+
return getProbeTypeFromPath("/sys/bus/event_source/devices/kprobe/type");
118+
}
119+
120+
pub fn getUprobeType() !u32 {
121+
return getProbeTypeFromPath("/sys/bus/event_source/devices/uprobe/type");
122+
}
123+
111124
pub const pt_regs_amd64 = enum(u8) {
112125
r15,
113126
r14,

stack.ir

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ func $main GPL
1212
ret 0
1313
end
1414

15-
elf $neovim /home/bfredl/dev/neovim/build/bin/nvim
1615

16+
elf $neovim /home/bfredl/dev/neovim/build/bin/nvim
1717
attach $main usdt $neovim xfree

0 commit comments

Comments
 (0)