rbbcc 0.3.1 → 0.6.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +3 -1
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +6 -6
  5. data/README.md +4 -0
  6. data/docs/README.md +2 -0
  7. data/docs/answers/01-hello-world.rb +16 -0
  8. data/docs/answers/02-sys_sync.rb +18 -0
  9. data/docs/answers/03-hello_fields.rb +33 -0
  10. data/docs/answers/04-sync_timing.rb +46 -0
  11. data/docs/answers/05-sync_count.rb +54 -0
  12. data/docs/answers/06-disksnoop.rb +71 -0
  13. data/docs/answers/07-hello_perf_output.rb +59 -0
  14. data/docs/answers/08-sync_perf_output.rb +60 -0
  15. data/docs/answers/09-bitehist.rb +32 -0
  16. data/docs/answers/10-disklatency.rb +51 -0
  17. data/docs/answers/11-vfsreadlat.c +46 -0
  18. data/docs/answers/11-vfsreadlat.rb +66 -0
  19. data/docs/answers/12-urandomread.rb +38 -0
  20. data/docs/answers/13-disksnoop_fixed.rb +108 -0
  21. data/docs/answers/14-strlen_count.rb +46 -0
  22. data/docs/answers/15-nodejs_http_server.rb +44 -0
  23. data/docs/answers/16-task_switch.c +23 -0
  24. data/docs/answers/16-task_switch.rb +17 -0
  25. data/docs/answers/node-server.js +11 -0
  26. data/docs/projects_using_rbbcc.md +43 -0
  27. data/docs/tutorial_bcc_ruby_developer.md +774 -0
  28. data/docs/tutorial_bcc_ruby_developer_japanese.md +770 -0
  29. data/examples/networking/http_filter/http-parse-simple.c +114 -0
  30. data/examples/networking/http_filter/http-parse-simple.rb +85 -0
  31. data/examples/ruby_usdt.rb +105 -0
  32. data/examples/sbrk_trace.rb +204 -0
  33. data/examples/tools/bashreadline.rb +83 -0
  34. data/lib/rbbcc/bcc.rb +73 -20
  35. data/lib/rbbcc/clib.rb +7 -2
  36. data/lib/rbbcc/debug.rb +17 -0
  37. data/lib/rbbcc/table.rb +16 -22
  38. data/lib/rbbcc/usdt.rb +21 -4
  39. data/lib/rbbcc/version.rb +1 -1
  40. data/rbbcc.gemspec +1 -5
  41. metadata +34 -61
