rbbcc 0.11.6 → 0.11.8

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/rbbcc/table.rb +8 -7
  4. data/lib/rbbcc/version.rb +1 -1
  5. data/rbbcc.gemspec +5 -2
  6. metadata +2 -62
  7. data/docs/README.md +0 -7
  8. data/docs/answers/01-hello-world.rb +0 -16
  9. data/docs/answers/02-sys_sync.rb +0 -18
  10. data/docs/answers/03-hello_fields.rb +0 -33
  11. data/docs/answers/04-sync_timing.rb +0 -46
  12. data/docs/answers/05-sync_count.rb +0 -54
  13. data/docs/answers/06-disksnoop.rb +0 -71
  14. data/docs/answers/07-hello_perf_output.rb +0 -59
  15. data/docs/answers/08-sync_perf_output.rb +0 -60
  16. data/docs/answers/09-bitehist.rb +0 -32
  17. data/docs/answers/10-disklatency.rb +0 -51
  18. data/docs/answers/11-vfsreadlat.c +0 -46
  19. data/docs/answers/11-vfsreadlat.rb +0 -66
  20. data/docs/answers/12-urandomread.rb +0 -38
  21. data/docs/answers/13-disksnoop_fixed.rb +0 -108
  22. data/docs/answers/14-strlen_count.rb +0 -46
  23. data/docs/answers/15-nodejs_http_server.rb +0 -44
  24. data/docs/answers/16-task_switch.c +0 -23
  25. data/docs/answers/16-task_switch.rb +0 -17
  26. data/docs/answers/node-server.js +0 -11
  27. data/docs/getting_started.md +0 -154
  28. data/docs/projects_using_rbbcc.md +0 -43
  29. data/docs/tutorial_bcc_ruby_developer.md +0 -774
  30. data/docs/tutorial_bcc_ruby_developer_japanese.md +0 -770
  31. data/examples/bitehist.rb +0 -46
  32. data/examples/charty/Gemfile +0 -11
  33. data/examples/charty/Gemfile.lock +0 -48
  34. data/examples/charty/bitehist-unicode.rb +0 -87
  35. data/examples/collectsyscall.rb +0 -182
  36. data/examples/dddos.rb +0 -112
  37. data/examples/disksnoop.rb +0 -73
  38. data/examples/dns_blocker.rb +0 -134
  39. data/examples/example.gif +0 -0
  40. data/examples/extract_arg.rb +0 -26
  41. data/examples/hello_fields.rb +0 -32
  42. data/examples/hello_perf_output.rb +0 -64
  43. data/examples/hello_ring_buffer.rb +0 -64
  44. data/examples/hello_world.rb +0 -6
  45. data/examples/kvm_hypercall.rb +0 -69
  46. data/examples/lsm_sockblock.rb +0 -141
  47. data/examples/mallocstack.rb +0 -63
  48. data/examples/networking/http_filter/http-parse-simple.c +0 -114
  49. data/examples/networking/http_filter/http-parse-simple.rb +0 -85
  50. data/examples/pin_maps_a.rb +0 -88
  51. data/examples/pin_maps_b.rb +0 -71
  52. data/examples/py-orig/sockblock.py +0 -119
  53. data/examples/ringbuf_pin_a.rb +0 -51
  54. data/examples/ringbuf_pin_b.rb +0 -29
  55. data/examples/ruby_usdt.rb +0 -105
  56. data/examples/sbrk_trace.rb +0 -204
  57. data/examples/ssl_http_trace.rb +0 -274
  58. data/examples/syscalluname.rb +0 -39
  59. data/examples/table.rb +0 -15
  60. data/examples/tools/bashreadline.rb +0 -83
  61. data/examples/tools/execsnoop.rb +0 -229
  62. data/examples/tools/runqlat.rb +0 -148
  63. data/examples/urandomread-explicit.rb +0 -58
  64. data/examples/urandomread.rb +0 -39
  65. data/examples/usdt-test.rb +0 -6
  66. data/examples/usdt.rb +0 -26
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # pin_maps_a.rb
4
- # Program A: create HashMap/ArrayMap, hook execve, count up, and pin maps.
5
- #
6
- # Usage (root):
7
- # sudo ruby examples/pin_maps_a.rb --pin-dir /sys/fs/bpf/rbbcc_pin_demo
8
-
9
- require 'rbbcc'
10
- require 'optparse'
11
- require 'fileutils'
12
-
13
- include RbBCC
14
-
15
- BPF_TEXT = <<~CLANG
16
- BPF_HASH(pin_hash_map, u32, u64, 1024);
17
- BPF_ARRAY(pin_array_map, u64, 1);
18
-
19
- int trace_execve(void *ctx) {
20
- u64 zero = 0;
21
- u32 pid = bpf_get_current_pid_tgid();
22
- u32 idx = 0;
23
- u64 *hval;
24
- u64 *aval;
25
-
26
- hval = pin_hash_map.lookup_or_try_init(&pid, &zero);
27
- if (hval) {
28
- __sync_fetch_and_add(hval, 1);
29
- }
30
-
31
- aval = pin_array_map.lookup(&idx);
32
- if (aval) {
33
- __sync_fetch_and_add(aval, 1);
34
- }
35
-
36
- return 0;
37
- }
38
- CLANG
39
-
40
- options = {
41
- pin_dir: '/sys/fs/bpf/rbbcc_pin_demo'
42
- }
43
-
44
- OptionParser.new do |opts|
45
- opts.banner = 'Usage: pin_maps_a.rb [options]'
46
-
47
- opts.on('--pin-dir DIR', 'Pin directory under bpffs') do |v|
48
- options[:pin_dir] = v
49
- end
50
- end.parse!
51
-
52
- b = BCC.new(text: BPF_TEXT)
53
- b.attach_kprobe(
54
- event: b.get_syscall_fnname('execve'),
55
- fn_name: 'trace_execve'
56
- )
57
-
58
- hash_map = b['pin_hash_map']
59
- array_map = b['pin_array_map']
60
-
61
- # Initialize global counter slot.
62
- array_map[0] = 0
63
-
64
- FileUtils.mkdir_p(options[:pin_dir])
65
- hash_path = File.join(options[:pin_dir], 'pin_hash_map')
66
- array_path = File.join(options[:pin_dir], 'pin_array_map')
67
-
68
- File.unlink(hash_path) if File.exist?(hash_path)
69
- File.unlink(array_path) if File.exist?(array_path)
70
-
71
- BCC.pin!(hash_map.map_fd, hash_path)
72
- BCC.pin!(array_map.map_fd, array_path)
73
-
74
- puts 'Pinned maps created.'
75
- puts " hash map: #{hash_path}"
76
- puts " array map: #{array_path}"
77
- puts 'kprobe attached to execve. Run some commands in another terminal.'
78
- puts 'Press Ctrl-C to stop Program A.'
79
-
80
- begin
81
- loop do
82
- sleep 1
83
- total = array_map[0]&.to_bcc_value || 0
84
- puts "execve total count: #{total}"
85
- end
86
- rescue Interrupt
87
- puts '\nStopping Program A...'
88
- end
@@ -1,71 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # pin_maps_b.rb
4
- # Program B: open pinned HashMap/ArrayMap with from_pin and read/update values.
5
- #
6
- # Usage (root):
7
- # sudo ruby examples/pin_maps_b.rb --pin-dir /sys/fs/bpf/rbbcc_pin_demo
8
-
9
- require 'rbbcc'
10
- require 'optparse'
11
-
12
- include RbBCC
13
-
14
- options = {
15
- pin_dir: '/sys/fs/bpf/rbbcc_pin_demo'
16
- }
17
-
18
- OptionParser.new do |opts|
19
- opts.banner = 'Usage: pin_maps_b.rb [options]'
20
-
21
- opts.on('--pin-dir DIR', 'Pin directory under bpffs') do |v|
22
- options[:pin_dir] = v
23
- end
24
- end.parse!
25
-
26
- hash_path = File.join(options[:pin_dir], 'pin_hash_map')
27
- array_path = File.join(options[:pin_dir], 'pin_array_map')
28
-
29
- unless File.exist?(hash_path) && File.exist?(array_path)
30
- abort("Pinned map files are missing. Run pin_maps_a.rb first: #{options[:pin_dir]}")
31
- end
32
-
33
- # Explicitly pass key/leaf type and size; no type detection is performed.
34
- hash_map = HashTable.from_pin(
35
- hash_path,
36
- 'unsigned int',
37
- 'unsigned long long',
38
- keysize: 4,
39
- leafsize: 8
40
- )
41
- array_map = ArrayTable.from_pin(
42
- array_path,
43
- 'unsigned int',
44
- 'unsigned long long',
45
- keysize: 4,
46
- leafsize: 8
47
- )
48
-
49
- puts 'Loaded pinned maps.'
50
- puts " hash map fd: #{hash_map.map_fd}"
51
- puts " array map fd: #{array_map.map_fd}"
52
- puts " hash ttype: #{hash_map.ttype}"
53
- puts " array ttype: #{array_map.ttype}"
54
-
55
- puts 'Read existing values:'
56
- puts " hash[1] = #{hash_map[1]&.to_bcc_value.inspect}"
57
- puts " hash[2] = #{hash_map[2]&.to_bcc_value.inspect}"
58
- puts " array[0] = #{array_map[0]&.to_bcc_value.inspect}"
59
- puts " array[1] = #{array_map[1]&.to_bcc_value.inspect}"
60
-
61
- hash_map[3] = 1
62
- array_map[0] = (array_map[0]&.to_bcc_value || 0) + 1
63
-
64
- puts 'Updated values:'
65
- puts " hash[3] = #{hash_map[3]&.to_bcc_value.inspect}"
66
- puts " array[0] = #{array_map[0]&.to_bcc_value.inspect}"
67
-
68
- puts 'Iterate hash entries:'
69
- hash_map.each_pair do |k, v|
70
- puts " #{k.to_bcc_value} => #{v.to_bcc_value}"
71
- end
@@ -1,119 +0,0 @@
1
- from bcc import BPF, lib
2
- import socket
3
- import ctypes
4
- import os
5
-
6
- # 1. C言語側のプログラム
7
- program = r"""
8
- #include <linux/lsm_hooks.h>
9
- #include <linux/socket.h>
10
- #include <uapi/asm-generic/errno-base.h>
11
-
12
- struct data_t {
13
- u32 pid;
14
- int family;
15
- int type;
16
- int is_warning;
17
- int is_blocked;
18
- char comm[16];
19
- };
20
-
21
- BPF_PERF_OUTPUT(events);
22
-
23
- // モード保存用マップ (Index 0 を使用)
24
- // 1: blockモード, 0: previewモード
25
- BPF_ARRAY(config_map, u32, 1);
26
-
27
- LSM_PROBE(socket_create, int family, int type, int protocol, int kern)
28
- {
29
- u32 pid = bpf_get_current_pid_tgid() >> 32;
30
- struct data_t data = {};
31
-
32
- // マップから現在のモードを取得
33
- u32 key = 0;
34
- u32 *mode = config_map.lookup(&key);
35
- int is_block_mode = (mode && *mode == 1);
36
-
37
- data.pid = pid;
38
- data.family = family;
39
- data.type = type;
40
- bpf_get_current_comm(&data.comm, sizeof(data.comm));
41
-
42
- if (family == AF_ALG) {
43
- data.is_blocked = is_block_mode;
44
- data.is_warning = 1;
45
-
46
- // ログデータを送信
47
- events.perf_submit(ctx, &data, sizeof(data));
48
-
49
- // blockモードならエラーを返してシステムコールを失敗させる
50
- if (is_block_mode) {
51
- return -EPERM; // -1 (Operation not permitted)
52
- }
53
- } else {
54
- data.is_blocked = 0;
55
- data.is_warning = 0;
56
-
57
- // ログデータを送信
58
- events.perf_submit(ctx, &data, sizeof(data));
59
- }
60
-
61
- return 0;
62
- }
63
- """
64
-
65
- # 定数解決用
66
- families = {getattr(socket, n): n for n in dir(socket) if n.startswith('AF_')}
67
- types = {getattr(socket, n): n for n in dir(socket) if n.startswith('SOCK_')}
68
-
69
- def print_event(cpu, data, size):
70
- event = b["events"].event(data)
71
-
72
- family_str = families.get(event.family, f"AF_UNKNOWN({event.family})")
73
- type_str = types.get(event.type, f"SOCK_UNKNOWN({event.type})")
74
-
75
- print(f"PID: {event.pid:<7} | COMM: {event.comm.decode('utf-8'):<15} | "
76
- f"FAMILY: {family_str:<12} | TYPE: {type_str}")
77
-
78
- if event.is_warning:
79
- mode_str = "BLOCK" if event.is_blocked else "PREVIEW"
80
- status = "!! REJECTED !!" if event.is_blocked else "WARNING"
81
-
82
- print(f"[{mode_str}] {status}: PID {event.pid} ({event.comm.decode()}) "
83
- f"tried to create AF_ALG socket.")
84
-
85
- # 2. ロードと設定
86
- try:
87
- b = BPF(text=program)
88
-
89
- # --- モード設定 ---
90
- # 1 を書き込むと blockモード、0 だと previewモード
91
- mode = 0
92
- config_table = b.get_table("config_map")
93
- config_table[ctypes.c_uint32(0)] = ctypes.c_uint32(mode)
94
-
95
- # Mapの永続化 - ユーザ空間からモードを変更できるようにするため
96
- map_path = "/sys/fs/bpf/my_config_map"
97
- if os.path.exists(map_path):
98
- os.remove(map_path)
99
- print(f"Pinning config map to -> {map_path}")
100
- map_fd = config_table.get_fd()
101
- res = lib.bpf_obj_pin(map_fd, ctypes.c_char_p(map_path.encode()))
102
- if res != 0:
103
- raise Exception(f"Failed to pin map to {map_path}: {os.strerror(-res)}")
104
-
105
- # -----------------
106
-
107
- print(f"LSM BPF started in {'BLOCK' if mode == 1 else 'PREVIEW'} mode.")
108
- print("Tracing AF_ALG creation... Press Ctrl-C to exit.")
109
-
110
- b["events"].open_perf_buffer(print_event)
111
-
112
- while True:
113
- try:
114
- b.perf_buffer_poll()
115
- except KeyboardInterrupt:
116
- exit()
117
-
118
- except Exception as e:
119
- print(f"Failed: {e}")
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'rbbcc'
3
- include RbBCC
4
-
5
- PIN_PATH = "/sys/fs/bpf/pinned_ringbuf"
6
-
7
- if File.exist?(PIN_PATH)
8
- puts "Removing old pinned map at #{PIN_PATH}..."
9
- File.unlink(PIN_PATH)
10
- end
11
-
12
- bpf_text = <<~CLANG
13
- #include <uapi/linux/ptrace.h>
14
-
15
- struct data_t {
16
- u32 pid;
17
- char comm[16];
18
- };
19
-
20
- BPF_RINGBUF_OUTPUT(events, 4);
21
-
22
- int trace_uname(struct pt_regs *ctx) {
23
- struct data_t *data = events.ringbuf_reserve(sizeof(struct data_t));
24
- if (!data) return 0;
25
-
26
- data->pid = bpf_get_current_pid_tgid() >> 32;
27
- bpf_get_current_comm(&data->comm, sizeof(data->comm));
28
-
29
- events.ringbuf_submit(data, 0);
30
- return 0;
31
- }
32
- CLANG
33
-
34
- puts "Process A (Ruby): Loading BPF and attaching tracepoint..."
35
- b = BCC.new(text: bpf_text)
36
- b.attach_tracepoint(tp: "syscalls:sys_enter_newuname", fn_name: "trace_uname")
37
-
38
- puts "Process A (Ruby): Pinning ringbuf map to #{PIN_PATH}..."
39
- b["events"].pin!(PIN_PATH)
40
-
41
- puts "\n[Process A Active] Kept alive to feed events. Press Ctrl+C to stop."
42
- begin
43
- loop do
44
- sleep 1
45
- end
46
- ensure
47
- if File.exist?(PIN_PATH)
48
- puts "\nUnpinning map..."
49
- File.unlink(PIN_PATH)
50
- end
51
- end
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'rbbcc'
3
- include RbBCC
4
-
5
- PIN_PATH = "/sys/fs/bpf/pinned_ringbuf"
6
-
7
- type = "struct data_t {
8
- u32 pid;
9
- char comm[16];
10
- };"
11
-
12
- puts "Process B (Ruby): Initializing consumer client..."
13
- buf = RingBuf.from_pin(PIN_PATH, type, 4)
14
-
15
- # ring_buffer listner
16
- buf.open_ring_buffer do |cpu, data, size|
17
- event = buf.event(data)
18
- puts "[Process B Captured] PID: #{event.pid.to_s.ljust(6)} | COMMAND: #{event.comm}"
19
- end
20
-
21
- puts "\n[Process B Active] Successfully hooked to pinned map (FD: #{buf.map_fd}). Listening for events...\n\n"
22
-
23
- begin
24
- loop do
25
- buf.ring_buffer_poll()
26
- end
27
- rescue Interrupt
28
- puts "\nExiting Process B."
29
- end
@@ -1,105 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # To run this example, please build the target ruby with an `--enable-dtrace` option in advance.
3
- # To build via rbenv, sample command is:
4
- # $ RUBY_CONFIGURE_OPTS='--enable-dtrace' rbenv install 2.7.0
5
- #
6
- # Example autput:
7
- # # bundle exec ruby examples/ruby_usdt.rb $(pidof irb)
8
- # TIME(s) COMM KLASS PATH
9
- # 0.000000000 irb Struct::Key /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline.rb
10
- # 0.000055206 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
11
- # 0.000088588 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
12
- # 0.000117740 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
13
- # 0.000126697 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
14
- # 0.000213388 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
15
- # 0.000225678 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
16
- # 0.000243638 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
17
- # 0.000254680 irb Range /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/ruby-lex.rb
18
- # 0.000264707 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
19
- # 0.000275579 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
20
- # 0.000282438 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
21
- # 0.000326136 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb.rb
22
- # 0.001353621 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
23
- # 0.001385320 irb IRB::Color::SymbolState /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/color.rb
24
- # 0.001397043 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/color.rb
25
- # 0.001416420 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
26
- # 0.001423861 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
27
- # 0.001462010 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
28
- # 0.001478995 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
29
- # 0.001487499 irb Range /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb/ruby-lex.rb
30
- # 0.001496666 irb Ripper::Lexer /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
31
- # 0.001508224 irb Ripper::Lexer::Elem /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
32
- # 0.001515143 irb Ripper::Lexer::State /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/ripper/lexer.rb
33
- # 0.001556170 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/irb.rb
34
- # 0.001726273 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
35
- # 0.001946948 irb Array /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline/line_editor.rb
36
- # 0.001956585 irb String /root/.rbenv/versions/2.7.0/lib/ruby/2.7.0/reline.rb
37
-
38
- require 'rbbcc'
39
- include RbBCC
40
-
41
- pid = ARGV[0] || begin
42
- puts("USAGE: #{$0} PID")
43
- exit()
44
- end
45
- debug = !!ENV['DEBUG']
46
-
47
- bpf_text = <<BPF
48
- #include <uapi/linux/ptrace.h>
49
- #include <linux/sched.h>
50
-
51
- struct data_t {
52
- u64 ts;
53
- char comm[TASK_COMM_LEN];
54
- char klass[64];
55
- char path[256];
56
- };
57
- BPF_PERF_OUTPUT(events);
58
-
59
- int do_trace_create_object(struct pt_regs *ctx) {
60
- struct data_t data = {};
61
- uint64_t addr, addr2;
62
-
63
- data.ts = bpf_ktime_get_ns();
64
-
65
- bpf_get_current_comm(&data.comm, sizeof(data.comm));
66
- bpf_usdt_readarg_p(1, ctx, &data.klass, sizeof(data.klass));
67
- bpf_usdt_readarg_p(2, ctx, &data.path, sizeof(data.path));
68
-
69
- events.perf_submit(ctx, &data, sizeof(data));
70
-
71
- return 0;
72
- };
73
- BPF
74
-
75
- u = USDT.new(pid: pid.to_i)
76
- u.enable_probe(probe: "object__create", fn_name: "do_trace_create_object")
77
- if debug
78
- puts(u.get_text)
79
- puts(bpf_text)
80
- end
81
-
82
- # initialize BPF
83
- b = BCC.new(text: bpf_text, usdt_contexts: [u])
84
-
85
- puts("%-18s %-6s %-24s %s" % ["TIME(s)", "COMM", "KLASS", "PATH"])
86
-
87
- # process event
88
- start = 0
89
- b["events"].open_perf_buffer do |cpu, data, size|
90
- event = b["events"].event(data)
91
- if start == 0
92
- start = event.ts
93
- end
94
-
95
- time_s = ((event.ts - start).to_f) / 1000000000
96
- puts(
97
- "%-18.9f %-6s %-24s %s" %
98
- [time_s, event.comm, event.klass, event.path]
99
- )
100
- end
101
-
102
- Signal.trap(:INT) { puts "\nDone."; exit }
103
- loop do
104
- b.perf_buffer_poll()
105
- end
@@ -1,204 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # Trace example for libc's USDT:
3
- # - memory_sbrk_more
4
- # - memory_sbrk_less
5
- # - memory_mallopt_free_dyn_thresholds
6
- # Description is here: https://www.gnu.org/software/libc/manual/html_node/Memory-Allocation-Probes.html
7
- #
8
- # Example output:
9
- # bundle exec ruby examples/sbrk_trace.rb -c ruby
10
- # !! Trace start.
11
- # [ 0.000000000] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x55f34b979000 size=135168
12
- # [ 0.036549804] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9760000 size=135168
13
- # [ 0.036804183] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9781000 size=143360
14
- # [ 0.036855378] pid=32756 comm=ruby probe=memory_sbrk_less addr=0x557fd97a0000 size=16384
15
- # [ 0.036931376] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd97a0000 size=147456
16
- # [ 0.036940382] pid=32756 comm=ruby probe=memory_sbrk_less addr=0x557fd97c0000 size=16384
17
- # [ 0.037022971] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd97c0000 size=151552
18
- # [ 0.038602464] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd97e5000 size=204800
19
- # [ 0.039398297] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9817000 size=135168
20
- # [ 0.039909594] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9838000 size=135168
21
- # [ 0.040536005] pid=32756 comm=ruby probe=memory_sbrk_more addr=0x557fd9859000 size=163840
22
- # ...
23
-
24
- require 'rbbcc'
25
- include RbBCC
26
-
27
- def usage
28
- puts("USAGE: #{$0} [-p PID|-c COMM]")
29
- exit()
30
- end
31
-
32
- def find_libc_location
33
- if File.exist?('/lib/x86_64-linux-gnu/libc.so.6')
34
- '/lib/x86_64-linux-gnu/libc.so.6'
35
- else
36
- `find /lib/ -name 'libc.so*' | tail -1`.chomp
37
- end
38
- end
39
-
40
- usage if ARGV.size != 0 && ARGV.size != 2
41
-
42
- pid = comm = nil
43
- path = find_libc_location
44
- case ARGV[0]
45
- when '-p', '--pid'
46
- pid = ARGV[1].to_i
47
- when '-c', '--comm'
48
- comm = ARGV[1]
49
- when nil
50
- # nop
51
- else
52
- usage
53
- end
54
-
55
- debug = !!ENV['DEBUG']
56
-
57
- bpf_text = <<BPF
58
- #include <uapi/linux/ptrace.h>
59
- #include <linux/sched.h>
60
-
61
- struct data_t {
62
- u32 type;
63
- u64 ts;
64
- u32 pid;
65
- char comm[TASK_COMM_LEN];
66
- u64 addr;
67
- u32 sbrk_size;
68
- u32 adjusted_mmap;
69
- u32 trim_thresholds;
70
- };
71
- BPF_PERF_OUTPUT(events);
72
-
73
- static inline bool streq(uintptr_t str) {
74
- char needle[] = "{{NEEDLE}}";
75
- char haystack[sizeof(needle)];
76
- bpf_probe_read(&haystack, sizeof(haystack), (void *)str);
77
- for (int i = 0; i < sizeof(needle) - 1; ++i) {
78
- if (needle[i] != haystack[i]) {
79
- return false;
80
- }
81
- }
82
- return true;
83
- }
84
-
85
- #define PROBE_TYPE_more 1
86
- #define PROBE_TYPE_less 2
87
- #define PROBE_TYPE_free 3
88
-
89
- {{FUNC_MORE}}
90
-
91
- {{FUNC_LESS}}
92
-
93
- int trace_memory_free(struct pt_regs *ctx) {
94
- struct data_t data = {};
95
- long buf;
96
-
97
- data.type = PROBE_TYPE_free;
98
- data.ts = bpf_ktime_get_ns();
99
- data.pid = bpf_get_current_pid_tgid();
100
- bpf_get_current_comm(&data.comm, sizeof(data.comm));
101
-
102
- {{NEEDLE_START}}
103
- bpf_usdt_readarg(1, ctx, &buf);
104
- data.adjusted_mmap = buf;
105
-
106
- bpf_usdt_readarg(2, ctx, &buf);
107
- data.trim_thresholds = buf;
108
-
109
- events.perf_submit(ctx, &data, sizeof(data));
110
- {{NEEDLE_END}}
111
-
112
- return 0;
113
- };
114
-
115
- BPF
116
-
117
- trace_fun_sbrk = <<FUNC
118
- int trace_memory_sbrk_{{TYPE}}(struct pt_regs *ctx) {
119
- struct data_t data = {};
120
- long buf;
121
-
122
- data.type = PROBE_TYPE_{{TYPE}};
123
- data.ts = bpf_ktime_get_ns();
124
- data.pid = bpf_get_current_pid_tgid();
125
- bpf_get_current_comm(&data.comm, sizeof(data.comm));
126
-
127
- {{NEEDLE_START}}
128
- bpf_usdt_readarg(1, ctx, &buf);
129
- data.addr = buf;
130
-
131
- bpf_usdt_readarg(2, ctx, &buf);
132
- data.sbrk_size = buf;
133
-
134
- events.perf_submit(ctx, &data, sizeof(data));
135
- {{NEEDLE_END}}
136
-
137
- return 0;
138
- };
139
- FUNC
140
-
141
- PROBE_TYPE_more = 1
142
- PROBE_TYPE_less = 2
143
- PROBE_TYPE_free = 3
144
- PROBE_MAP = {
145
- PROBE_TYPE_more => 'memory_sbrk_more',
146
- PROBE_TYPE_less => 'memory_sbrk_less',
147
- PROBE_TYPE_free => 'memory_mallopt_free_dyn_thresholds'
148
- }
149
-
150
- bpf_text.sub!('{{FUNC_MORE}}', trace_fun_sbrk.gsub('{{TYPE}}', 'more'))
151
- bpf_text.sub!('{{FUNC_LESS}}', trace_fun_sbrk.gsub('{{TYPE}}', 'less'))
152
-
153
- if comm
154
- bpf_text.sub!('{{NEEDLE}}', comm)
155
- bpf_text.gsub!('{{NEEDLE_START}}', "if(streq((uintptr_t)data.comm)) {")
156
- bpf_text.gsub!('{{NEEDLE_END}}', "}")
157
- else
158
- bpf_text.sub!('{{NEEDLE}}', "")
159
- bpf_text.gsub!('{{NEEDLE_START}}', "")
160
- bpf_text.gsub!('{{NEEDLE_END}}', "")
161
- end
162
-
163
- u = USDT.new(pid: pid, path: path)
164
- u.enable_probe(probe: "memory_sbrk_more", fn_name: "trace_memory_sbrk_more")
165
- u.enable_probe(probe: "memory_sbrk_less", fn_name: "trace_memory_sbrk_less")
166
- if pid
167
- # FIXME: Only available when PID is specified
168
- # otherwise got an error:
169
- # bpf: Failed to load program: Invalid argument
170
- # last insn is not an exit or jmp
171
- # It seems libbcc won't generate proper readarg helper
172
- u.enable_probe(probe: "memory_mallopt_free_dyn_thresholds", fn_name: "trace_memory_free")
173
- end
174
-
175
- # initialize BPF
176
- b = BCC.new(text: bpf_text, usdt_contexts: [u])
177
-
178
- puts "!! Trace start."
179
- # process event
180
- start = 0
181
- b["events"].open_perf_buffer do |cpu, data, size|
182
- event = b["events"].event(data)
183
- if start == 0
184
- start = event.ts
185
- end
186
-
187
- time_s = ((event.ts - start).to_f) / 1000000000
188
- if [PROBE_TYPE_more, PROBE_TYPE_less].include?(event.type)
189
- puts(
190
- "[%18.9f] pid=%d comm=%s probe=%s addr=%#x size=%d" %
191
- [time_s, event.pid, event.comm, PROBE_MAP[event.type], event.addr, event.sbrk_size]
192
- )
193
- else
194
- puts(
195
- "[%18.9f] pid=%d comm=%s probe=%s adjusted_mmap=%d trim_thresholds=%d" %
196
- [time_s, event.pid, event.comm, PROBE_MAP[event.type], event.adjusted_mmap, event.trim_thresholds]
197
- )
198
- end
199
- end
200
-
201
- Signal.trap(:INT) { puts "\n!! Done."; exit }
202
- loop do
203
- b.perf_buffer_poll()
204
- end