rbbcc 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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