@@ -0,0 +1,114 @@
1
+ #include <uapi/linux/ptrace.h>
2
+ #include <net/sock.h>
3
+ #include <bcc/proto.h>
4
+
5
+ #define IP_TCP 6
6
+ #define ETH_HLEN 14
7
+
8
+ /*eBPF program.
9
+ Filter IP and TCP packets, having payload not empty
10
+ and containing "HTTP", "GET", "POST" ... as first bytes of payload
11
+ if the program is loaded as PROG_TYPE_SOCKET_FILTER
12
+ and attached to a socket
13
+ return 0 -> DROP the packet
14
+ return -1 -> KEEP the packet and return it to user space (userspace can read it from the socket_fd )
15
+ */
16
+ int http_filter(struct __sk_buff *skb) {
17
+
18
+ u8 *cursor = 0;
19
+
20
+ struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
21
+ //filter IP packets (ethernet type = 0x0800)
22
+ if (!(ethernet->type == 0x0800)) {
23
+ goto DROP;
24
+ }
25
+
26
+ struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
27
+ //filter TCP packets (ip next protocol = 0x06)
28
+ if (ip->nextp != IP_TCP) {
29
+ goto DROP;
30
+ }
31
+
32
+ u32 tcp_header_length = 0;
33
+ u32 ip_header_length = 0;
34
+ u32 payload_offset = 0;
35
+ u32 payload_length = 0;
36
+
37
+ //calculate ip header length
38
+ //value to multiply * 4
39
+ //e.g. ip->hlen = 5 ; IP Header Length = 5 x 4 byte = 20 byte
40
+ ip_header_length = ip->hlen << 2; //SHL 2 -> *4 multiply
41
+
42
+ //check ip header length against minimum
43
+ if (ip_header_length < sizeof(*ip)) {
44
+ goto DROP;
45
+ }
46
+
47
+ //shift cursor forward for dynamic ip header size
48
+ void *_ = cursor_advance(cursor, (ip_header_length-sizeof(*ip)));
49
+
50
+ struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp));
51
+
52
+ //calculate tcp header length
53
+ //value to multiply *4
54
+ //e.g. tcp->offset = 5 ; TCP Header Length = 5 x 4 byte = 20 byte
55
+ tcp_header_length = tcp->offset << 2; //SHL 2 -> *4 multiply
56
+
57
+ //calculate payload offset and length
58
+ payload_offset = ETH_HLEN + ip_header_length + tcp_header_length;
59
+ payload_length = ip->tlen - ip_header_length - tcp_header_length;
60
+
61
+ //http://stackoverflow.com/questions/25047905/http-request-minimum-size-in-bytes
62
+ //minimum length of http request is always geater than 7 bytes
63
+ //avoid invalid access memory
64
+ //include empty payload
65
+ if(payload_length < 7) {
66
+ goto DROP;
67
+ }
68
+
69
+ //load first 7 byte of payload into p (payload_array)
70
+ //direct access to skb not allowed
71
+ unsigned long p[7];
72
+ int i = 0;
73
+ for (i = 0; i < 7; i++) {
74
+ p[i] = load_byte(skb , payload_offset + i);
75
+ }
76
+
77
+ //find a match with an HTTP message
78
+ //HTTP
79
+ if ((p[0] == 'H') && (p[1] == 'T') && (p[2] == 'T') && (p[3] == 'P')) {
80
+ goto KEEP;
81
+ }
82
+ //GET
83
+ if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) {
84
+ goto KEEP;
85
+ }
86
+ //POST
87
+ if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) {
88
+ goto KEEP;
89
+ }
90
+ //PUT
91
+ if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) {
92
+ goto KEEP;
93
+ }
94
+ //DELETE
95
+ if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) {
96
+ goto KEEP;
97
+ }
98
+ //HEAD
99
+ if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) {
100
+ goto KEEP;
101
+ }
102
+
103
+ //no HTTP match
104
+ goto DROP;
105
+
106
+ //keep the packet and send it to userspace retruning -1
107
+ KEEP:
108
+ return -1;
109
+
110
+ //drop the packet returning 0
111
+ DROP:
112
+ return 0;
113
+
114
+ }
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ #Original http-parse-simple.py in invisor/bcc
4
+ #Bertrone Matteo - Polytechnic of Turin
5
+ #November 2015
6
+ #Ruby version by Uchio Kondo, License follows
7
+ #
8
+ #eBPF application that parses HTTP packets
9
+ #and extracts (and prints on screen) the URL contained in the GET/POST request.
10
+ #
11
+ #eBPF program http_filter is used as SOCKET_FILTER attached to eth0 interface.
12
+ #only packet of type ip and tcp containing HTTP GET/POST are returned to userspace, others dropped
13
+ #
14
+ #python script uses bcc BPF Compiler Collection by iovisor (https://github.com/iovisor/bcc)
15
+ #and prints on stdout the first line of the HTTP GET/POST request containing the url
16
+
17
+ require 'rbbcc'
18
+ require 'socket'
19
+ require 'io/nonblock'
20
+ include RbBCC
21
+
22
+ def usage
23
+ puts <<-USAGE
24
+ USAGE: #{$0} [-i <if_name>]
25
+ USAGE
26
+ exit
27
+ end
28
+
29
+ interface = "eth0"
30
+
31
+ if ARGV.size == 2
32
+ if ARGV[0] == '-i'
33
+ interface = ARGV[1]
34
+ else
35
+ usage
36
+ end
37
+ elsif ARGV.size != 0
38
+ usage
39
+ end
40
+
41
+ puts("binding socket to '%s'" % interface)
42
+
43
+ bpf = BCC.new(src_file: "http-parse-simple.c")
44
+
45
+ function_http_filter = bpf.load_func("http_filter", BPF::SOCKET_FILTER)
46
+
47
+ BCC.attach_raw_socket(function_http_filter, interface)
48
+
49
+ socket_fd = function_http_filter[:sock]
50
+
51
+ sock = Socket.for_fd socket_fd
52
+ sock.nonblock = false
53
+
54
+ ETH_HLEN = 14
55
+ loop do
56
+ packet_str = sock.sysread(2048)
57
+ packet_bytearray = packet_str.bytes
58
+
59
+ # See original comment...
60
+ #calculate packet total length
61
+ total_length = packet_bytearray[ETH_HLEN + 2] #load MSB
62
+ total_length = total_length << 8 #shift MSB
63
+ total_length = total_length + packet_bytearray[ETH_HLEN + 3] #add LSB
64
+
65
+ #calculate ip header length
66
+ ip_header_length = packet_bytearray[ETH_HLEN] #load Byte
67
+ ip_header_length = ip_header_length & 0x0F #mask bits 0..3
68
+ ip_header_length = ip_header_length << 2 #shift to obtain length
69
+
70
+ tcp_header_length = packet_bytearray[ETH_HLEN + ip_header_length + 12] #load Byte
71
+ tcp_header_length = tcp_header_length & 0xF0 #mask bit 4..7
72
+ tcp_header_length = tcp_header_length >> 2 #SHR 4 ; SHL 2 -> SHR 2
73
+
74
+ payload_offset = ETH_HLEN + ip_header_length + tcp_header_length
75
+
76
+ ((payload_offset-1)..(packet_bytearray.size-1)).each do |i|
77
+ if packet_bytearray[i] == 0x0A
78
+ if packet_bytearray[i-1] == 0x0D
79
+ break
80
+ end
81
+ end
82
+ print(packet_bytearray[i].chr)
83
+ end
84
+ puts
85
+ end
@@ -0,0 +1,105 @@
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
@@ -0,0 +1,204 @@
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*' | grep -v musl | head -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