rbbcc 0.7.0 → 0.9.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: 5cd5d45a97213b496575bcc8bd01fdda9077598efa59ef841aa7724b15189418
4
+ data.tar.gz: 9abd5b2278de91c67daa9d90e8ae85e8ce662b2213474633120cd12fb7c36ffc
5
5
  SHA512:
6
- metadata.gz: 10a1ab7568b5c3798ab9e1e35c083bdcef1acabd08d730033283fbf971f9853154a6bff99c15b58b450a891b7bff450285d29c7e08067a2d63d523294f2a2dcd
7
- data.tar.gz: fd5ea7879da82d203f22c6351d45b2811a3d5101d0b7996ad205406131c546a5b86fc85d2644066223e79746bd6ccd34106a791b9e239b013ff2a3787840cc27
6
+ metadata.gz: 8479707a606688b8f4ff57b854127cf383764bd0fe2bbfefbfcd51bea4108ef799f768a6f4b1e551c283a6315a3ff22d76cfc58e23d874187895fb76bb0d1e58
7
+ data.tar.gz: 630c85668e1112f4957441f403d84128514eab1c157cd7d54d6c773c40cf63e22f4a7ad73c1dc2576ca02df1de75f9bf06a2393376b77b3c785baa3290c6688d
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.9.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
@@ -231,37 +231,35 @@ module RbBCC
231
231
  hdr_file = BCC._find_file(hdr_file)
232
232
  end
233
233
 
234
- if src_file && src_file.end_with?(".b")
235
- @module = Clib.bpf_module_create_b(src_file, hdr_file, debug, dev_name)
236
- else
237
- if src_file
238
- text = File.read(src_file)
239
- end
234
+ if src_file
235
+ text = File.read(src_file)
236
+ end
240
237
 
241
- @usdt_contexts = usdt_contexts
242
- if code = gen_args_from_usdt
243
- text = code + text
244
- end
238
+ @usdt_contexts = usdt_contexts
239
+ if code = gen_args_from_usdt
240
+ text = code + text
241
+ end
245
242
 
246
243
 
247
- cflags_safe = if cflags.empty? or !cflags[-1].nil?
248
- cflags + [nil]
249
- else
250
- cflags
251
- end
244
+ cflags_safe = if cflags.empty? or !cflags[-1].nil?
245
+ cflags + [nil]
246
+ else
247
+ cflags
248
+ end
249
+
250
+ @module = Clib.do_bpf_module_create_c_from_string(
251
+ text,
252
+ debug,
253
+ cflags_safe.pack("p*"),
254
+ cflags_safe.size,
255
+ allow_rlimit,
256
+ dev_name
257
+ )
252
258
 
253
- @module = Clib.do_bpf_module_create_c_from_string(
254
- text,
255
- debug,
256
- cflags_safe.pack("p*"),
257
- cflags_safe.size,
258
- allow_rlimit,
259
- dev_name
260
- )
261
- end
262
259
  @funcs = {}
263
260
  @tables = {}
264
261
  @perf_buffers = {}
262
+ @_ringbuf_manager = nil
265
263
 
266
264
  unless @module
267
265
  raise "BPF module not created"
@@ -555,6 +553,21 @@ module RbBCC
555
553
  Clib.perf_reader_poll(readers.size, pack, timeout)
556
554
  end
557
555
 
556
+ def _open_ring_buffer(map_fd, fn, ctx)
557
+ buf = Clib.bpf_new_ringbuf(map_fd, fn, ctx)
558
+ if !buf
559
+ raise "Could not open ring buffer"
560
+ end
561
+ @_ringbuf_manager ||= buf
562
+ end
563
+
564
+ def ring_buffer_poll(timeout=-1)
565
+ unless @_ringbuf_manager
566
+ raise "No ring buffers to poll"
567
+ end
568
+ Clib.bpf_poll_ringbuf(@_ringbuf_manager, timeout)
569
+ end
570
+
558
571
  def ksymname(name)
559
572
  SymbolCache.resolve_global(name)
560
573
  end
data/lib/rbbcc/clib.rb CHANGED
@@ -25,7 +25,7 @@ module RbBCC
25
25
  end
26
26
 
27
27
  extend Fiddle::Importer
28
- targets = %w(0.18.0 0.17.0 0.16.0 0.15.0 0.14.0 0.13.0 0.12.0 0.11.0 0.10.0)
28
+ targets = %w(0.29.1 0.26.0 0.22.0 0.18.0 0.17.0 0.16.0 0.15.0 0.14.0 0.13.0 0.12.0 0.11.0 0.10.0)
29
29
  if default_load = ENV['LIBBCC_VERSION']
30
30
  targets.unshift(default_load)
31
31
  targets.uniq!
@@ -44,7 +44,6 @@ module RbBCC
44
44
  end
45
45
  typealias "size_t", "int"
46
46
 
47
- extern 'void * bpf_module_create_b(char *filename, char *proto_filename, unsigned int flags, char *dev_name)'
48
47
  extern 'int bpf_num_functions(void *)'
49
48
  extern 'char * bpf_function_name(void *, int)'
50
49
  extern 'void bpf_module_destroy(void *)'
@@ -133,6 +132,11 @@ module RbBCC
133
132
  extern 'size_t bpf_perf_event_fields(void *program, const char *event)'
134
133
  extern 'char * bpf_perf_event_field(void *program, const char *event, size_t i)'
135
134
 
135
+ # typedef int (*ring_buffer_sample_fn)(void *ctx, void *data, size_t size);
136
+ extern 'void * bpf_new_ringbuf(int map_fd, void *sample_cb, void *ctx)'
137
+ extern 'int bpf_poll_ringbuf(void *rb, int timeout_ms)'
138
+ extern 'void bpf_free_ringbuf(void *rb)'
139
+
136
140
  extern 'void * bcc_usdt_new_frompid(int, char *)'
137
141
  extern 'void * bcc_usdt_new_frompath(char *path)'
138
142
  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.9.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.9.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-07-10 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