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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69a172609924d64c58b36abca26df8fc707505724961ac16d18355441e1d56ff
4
- data.tar.gz: e567230d248ba2c79739efdaa3dab96ce302ace021df1639707ee0c7c11aeaa3
3
+ metadata.gz: 5b46fa6dd0b99e4a178bc00bad497beab5d72426517f3d7b328c29d492e9e8a1
4
+ data.tar.gz: a5f84fdc89d50621b3dd06eca22aea243efc1991ec053acece136016ccbdfb64
5
5
  SHA512:
6
- metadata.gz: 10a1ab7568b5c3798ab9e1e35c083bdcef1acabd08d730033283fbf971f9853154a6bff99c15b58b450a891b7bff450285d29c7e08067a2d63d523294f2a2dcd
7
- data.tar.gz: fd5ea7879da82d203f22c6351d45b2811a3d5101d0b7996ad205406131c546a5b86fc85d2644066223e79746bd6ccd34106a791b9e239b013ff2a3787840cc27
6
+ metadata.gz: 532f1d544b56d82e68d5931aac278bf36a850943c3e78e8851c59e56852ebe4f6a3d974fa6c129544f0c7f895696bd9b894f3d033490faff8c408bbd7deed424
7
+ data.tar.gz: 03de507c9094d850d77253610d2ffe08e2ee4ad97048e742f4c283ce6d8748de68f019a0e247ac826b5cbcbd48664a359ec57c92113f5f4015bc0c0f1984b482
data/Gemfile.lock CHANGED
@@ -8,7 +8,7 @@ GIT
8
8
  PATH
9
9
  remote: .
10
10
  specs:
11
- rbbcc (0.7.0)
11
+ rbbcc (0.8.0)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
@@ -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
@@ -16,6 +16,8 @@
16
16
  # Copyright 2016 Netflix, Inc.
17
17
  # Licensed under the Apache License, Version 2.0 (the "License")
18
18
 
19
+ # FIXME: random/urandom_read is removed from newer kernel!!
20
+
19
21
  require 'rbbcc'
20
22
  include RbBCC
21
23
 
@@ -11,6 +11,8 @@
11
11
  # Copyright 2016 Netflix, Inc.
12
12
  # Licensed under the Apache License, Version 2.0 (the "License")
13
13
 
14
+ # FIXME: random/urandom_read is removed from newer kernel!!
15
+
14
16
  require 'rbbcc'
15
17
  include RbBCC
16
18
 
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
@@ -1,3 +1,3 @@
1
1
  module RbBCC
2
- VERSION = "0.7.0"
2
+ VERSION = "0.8.0"
3
3
  end
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.7.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: 2022-08-10 00:00:00.000000000 Z
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