rbbcc 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/examples/hello_ring_buffer.rb +64 -0
- data/examples/syscalluname.rb +39 -0
- data/examples/table.rb +15 -0
- data/examples/urandomread-explicit.rb +2 -0
- data/examples/urandomread.rb +2 -0
- data/lib/rbbcc/bcc.rb +16 -0
- data/lib/rbbcc/clib.rb +5 -0
- data/lib/rbbcc/table.rb +115 -54
- data/lib/rbbcc/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b46fa6dd0b99e4a178bc00bad497beab5d72426517f3d7b328c29d492e9e8a1
|
4
|
+
data.tar.gz: a5f84fdc89d50621b3dd06eca22aea243efc1991ec053acece136016ccbdfb64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 532f1d544b56d82e68d5931aac278bf36a850943c3e78e8851c59e56852ebe4f6a3d974fa6c129544f0c7f895696bd9b894f3d033490faff8c408bbd7deed424
|
7
|
+
data.tar.gz: 03de507c9094d850d77253610d2ffe08e2ee4ad97048e742f4c283ce6d8748de68f019a0e247ac826b5cbcbd48664a359ec57c92113f5f4015bc0c0f1984b482
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This is a Hello World example that uses BPF_PERF_OUTPUT.
|
4
|
+
# Ported from hello_perf_output.py
|
5
|
+
|
6
|
+
require 'rbbcc'
|
7
|
+
include RbBCC
|
8
|
+
|
9
|
+
# define BPF program
|
10
|
+
prog = """
|
11
|
+
#include <linux/sched.h>
|
12
|
+
|
13
|
+
struct data_t {
|
14
|
+
u32 pid;
|
15
|
+
u64 ts;
|
16
|
+
char comm[TASK_COMM_LEN];
|
17
|
+
};
|
18
|
+
BPF_RINGBUF_OUTPUT(buffer, 1 << 4);
|
19
|
+
|
20
|
+
int hello(struct pt_regs *ctx) {
|
21
|
+
struct data_t data = {};
|
22
|
+
|
23
|
+
data.pid = bpf_get_current_pid_tgid();
|
24
|
+
data.ts = bpf_ktime_get_ns();
|
25
|
+
bpf_get_current_comm(&data.comm, sizeof(data.comm));
|
26
|
+
|
27
|
+
buffer.ringbuf_output(&data, sizeof(data), 0);
|
28
|
+
|
29
|
+
return 0;
|
30
|
+
}
|
31
|
+
"""
|
32
|
+
|
33
|
+
# load BPF program
|
34
|
+
b = BCC.new(text: prog)
|
35
|
+
b.attach_kprobe(event: b.get_syscall_fnname("clone"), fn_name: "hello")
|
36
|
+
|
37
|
+
# header
|
38
|
+
puts("%-18s %-16s %-6s %s" % ["TIME(s)", "COMM", "PID", "MESSAGE"])
|
39
|
+
|
40
|
+
# process event
|
41
|
+
start = 0
|
42
|
+
print_event = lambda { |ctx, data, size|
|
43
|
+
event = b["buffer"].event(data)
|
44
|
+
if start == 0
|
45
|
+
start = event.ts
|
46
|
+
end
|
47
|
+
|
48
|
+
time_s = ((event.ts - start).to_f) / 1000000000
|
49
|
+
# event.comm.pack("c*").sprit
|
50
|
+
puts("%-18.9f %-16s %-6d %s" % [time_s, event.comm, event.pid,
|
51
|
+
"Hello, ringbuf!"])
|
52
|
+
}
|
53
|
+
|
54
|
+
# loop with callback to print_event
|
55
|
+
b["buffer"].open_ring_buffer(&print_event)
|
56
|
+
|
57
|
+
loop do
|
58
|
+
begin
|
59
|
+
b.ring_buffer_poll()
|
60
|
+
sleep(0.5)
|
61
|
+
rescue Interrupt
|
62
|
+
exit()
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# syscalluname.rb Example of instrumenting a kernel tracepoint.
|
4
|
+
#
|
5
|
+
# Copyright 2024 Uchio Kondo
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License")
|
7
|
+
|
8
|
+
require 'rbbcc'
|
9
|
+
include RbBCC
|
10
|
+
|
11
|
+
b = BCC.new(text: %[
|
12
|
+
#include <linux/utsname.h>
|
13
|
+
|
14
|
+
TRACEPOINT_PROBE(syscalls, sys_enter_newuname) {
|
15
|
+
// args is from
|
16
|
+
// /sys/kernel/debug/tracing/events/syscalls/sys_enter_newuname/format
|
17
|
+
char release[16];
|
18
|
+
bpf_probe_read_user_str(release, 16, args->name->release);
|
19
|
+
// avoid broken data
|
20
|
+
if (release[0] == '5' || release[0] == '6') {
|
21
|
+
bpf_trace_printk("%s\\n", release);
|
22
|
+
}
|
23
|
+
return 0;
|
24
|
+
}
|
25
|
+
])
|
26
|
+
|
27
|
+
# header
|
28
|
+
printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "RELEASE")
|
29
|
+
|
30
|
+
# format output
|
31
|
+
loop do
|
32
|
+
begin
|
33
|
+
b.trace_fields do |task, pid, cpu, flags, ts, msg|
|
34
|
+
puts("%-18.9f %-16s %-6d %s" % [ts, task, pid, msg])
|
35
|
+
end
|
36
|
+
rescue Interrupt
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
end
|
data/examples/table.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rbbcc'
|
4
|
+
include RbBCC
|
5
|
+
|
6
|
+
b = BCC.new(text: <<CLANG)
|
7
|
+
BPF_HASH(the_table_name, int);
|
8
|
+
CLANG
|
9
|
+
|
10
|
+
table = b.get_table('the_table_name', leaftype: 'int')
|
11
|
+
|
12
|
+
table[10] = 1
|
13
|
+
table[20] = 2
|
14
|
+
puts table[10].to_bcc_value == 1
|
15
|
+
puts table[20].to_bcc_value == 2
|
data/examples/urandomread.rb
CHANGED
data/lib/rbbcc/bcc.rb
CHANGED
@@ -262,6 +262,7 @@ module RbBCC
|
|
262
262
|
@funcs = {}
|
263
263
|
@tables = {}
|
264
264
|
@perf_buffers = {}
|
265
|
+
@_ringbuf_manager = nil
|
265
266
|
|
266
267
|
unless @module
|
267
268
|
raise "BPF module not created"
|
@@ -555,6 +556,21 @@ module RbBCC
|
|
555
556
|
Clib.perf_reader_poll(readers.size, pack, timeout)
|
556
557
|
end
|
557
558
|
|
559
|
+
def _open_ring_buffer(map_fd, fn, ctx)
|
560
|
+
buf = Clib.bpf_new_ringbuf(map_fd, fn, ctx)
|
561
|
+
if !buf
|
562
|
+
raise "Could not open ring buffer"
|
563
|
+
end
|
564
|
+
@_ringbuf_manager ||= buf
|
565
|
+
end
|
566
|
+
|
567
|
+
def ring_buffer_poll(timeout=-1)
|
568
|
+
unless @_ringbuf_manager
|
569
|
+
raise "No ring buffers to poll"
|
570
|
+
end
|
571
|
+
Clib.bpf_poll_ringbuf(@_ringbuf_manager, timeout)
|
572
|
+
end
|
573
|
+
|
558
574
|
def ksymname(name)
|
559
575
|
SymbolCache.resolve_global(name)
|
560
576
|
end
|
data/lib/rbbcc/clib.rb
CHANGED
@@ -133,6 +133,11 @@ module RbBCC
|
|
133
133
|
extern 'size_t bpf_perf_event_fields(void *program, const char *event)'
|
134
134
|
extern 'char * bpf_perf_event_field(void *program, const char *event, size_t i)'
|
135
135
|
|
136
|
+
# typedef int (*ring_buffer_sample_fn)(void *ctx, void *data, size_t size);
|
137
|
+
extern 'void * bpf_new_ringbuf(int map_fd, void *sample_cb, void *ctx)'
|
138
|
+
extern 'int bpf_poll_ringbuf(void *rb, int timeout_ms)'
|
139
|
+
extern 'void bpf_free_ringbuf(void *rb)'
|
140
|
+
|
136
141
|
extern 'void * bcc_usdt_new_frompid(int, char *)'
|
137
142
|
extern 'void * bcc_usdt_new_frompath(char *path)'
|
138
143
|
extern 'int bcc_usdt_enable_probe(void *, char *, char *)'
|
data/lib/rbbcc/table.rb
CHANGED
@@ -5,6 +5,61 @@ require 'rbbcc/disp_helper'
|
|
5
5
|
require 'rbbcc/cpu_helper'
|
6
6
|
|
7
7
|
module RbBCC
|
8
|
+
module EventTypeSupported
|
9
|
+
def get_event_class
|
10
|
+
ct_mapping = {
|
11
|
+
's8': 'char',
|
12
|
+
'u8': 'unsined char',
|
13
|
+
's8 *': 'char *',
|
14
|
+
's16': 'short',
|
15
|
+
'u16': 'unsigned short',
|
16
|
+
's32': 'int',
|
17
|
+
'u32': 'unsigned int',
|
18
|
+
's64': 'long long',
|
19
|
+
'u64': 'unsigned long long'
|
20
|
+
}
|
21
|
+
|
22
|
+
array_type = /(.+) \[([0-9]+)\]$/
|
23
|
+
fields = []
|
24
|
+
num_fields = Clib.bpf_perf_event_fields(self.bpf.module, @name)
|
25
|
+
num_fields.times do |i|
|
26
|
+
field = Clib.__extract_char(Clib.bpf_perf_event_field(self.bpf.module, @name, i))
|
27
|
+
field_name, field_type = *field.split('#')
|
28
|
+
if field_type =~ /enum .*/
|
29
|
+
field_type = "int" #it is indeed enum...
|
30
|
+
end
|
31
|
+
if _field_type = ct_mapping[field_type.to_sym]
|
32
|
+
field_type = _field_type
|
33
|
+
end
|
34
|
+
|
35
|
+
m = array_type.match(field_type)
|
36
|
+
if m
|
37
|
+
field_type = "#{m[1]}[#{m[2]}]"
|
38
|
+
fields << [field_type, field_name].join(" ")
|
39
|
+
else
|
40
|
+
fields << [field_type, field_name].join(" ")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
klass = Fiddle::Importer.struct(fields)
|
44
|
+
char_ps = fields.select {|f| f =~ /^char\[(\d+)\] ([_a-zA-Z0-9]+)/ }
|
45
|
+
unless char_ps.empty?
|
46
|
+
m = Module.new do
|
47
|
+
char_ps.each do |char_p|
|
48
|
+
md = /^char\[(\d+)\] ([_a-zA-Z0-9]+)/.match(char_p)
|
49
|
+
define_method md[2] do
|
50
|
+
# Split the char[] in the place where the first \0 appears
|
51
|
+
raw = super()
|
52
|
+
raw = raw[0...raw.index(0)] if raw.index(0)
|
53
|
+
raw.pack("c*")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
klass.prepend m
|
58
|
+
end
|
59
|
+
klass
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
8
63
|
module Table
|
9
64
|
BPF_MAP_TYPE_HASH = 1
|
10
65
|
BPF_MAP_TYPE_ARRAY = 2
|
@@ -24,6 +79,17 @@ module RbBCC
|
|
24
79
|
BPF_MAP_TYPE_CPUMAP = 16
|
25
80
|
BPF_MAP_TYPE_XSKMAP = 17
|
26
81
|
BPF_MAP_TYPE_SOCKHASH = 18
|
82
|
+
BPF_MAP_TYPE_CGROUP_STORAGE = 19
|
83
|
+
BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20
|
84
|
+
BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21
|
85
|
+
BPF_MAP_TYPE_QUEUE = 22
|
86
|
+
BPF_MAP_TYPE_STACK = 23
|
87
|
+
BPF_MAP_TYPE_SK_STORAGE = 24
|
88
|
+
BPF_MAP_TYPE_DEVMAP_HASH = 25
|
89
|
+
BPF_MAP_TYPE_STRUCT_OPS = 26
|
90
|
+
BPF_MAP_TYPE_RINGBUF = 27
|
91
|
+
BPF_MAP_TYPE_INODE_STORAGE = 28
|
92
|
+
BPF_MAP_TYPE_TASK_STORAGE = 29
|
27
93
|
|
28
94
|
def self.new(bpf, map_id, map_fd, keytype, leaftype, name, **kwargs)
|
29
95
|
ttype = Clib.bpf_table_type_id(bpf.module, map_id)
|
@@ -34,6 +100,8 @@ module RbBCC
|
|
34
100
|
ArrayTable.new(bpf, map_id, map_fd, keytype, leaftype)
|
35
101
|
when BPF_MAP_TYPE_PERF_EVENT_ARRAY
|
36
102
|
PerfEventArray.new(bpf, map_id, map_fd, keytype, leaftype, name: name)
|
103
|
+
when BPF_MAP_TYPE_RINGBUF
|
104
|
+
RingBuf.new(bpf, map_id, map_fd, keytype, leaftype, name: name)
|
37
105
|
when BPF_MAP_TYPE_STACK_TRACE
|
38
106
|
StackTrace.new(bpf, map_id, map_fd, keytype, leaftype)
|
39
107
|
else
|
@@ -260,6 +328,8 @@ module RbBCC
|
|
260
328
|
end
|
261
329
|
|
262
330
|
class PerfEventArray < TableBase
|
331
|
+
include EventTypeSupported
|
332
|
+
|
263
333
|
def initialize(bpf, map_id, map_fd, keytype, leaftype, name: nil)
|
264
334
|
super
|
265
335
|
@open_key_fds = {}
|
@@ -285,60 +355,6 @@ module RbBCC
|
|
285
355
|
end
|
286
356
|
end
|
287
357
|
|
288
|
-
private
|
289
|
-
def get_event_class
|
290
|
-
ct_mapping = {
|
291
|
-
's8': 'char',
|
292
|
-
'u8': 'unsined char',
|
293
|
-
's8 *': 'char *',
|
294
|
-
's16': 'short',
|
295
|
-
'u16': 'unsigned short',
|
296
|
-
's32': 'int',
|
297
|
-
'u32': 'unsigned int',
|
298
|
-
's64': 'long long',
|
299
|
-
'u64': 'unsigned long long'
|
300
|
-
}
|
301
|
-
|
302
|
-
array_type = /(.+) \[([0-9]+)\]$/
|
303
|
-
fields = []
|
304
|
-
num_fields = Clib.bpf_perf_event_fields(self.bpf.module, @name)
|
305
|
-
num_fields.times do |i|
|
306
|
-
field = Clib.__extract_char(Clib.bpf_perf_event_field(self.bpf.module, @name, i))
|
307
|
-
field_name, field_type = *field.split('#')
|
308
|
-
if field_type =~ /enum .*/
|
309
|
-
field_type = "int" #it is indeed enum...
|
310
|
-
end
|
311
|
-
if _field_type = ct_mapping[field_type.to_sym]
|
312
|
-
field_type = _field_type
|
313
|
-
end
|
314
|
-
|
315
|
-
m = array_type.match(field_type)
|
316
|
-
if m
|
317
|
-
field_type = "#{m[1]}[#{m[2]}]"
|
318
|
-
fields << [field_type, field_name].join(" ")
|
319
|
-
else
|
320
|
-
fields << [field_type, field_name].join(" ")
|
321
|
-
end
|
322
|
-
end
|
323
|
-
klass = Fiddle::Importer.struct(fields)
|
324
|
-
char_ps = fields.select {|f| f =~ /^char\[(\d+)\] ([_a-zA-Z0-9]+)/ }
|
325
|
-
unless char_ps.empty?
|
326
|
-
m = Module.new do
|
327
|
-
char_ps.each do |char_p|
|
328
|
-
md = /^char\[(\d+)\] ([_a-zA-Z0-9]+)/.match(char_p)
|
329
|
-
define_method md[2] do
|
330
|
-
# Split the char[] in the place where the first \0 appears
|
331
|
-
raw = super()
|
332
|
-
raw = raw[0...raw.index(0)] if raw.index(0)
|
333
|
-
raw.pack("c*")
|
334
|
-
end
|
335
|
-
end
|
336
|
-
end
|
337
|
-
klass.prepend m
|
338
|
-
end
|
339
|
-
klass
|
340
|
-
end
|
341
|
-
|
342
358
|
def _open_perf_buffer(cpu, callback, page_cnt, lost_cb)
|
343
359
|
# bind("void raw_cb_callback(void *, void *, int)")
|
344
360
|
fn = Fiddle::Closure::BlockCaller.new(
|
@@ -382,6 +398,51 @@ module RbBCC
|
|
382
398
|
end
|
383
399
|
end
|
384
400
|
|
401
|
+
class RingBuf < TableBase
|
402
|
+
include EventTypeSupported
|
403
|
+
|
404
|
+
def initialize(bpf, map_id, map_fd, keytype, leaftype, name: nil)
|
405
|
+
super
|
406
|
+
@_ringbuf = nil
|
407
|
+
@_ringbuf_manager = nil
|
408
|
+
@event_class = nil
|
409
|
+
end
|
410
|
+
|
411
|
+
def event(data)
|
412
|
+
@event_class ||= get_event_class
|
413
|
+
ev = @event_class.malloc
|
414
|
+
Fiddle::Pointer.new(ev.to_ptr)[0, @event_class.size] = data[0, @event_class.size]
|
415
|
+
return ev
|
416
|
+
end
|
417
|
+
|
418
|
+
def open_ring_buffer(ctx=nil, &callback)
|
419
|
+
# bind("int ring_buffer_sample_fn(void *, void *, int)")
|
420
|
+
fn = Fiddle::Closure::BlockCaller.new(
|
421
|
+
Fiddle::TYPE_INT,
|
422
|
+
[Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT]
|
423
|
+
) do |_dummy, data, size|
|
424
|
+
begin
|
425
|
+
_ret = callback.call(ctx, data, size)
|
426
|
+
ret = _ret.to_i
|
427
|
+
ret
|
428
|
+
rescue NoMethodError
|
429
|
+
# Callback for ringbufs should _always_ return an integer.
|
430
|
+
# simply fall back to returning 0 when failed
|
431
|
+
0
|
432
|
+
rescue => e
|
433
|
+
if Fiddle.last_error == 32 # EPIPE
|
434
|
+
exit
|
435
|
+
else
|
436
|
+
raise e
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
@bpf._open_ring_buffer(@map_fd, fn, ctx)
|
442
|
+
nil
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
385
446
|
class StackTrace < TableBase
|
386
447
|
MAX_DEPTH = 127
|
387
448
|
BPF_F_STACK_BUILD_ID = (1<<5)
|
data/lib/rbbcc/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbcc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uchio Kondo
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: BCC port for MRI. See https://github.com/iovisor/bcc
|
14
14
|
email:
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- examples/extract_arg.rb
|
73
73
|
- examples/hello_fields.rb
|
74
74
|
- examples/hello_perf_output.rb
|
75
|
+
- examples/hello_ring_buffer.rb
|
75
76
|
- examples/hello_world.rb
|
76
77
|
- examples/kvm_hypercall.rb
|
77
78
|
- examples/mallocstack.rb
|
@@ -79,6 +80,8 @@ files:
|
|
79
80
|
- examples/networking/http_filter/http-parse-simple.rb
|
80
81
|
- examples/ruby_usdt.rb
|
81
82
|
- examples/sbrk_trace.rb
|
83
|
+
- examples/syscalluname.rb
|
84
|
+
- examples/table.rb
|
82
85
|
- examples/tools/bashreadline.rb
|
83
86
|
- examples/tools/execsnoop.rb
|
84
87
|
- examples/tools/runqlat.rb
|