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.
- checksums.yaml +4 -4
- data/.semaphore/semaphore.yml +3 -1
- data/Gemfile +5 -0
- data/Gemfile.lock +6 -6
- data/README.md +4 -0
- data/docs/README.md +2 -0
- data/docs/answers/01-hello-world.rb +16 -0
- data/docs/answers/02-sys_sync.rb +18 -0
- data/docs/answers/03-hello_fields.rb +33 -0
- data/docs/answers/04-sync_timing.rb +46 -0
- data/docs/answers/05-sync_count.rb +54 -0
- data/docs/answers/06-disksnoop.rb +71 -0
- data/docs/answers/07-hello_perf_output.rb +59 -0
- data/docs/answers/08-sync_perf_output.rb +60 -0
- data/docs/answers/09-bitehist.rb +32 -0
- data/docs/answers/10-disklatency.rb +51 -0
- data/docs/answers/11-vfsreadlat.c +46 -0
- data/docs/answers/11-vfsreadlat.rb +66 -0
- data/docs/answers/12-urandomread.rb +38 -0
- data/docs/answers/13-disksnoop_fixed.rb +108 -0
- data/docs/answers/14-strlen_count.rb +46 -0
- data/docs/answers/15-nodejs_http_server.rb +44 -0
- data/docs/answers/16-task_switch.c +23 -0
- data/docs/answers/16-task_switch.rb +17 -0
- data/docs/answers/node-server.js +11 -0
- data/docs/projects_using_rbbcc.md +43 -0
- data/docs/tutorial_bcc_ruby_developer.md +774 -0
- data/docs/tutorial_bcc_ruby_developer_japanese.md +770 -0
- data/examples/networking/http_filter/http-parse-simple.c +114 -0
- data/examples/networking/http_filter/http-parse-simple.rb +85 -0
- data/examples/ruby_usdt.rb +105 -0
- data/examples/sbrk_trace.rb +204 -0
- data/examples/tools/bashreadline.rb +83 -0
- data/lib/rbbcc/bcc.rb +73 -20
- data/lib/rbbcc/clib.rb +7 -2
- data/lib/rbbcc/debug.rb +17 -0
- data/lib/rbbcc/table.rb +16 -22
- data/lib/rbbcc/usdt.rb +21 -4
- data/lib/rbbcc/version.rb +1 -1
- data/rbbcc.gemspec +1 -5
- 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
|