rbbcc 0.1.1 → 0.2.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: ccce892b1f35442a6a076717e95ff3d0dd1f3ea9b7454042ea1ed6edcafdf3ef
4
- data.tar.gz: 928a6c4f96aec375d0d736122fefbbcfcabaafcb5be6ee0945facfcc59c8521a
3
+ metadata.gz: 7d467cf3f971af1d5a6cc25342768dc87c50edd97cc236fa2610e4de5a49fe45
4
+ data.tar.gz: 83495ff5a1dc88c671db426690e1075641cb50dd6734f078a8e082f32a148e4e
5
5
  SHA512:
6
- metadata.gz: 0b9aefc4bec5f184ae268fe4191bb5de1271e52ce8a6198fe38bae9e558dd273f4c893675581c9d0700d67a3e55d5b5a961d145431ed9ca01785ed4e9183af19
7
- data.tar.gz: 9b5dabad56e3ee0453473aeca770251ba3453cb0e822503c0ad86fc8016a2768a62444b05191303e6a11bdd88aaf353097d691dc1e59d12cb41bf4d694b68f47
6
+ metadata.gz: 35f38c830eb046b72da90dddff2e9f6e050a34b0c0bb82c372bdcc144ab82bab3ba3e8ec6450879f3d086f7d4437f99fdb55b54f4b204974c9fa0fb2940892bc
7
+ data.tar.gz: 9f14a1979cf8892920b443915f5773e743562ae5f391ed0e9bdbaa97d0524932f0c4fa1a5397f4987a0e5c4bf2d404755c6eb55bd0cd6a687279fcce4bc66e63
data/docs/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  * [Getting Started](getting_started.md)
4
4
 
5
- **Please see also [BCC itselfe's README](https://github.com/iovisor/bcc/#readme) and [docs](https://github.com/iovisor/bcc/tree/master/docs).**
5
+ **Please see also [BCC itself's README](https://github.com/iovisor/bcc/#readme) and [docs](https://github.com/iovisor/bcc/tree/master/docs).**
@@ -5,14 +5,22 @@ include RbBCC
5
5
 
6
6
  code = <<CLANG
7
7
  #include <uapi/linux/ptrace.h>
8
- int kprobe__sys_execve(struct pt_regs *ctx) {
9
- if (!PT_REGS_PARM1(ctx))
10
- return 0;
11
- char str[80] = {};
12
- bpf_probe_read(&str, sizeof(str), (void *)PT_REGS_PARM1(ctx));
13
- bpf_trace_printk("execve detected: %s\\n", &str);
8
+ #define ARGSIZE 128
9
+
10
+ // must be syscall__${the_name} ?
11
+ int syscall__execve(struct pt_regs *ctx,
12
+ const char __user *filename,
13
+ const char __user *const __user *__argv,
14
+ const char __user *const __user *__envp)
15
+ {
16
+ char buf[ARGSIZE] = {};
17
+ bpf_probe_read(buf, sizeof(buf), (void *)filename);
18
+ bpf_trace_printk("execve: %s\\n", &buf);
19
+
14
20
  return 0;
15
21
  }
16
22
  CLANG
17
23
 
18
- BCC.new(text: code).trace_print
24
+ b = BCC.new(text: code)
25
+ b.attach_kprobe(event: b.get_syscall_fnname("execve"), fn_name: "syscall__execve")
26
+ b.trace_print
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # execsnoop Trace new processes via exec() syscalls.
4
+ # For Linux, uses BCC, eBPF. Embedded C.
5
+ # originally from tools/execsnoop.py
6
+ #
7
+ # USAGE: execsnoop [-h] [-t] [-x] [-n NAME]
8
+ #
9
+ # This currently will print up to a maximum of 19 arguments, plus the process
10
+ # name, so 20 fields in total (MAXARG).
11
+ #
12
+ # This won't catch all new processes: an application may fork() but not exec().
13
+ #
14
+ # Copyright 2016 Netflix, Inc.
15
+ # Licensed under the Apache License, Version 2.0 (the "License")
16
+ #
17
+ # 07-Feb-2016 Brendan Gregg Created execsnoop.py.
18
+
19
+ require 'optparse'
20
+ require 'ostruct'
21
+ require 'rbbcc'
22
+ include RbBCC
23
+
24
+ examples = <<USAGE
25
+ examples:
26
+ ./execsnoop.rb # trace all exec() syscalls
27
+ ./execsnoop.rb -x # include failed exec()s
28
+ ./execsnoop.rb -t # include timestamps
29
+ ./execsnoop.rb -q # add "quotemarks" around arguments
30
+ ./execsnoop.rb -n main # only print command lines containing "main"
31
+ ./execsnoop.rb -l tpkg # only print command where arguments contains "tpkg"
32
+ USAGE
33
+
34
+ args = OpenStruct.new
35
+ parser = OptionParser.new
36
+ parser.banner = <<BANNER
37
+ usage: execsnoop.rb [-h] [-t] [-x] [-q] [-n NAME] [-l LINE]
38
+ [--max-args MAX_ARGS]
39
+
40
+ Trace exec() syscalls
41
+
42
+ optional arguments:
43
+ BANNER
44
+ parser.on("-t", "--timestamp", "include timestamp on output") {|v| args.timestamp = v }
45
+ parser.on("-x", "--fails", "include failed exec()s") {|v| args.fails = v }
46
+ parser.on("-q", "--quote", "Add quotemarks (\") around arguments.") {|v| args.quote = v }
47
+ parser.on("-n", "--name NAME", "only print commands matching this name (regex), any arg") {|v| args.name = v }
48
+ parser.on("-l", "--line LINE", "only print commands where arg contains this line (regex)") {|v| args.line = v }
49
+ parser.on("--max-args MAX_ARGS", "maximum number of arguments parsed and displayed, defaults to 20") {|v| args.max_arges = v }
50
+ parser.on("--ebpf") {|v| opt.ebpf = v }
51
+
52
+ parser.on_tail("-h", "--help", "show this help message and exit") do
53
+ puts parser
54
+ puts
55
+ puts examples
56
+ exit
57
+ end
58
+
59
+ parser.parse!
60
+ args.max_arges ||= "20"
61
+
62
+ bpf_text = <<CLANG
63
+ #include <uapi/linux/ptrace.h>
64
+ #include <linux/sched.h>
65
+ #include <linux/fs.h>
66
+
67
+ #define ARGSIZE 128
68
+
69
+ enum event_type {
70
+ EVENT_ARG,
71
+ EVENT_RET,
72
+ };
73
+
74
+ struct data_t {
75
+ u32 pid; // PID as in the userspace term (i.e. task->tgid in kernel)
76
+ u32 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
77
+ char comm[TASK_COMM_LEN];
78
+ enum event_type type;
79
+ char argv[ARGSIZE];
80
+ int retval;
81
+ };
82
+
83
+ BPF_PERF_OUTPUT(events);
84
+
85
+ static int __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
86
+ {
87
+ bpf_probe_read(data->argv, sizeof(data->argv), ptr);
88
+ events.perf_submit(ctx, data, sizeof(struct data_t));
89
+ return 1;
90
+ }
91
+
92
+ static int submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
93
+ {
94
+ const char *argp = NULL;
95
+ bpf_probe_read(&argp, sizeof(argp), ptr);
96
+ if (argp) {
97
+ return __submit_arg(ctx, (void *)(argp), data);
98
+ }
99
+ return 0;
100
+ }
101
+
102
+ int syscall__execve(struct pt_regs *ctx,
103
+ const char __user *filename,
104
+ const char __user *const __user *__argv,
105
+ const char __user *const __user *__envp)
106
+ {
107
+ // create data here and pass to submit_arg to save stack space (#555)
108
+ struct data_t data = {};
109
+ struct task_struct *task;
110
+
111
+ data.pid = bpf_get_current_pid_tgid() >> 32;
112
+
113
+ task = (struct task_struct *)bpf_get_current_task();
114
+ // Some kernels, like Ubuntu 4.13.0-generic, return 0
115
+ // as the real_parent->tgid.
116
+ // We use the get_ppid function as a fallback in those cases. (#1883)
117
+ data.ppid = task->real_parent->tgid;
118
+
119
+ bpf_get_current_comm(&data.comm, sizeof(data.comm));
120
+ data.type = EVENT_ARG;
121
+
122
+ __submit_arg(ctx, (void *)filename, &data);
123
+
124
+ // skip first arg, as we submitted filename
125
+ #pragma unroll
126
+ for (int i = 1; i < MAXARG; i++) {
127
+ if (submit_arg(ctx, (void *)&__argv[i], &data) == 0)
128
+ goto out;
129
+ }
130
+
131
+ // handle truncated argument list
132
+ char ellipsis[] = "...";
133
+ __submit_arg(ctx, (void *)ellipsis, &data);
134
+ out:
135
+ return 0;
136
+ }
137
+
138
+ int do_ret_sys_execve(struct pt_regs *ctx)
139
+ {
140
+ struct data_t data = {};
141
+ struct task_struct *task;
142
+
143
+ data.pid = bpf_get_current_pid_tgid() >> 32;
144
+
145
+ task = (struct task_struct *)bpf_get_current_task();
146
+ // Some kernels, like Ubuntu 4.13.0-generic, return 0
147
+ // as the real_parent->tgid.
148
+ // We use the get_ppid function as a fallback in those cases. (#1883)
149
+ data.ppid = task->real_parent->tgid;
150
+
151
+ bpf_get_current_comm(&data.comm, sizeof(data.comm));
152
+ data.type = EVENT_RET;
153
+ data.retval = PT_REGS_RC(ctx);
154
+ events.perf_submit(ctx, &data, sizeof(data));
155
+
156
+ return 0;
157
+ }
158
+ CLANG
159
+
160
+ EVENT_ARG = 0
161
+ EVENT_RET = 1
162
+
163
+ # This is best-effort PPID matching. Short-lived processes may exit
164
+ # before we get a chance to read the PPID.
165
+ # This is a fallback for when fetching the PPID from task->real_parent->tgip
166
+ # returns 0, which happens in some kernel versions.
167
+ def get_ppid(pid)
168
+ line = File.read("/proc/%d/status" % pid).lines.grep(/^PPid:/)
169
+ if line.empty?
170
+ 0
171
+ else
172
+ line[0].split[1].to_i
173
+ end
174
+ rescue
175
+ 0
176
+ end
177
+
178
+ bpf_text.sub!("MAXARG", args.max_args)
179
+ if args.ebpf
180
+ puts(bpf_text)
181
+ exit
182
+ end
183
+
184
+ b = BCC.new(text: bpf_text)
185
+ execve_fnname = b.get_syscall_fnname("execve")
186
+ b.attach_kprobe(event: execve_fnname, fn_name: "syscall__execve")
187
+ b.attach_kretprobe(event: execve_fnname, fn_name: "do_ret_sys_execve")
188
+
189
+ printf("%-8s", "TIME(s)") if args.timestamp
190
+ printf("%-16s %-6s %-6s %3s %s\n", "PCOMM", "PID", "PPID", "RET", "ARGS")
191
+
192
+ start_ts = Time.now.to_f
193
+ argv = []
194
+
195
+ b["events"].open_perf_buffer do |cpu, data, size|
196
+ event = b["events"].event(data)
197
+ skip = false
198
+ if event.type == EVENT_ARG
199
+ argv[event.pid] ||= []
200
+ argv[event.pid] << event.argv
201
+ elsif event.type == EVENT_RET
202
+ skip = true if event.retval != 0 && !args.fails
203
+ skip = true if args.name && /#{args.name}/ !~ event.comm
204
+ skip = true if args.line && /#{args.line}/ !~ argv[event.pid].join(' ')
205
+ if args.quote
206
+ argv[event.pid] = argv[event.pid].map{|arg| '"' + arg.gsub(/\"/, '\\"') + '"' }
207
+ end
208
+
209
+ unless skip
210
+ ppid_ = event.ppid > 0 ? event.ppid : get_ppid(event.pid)
211
+ ppid = ppid_ > 0 ? ppid_.to_s : "?"
212
+ # FIXME: get empty argv[pid] in some cases...
213
+ argv_text = argv[event.pid].join(' ').gsub(/\n/, '\\n') rescue ""
214
+ printf("%-8.3f", (Time.now.to_f - start_ts)) if args.timestamp
215
+ printf("%-16s %-6d %-6s %3d %s\n",
216
+ event.comm, event.pid, ppid, event.retval, argv_text)
217
+ end
218
+
219
+ argv[event.pid] = nil
220
+ end
221
+ end
222
+
223
+ loop do
224
+ begin
225
+ b.perf_buffer_poll()
226
+ rescue Interrupt
227
+ exit
228
+ end
229
+ end
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env ruby
2
+ # @lint-avoid-python-3-compatibility-imports
3
+ #
4
+ # runqlat Run queue (scheduler) latency as a histogram.
5
+ # For Linux, uses BCC, eBPF.
6
+ #
7
+ # USAGE: runqlat [-h] [-T] [-m] [-P] [-L] [-p PID] [interval] [count]
8
+ #
9
+ # This measures the time a task spends waiting on a run queue for a turn
10
+ # on-CPU, and shows this time as a histogram. This time should be small, but a
11
+ # task may need to wait its turn due to CPU load.
12
+ #
13
+ # This measures two types of run queue latency:
14
+ # 1. The time from a task being enqueued on a run queue to its context switch
15
+ # and execution. This traces ttwu_do_wakeup(), wake_up_new_task() ->
16
+ # finish_task_switch() with either raw tracepoints (if supported) or kprobes
17
+ # and instruments the run queue latency after a voluntary context switch.
18
+ # 2. The time from when a task was involuntary context switched and still
19
+ # in the runnable state, to when it next executed. This is instrumented
20
+ # from finish_task_switch() alone.
21
+ #
22
+ # Copyright 2016 Netflix, Inc.
23
+ # Licensed under the Apache License, Version 2.0 (the "License")
24
+ #
25
+ # 07-Feb-2016 Brendan Gregg Created this.
26
+
27
+ require 'rbbcc'
28
+ include RbBCC
29
+
30
+ bpf_text = <<TXT
31
+ #include <uapi/linux/ptrace.h>
32
+ #include <linux/sched.h>
33
+ #include <linux/nsproxy.h>
34
+ #include <linux/pid_namespace.h>
35
+
36
+ typedef struct pid_key {
37
+ u64 id; // work around
38
+ u64 slot;
39
+ } pid_key_t;
40
+
41
+ typedef struct pidns_key {
42
+ u64 id; // work around
43
+ u64 slot;
44
+ } pidns_key_t;
45
+
46
+ BPF_HASH(start, u32);
47
+ STORAGE
48
+
49
+ struct rq;
50
+
51
+ // record enqueue timestamp
52
+ static int trace_enqueue(u32 tgid, u32 pid)
53
+ {
54
+ if (FILTER || pid == 0)
55
+ return 0;
56
+ u64 ts = bpf_ktime_get_ns();
57
+ start.update(&pid, &ts);
58
+ return 0;
59
+ }
60
+ TXT
61
+
62
+ bpf_text_raw_tp = <<TXT
63
+ RAW_TRACEPOINT_PROBE(sched_wakeup)
64
+ {
65
+ // TP_PROTO(struct task_struct *p)
66
+ struct task_struct *p = (struct task_struct *)ctx->args[0];
67
+ return trace_enqueue(p->tgid, p->pid);
68
+ }
69
+
70
+ RAW_TRACEPOINT_PROBE(sched_wakeup_new)
71
+ {
72
+ // TP_PROTO(struct task_struct *p)
73
+ struct task_struct *p = (struct task_struct *)ctx->args[0];
74
+ return trace_enqueue(p->tgid, p->pid);
75
+ }
76
+
77
+ RAW_TRACEPOINT_PROBE(sched_switch)
78
+ {
79
+ // TP_PROTO(bool preempt, struct task_struct *prev, struct task_struct *next)
80
+ struct task_struct *prev = (struct task_struct *)ctx->args[1];
81
+ struct task_struct *next = (struct task_struct *)ctx->args[2];
82
+ u32 pid, tgid;
83
+
84
+ // ivcsw: treat like an enqueue event and store timestamp
85
+ if (prev->state == TASK_RUNNING) {
86
+ tgid = prev->tgid;
87
+ pid = prev->pid;
88
+ if (!(FILTER || pid == 0)) {
89
+ u64 ts = bpf_ktime_get_ns();
90
+ start.update(&pid, &ts);
91
+ }
92
+ }
93
+
94
+ tgid = next->tgid;
95
+ pid = next->pid;
96
+ if (FILTER || pid == 0)
97
+ return 0;
98
+ u64 *tsp, delta;
99
+
100
+ // fetch timestamp and calculate delta
101
+ tsp = start.lookup(&pid);
102
+ if (tsp == 0) {
103
+ return 0; // missed enqueue
104
+ }
105
+ delta = bpf_ktime_get_ns() - *tsp;
106
+ FACTOR
107
+
108
+ // store as histogram
109
+ STORE
110
+
111
+ start.delete(&pid);
112
+ return 0;
113
+ }
114
+ TXT
115
+
116
+ is_support_raw_tp = BCC.support_raw_tracepoint
117
+ if is_support_raw_tp
118
+ bpf_text += bpf_text_raw_tp
119
+ else
120
+ raise "BCC.support_raw_tracepoint is false; Unsupported kernel version: ${`uname -a`.chomp}"
121
+ end
122
+
123
+ bpf_text.gsub!('FILTER', '0')
124
+ bpf_text.gsub!('FACTOR', 'delta /= 1000;')
125
+ label = "usecs"
126
+ bpf_text.gsub!('STORAGE', 'BPF_HISTOGRAM(dist);')
127
+ bpf_text.gsub!('STORE',
128
+ 'dist.increment(bpf_log2l(delta));')
129
+ section = ""
130
+
131
+ b = BCC.new(text: bpf_text)
132
+
133
+ puts("Tracing run queue latency... Hit Ctrl-C to end.")
134
+
135
+ # TODO: interval
136
+ loop do
137
+ begin
138
+ sleep 1
139
+ rescue Interrupt
140
+ puts
141
+ break
142
+ end
143
+ end
144
+
145
+ dist = b.get_table("dist")
146
+ dist.print_log2_hist(label, section_header: section, section_print_fn: :to_i)
147
+ # dist.clear()
148
+ exit
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # urandomread-explicit.rb Example of instrumenting a kernel tracepoint.
4
+ # For Linux, uses BCC, BPF. Embedded C.
5
+ # Originally urandomread-explicit.py in BCC
6
+ #
7
+ # This is an older example of instrumenting a tracepoint, which defines
8
+ # the argument struct and makes an explicit call to attach_tracepoint().
9
+ # See urandomread for a newer version that uses TRACEPOINT_PROBE().
10
+ #
11
+ # REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support).
12
+ #
13
+ # Test by running this, then in another shell, run:
14
+ # dd if=/dev/urandom of=/dev/null bs=1k count=5
15
+ #
16
+ # Copyright 2016 Netflix, Inc.
17
+ # Licensed under the Apache License, Version 2.0 (the "License")
18
+
19
+ require 'rbbcc'
20
+ include RbBCC
21
+
22
+ # define BPF program
23
+ bpf_text = <<CLANG
24
+ #include <uapi/linux/ptrace.h>
25
+
26
+ struct urandom_read_args {
27
+ // from /sys/kernel/debug/tracing/events/random/urandom_read/format
28
+ u64 __unused__;
29
+ u32 got_bits;
30
+ u32 pool_left;
31
+ u32 input_left;
32
+ };
33
+
34
+ int printarg(struct urandom_read_args *args) {
35
+ bpf_trace_printk("%d\\n", args->got_bits);
36
+ return 0;
37
+ }
38
+ CLANG
39
+
40
+ # load BPF program
41
+ b = BCC.new(text: bpf_text)
42
+ b.attach_tracepoint(tp: "random:urandom_read", fn_name: "printarg")
43
+
44
+ # header
45
+ printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "GOTBITS")
46
+
47
+ # format output
48
+ loop do
49
+ begin
50
+ b.trace_fields do |task, pid, cpu, flags, ts, msg|
51
+ puts("%-18.9f %-16s %-6d %s" % [ts, task, pid, msg])
52
+ end
53
+ rescue Interrupt
54
+ exit
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # urandomread.rb Example of instrumenting a kernel tracepoint.
4
+ # For Linux, uses BCC, BPF. Embedded C.
5
+ #
6
+ # REQUIRES: Linux 4.7+ (BPF_PROG_TYPE_TRACEPOINT support).
7
+ #
8
+ # Test by running this, then in another shell, run:
9
+ # dd if=/dev/urandom of=/dev/null bs=1k count=5
10
+ #
11
+ # Copyright 2016 Netflix, Inc.
12
+ # Licensed under the Apache License, Version 2.0 (the "License")
13
+
14
+ require 'rbbcc'
15
+ include RbBCC
16
+
17
+ b = BCC.new(text: %|
18
+ TRACEPOINT_PROBE(random, urandom_read) {
19
+ // args is from /sys/kernel/debug/tracing/events/random/urandom_read/format
20
+ bpf_trace_printk("%d\\n", args->got_bits);
21
+ return 0;
22
+ }
23
+ |)
24
+
25
+ # header
26
+ printf("%-18s %-16s %-6s %s\n", "TIME(s)", "COMM", "PID", "GOTBITS")
27
+
28
+ # format output
29
+ loop do
30
+ begin
31
+ b.trace_fields do |task, pid, cpu, flags, ts, msg|
32
+ puts("%-18.9f %-16s %-6d %s" % [ts, task, pid, msg])
33
+ end
34
+ rescue Interrupt
35
+ exit
36
+ end
37
+ end
data/lib/rbbcc/bcc.rb CHANGED
@@ -14,6 +14,20 @@ module RbBCC
14
14
 
15
15
  class BCC
16
16
  class << self
17
+ def ksym(addr, show_module: false, show_offset: false)
18
+ self.sym(addr, -1, show_module: show_module, show_offset: show_offset, demangle: false)
19
+ end
20
+
21
+ def ksymname(name)
22
+ SymbolCache.resolve_global(name)
23
+ end
24
+
25
+ def support_raw_tracepoint
26
+ # kernel symbol "bpf_find_raw_tracepoint"
27
+ # indicates raw_tracepint support
28
+ ksymname("bpf_find_raw_tracepoint") || ksymname("bpf_get_raw_tracepoint")
29
+ end
30
+
17
31
  def get_kprobe_functions(event_re)
18
32
  blacklist = []
19
33
  fns = []
@@ -152,6 +166,8 @@ module RbBCC
152
166
  def initialize(text:, debug: 0, cflags: [], usdt_contexts: [], allow_rlimit: 0)
153
167
  @kprobe_fds = {}
154
168
  @uprobe_fds = {}
169
+ @tracepoint_fds = {}
170
+ @raw_tracepoint_fds = {}
155
171
  @usdt_contexts = usdt_contexts
156
172
  if code = gen_args_from_usdt
157
173
  text = code + text
@@ -191,12 +207,7 @@ module RbBCC
191
207
  return nil
192
208
  end
193
209
 
194
- idx = 0
195
- while ptr[idx, 1] != "\x00"
196
- idx += 1
197
- end
198
- ptr.size = idx + 1
199
- ptr.to_s
210
+ Clib.__extract_char ptr
200
211
  end
201
212
 
202
213
  def load_func(func_name, prog_type)
@@ -219,12 +230,52 @@ module RbBCC
219
230
  return fnobj
220
231
  end
221
232
 
233
+ def attach_tracepoint(tp: "", tp_re: "", fn_name: "")
234
+ fn = load_func(fn_name, BPF::TRACEPOINT)
235
+ tp_category, tp_name = tp.split(':')
236
+ fd = Clib.bpf_attach_tracepoint(fn[:fd], tp_category, tp_name)
237
+ if fd < 0
238
+ raise SystemCallError.new("Failed to attach BPF program #{fn_name} to tracepoint #{tp}", Fiddle.last_error)
239
+ end
240
+ puts "Attach: #{tp}"
241
+ @tracepoint_fds[tp] = fd
242
+ self
243
+ end
244
+
245
+ def attach_raw_tracepoint(tp: "", fn_name: "")
246
+ if @raw_tracepoint_fds.keys.include?(tp)
247
+ raise "Raw tracepoint #{tp} has been attached"
248
+ end
249
+
250
+ fn = load_func(fn_name, BPF::RAW_TRACEPOINT)
251
+ fd = Clib.bpf_attach_raw_tracepoint(fn[:fd], tp)
252
+ if fd < 0
253
+ raise SystemCallError.new("Failed to attach BPF program #{fn_name} to raw tracepoint #{tp}", Fiddle.last_error)
254
+ end
255
+ puts "Attach: #{tp}"
256
+ @raw_tracepoint_fds[tp] = fd
257
+ self
258
+ end
259
+
222
260
  def attach_kprobe(event:, fn_name:, event_off: 0)
223
261
  fn = load_func(fn_name, BPF::KPROBE)
224
262
  ev_name = "p_" + event.gsub(/[\+\.]/, "_")
225
263
  fd = Clib.bpf_attach_kprobe(fn[:fd], 0, ev_name, event, event_off, 0)
226
264
  if fd < 0
227
- raise SystemCallError.new(Fiddle.last_error)
265
+ raise SystemCallError.new("Failed to attach BPF program #{fn_name} to kprobe #{event}", Fiddle.last_error)
266
+ end
267
+ puts "Attach: #{ev_name}"
268
+ @kprobe_fds[ev_name] = fd
269
+ [ev_name, fd]
270
+ end
271
+
272
+ def attach_kretprobe(event:, fn_name:, event_re: nil, maxactive: 0)
273
+ # TODO: if event_re ...
274
+ fn = load_func(fn_name, BPF::KPROBE)
275
+ ev_name = "r_" + event.gsub(/[\+\.]/, "_")
276
+ fd = Clib.bpf_attach_kprobe(fn[:fd], 1, ev_name, event, 0, maxactive)
277
+ if fd < 0
278
+ raise SystemCallError.new("Failed to attach BPF program #{fn_name} to kretprobe #{event}", Fiddle.last_error)
228
279
  end
229
280
  puts "Attach: #{ev_name}"
230
281
  @kprobe_fds[ev_name] = fd
@@ -261,6 +312,34 @@ module RbBCC
261
312
  [ev_name, fd]
262
313
  end
263
314
 
315
+ def detach_tracepoint(tp)
316
+ unless @tracepoint_fds.keys.include?(tp)
317
+ raise "Tracepoint #{tp} is not attached"
318
+ end
319
+ res = Clib.bpf_close_perf_event_fd(@tracepoint_fds[tp])
320
+ if res < 0
321
+ raise "Failed to detach BPF from tracepoint"
322
+ end
323
+ tp_category, tp_name = tp.split(':')
324
+ res = Clib.bpf_detach_tracepoint(tp_category, tp_name)
325
+ if res < 0
326
+ raise "Failed to detach BPF from tracepoint"
327
+ end
328
+ @tracepoint_fds.delete(tp)
329
+ end
330
+
331
+ def detach_raw_tracepoint(tp)
332
+ unless @raw_tracepoint_fds.keys.include?(tp)
333
+ raise "Raw tracepoint #{tp} is not attached"
334
+ end
335
+ begin
336
+ File.for_fd(@raw_tracepoint_fds[tp]).close
337
+ rescue => e
338
+ warn "Closing fd failed: #{e.inspect}. Ignore and skip"
339
+ end
340
+ @tracepoint_fds.delete(tp)
341
+ end
342
+
264
343
  def detach_kprobe_event(ev_name)
265
344
  unless @kprobe_fds.keys.include?(ev_name)
266
345
  raise "Event #{ev_name} not registered"
@@ -287,6 +366,18 @@ module RbBCC
287
366
  @uprobe_fds.delete(ev_name)
288
367
  end
289
368
 
369
+ def num_open_kprobes
370
+ @kprobe_fds.size
371
+ end
372
+
373
+ def num_open_uprobes
374
+ @uprobe_fds.size
375
+ end
376
+
377
+ def num_open_tracepoints
378
+ @tracepoint_fds.size
379
+ end
380
+
290
381
  def tracefile
291
382
  @tracefile ||= File.open("#{TRACEFS}/trace_pipe", "rb")
292
383
  end
@@ -335,6 +426,14 @@ module RbBCC
335
426
  detach_uprobe_event(k)
336
427
  end
337
428
 
429
+ @tracepoint_fds.each do |k, v|
430
+ detach_tracepoint(k)
431
+ end
432
+
433
+ @raw_tracepoint_fds.each do |k, v|
434
+ detach_raw_tracepoint(k)
435
+ end
436
+
338
437
  if @module
339
438
  Clib.bpf_module_destroy(@module)
340
439
  end
@@ -415,6 +514,26 @@ module RbBCC
415
514
  event: fix_syscall_fnname(func_name[8..-1]),
416
515
  fn_name: fn[:name]
417
516
  )
517
+ elsif func_name.start_with?("kretprobe__")
518
+ fn = load_func(func_name, BPF::KPROBE)
519
+ attach_kretprobe(
520
+ event: fix_syscall_fnname(func_name[11..-1]),
521
+ fn_name: fn[:name]
522
+ )
523
+ elsif func_name.start_with?("tracepoint__")
524
+ fn = load_func(func_name, BPF::TRACEPOINT)
525
+ tp = fn[:name].sub(/^tracepoint__/, "").sub(/__/, ":")
526
+ attach_tracepoint(
527
+ tp: tp,
528
+ fn_name: fn[:name]
529
+ )
530
+ elsif func_name.start_with?("raw_tracepoint__")
531
+ fn = load_func(func_name, BPF::RAW_TRACEPOINT)
532
+ tp = fn[:name].sub(/^raw_tracepoint__/, "")
533
+ attach_raw_tracepoint(
534
+ tp: tp,
535
+ fn_name: fn[:name]
536
+ )
418
537
  end
419
538
  end
420
539
  end
data/lib/rbbcc/clib.rb CHANGED
@@ -38,6 +38,9 @@ module RbBCC
38
38
  extern 'int bpf_detach_kprobe(char *)'
39
39
  extern 'int bpf_attach_uprobe(int, int, char *, char *, unsigned long, int)'
40
40
  extern 'int bpf_detach_uprobe(char *)'
41
+ extern 'int bpf_attach_tracepoint(int progfd, char *tp_category, char *tp_name)'
42
+ extern 'int bpf_detach_tracepoint(char *tp_category, char *tp_name)'
43
+ extern 'int bpf_attach_raw_tracepoint(int progfd, char *tp_name)'
41
44
  extern 'int bpf_open_perf_event(unsigned int, unsigned long, int, int)'
42
45
  extern 'int bpf_close_perf_event_fd(int)'
43
46
  extern 'int bpf_get_first_key(int, void *, int)'
data/lib/rbbcc/table.rb CHANGED
@@ -279,11 +279,17 @@ module RbBCC
279
279
  end
280
280
  end
281
281
  klass = Fiddle::Importer.struct(fields)
282
- if fields.find {|f| f =~ /^char\[(\d+)\] ([_a-zA-Z0-9]+)/ }
283
- size = $1
282
+ char_ps = fields.select {|f| f =~ /^char\[(\d+)\] ([_a-zA-Z0-9]+)/ }
283
+ unless char_ps.empty?
284
284
  m = Module.new do
285
- define_method $2 do
286
- super().pack("c#{size}").sub(/\0+$/, "")
285
+ char_ps.each do |char_p|
286
+ md = /^char\[(\d+)\] ([_a-zA-Z0-9]+)/.match(char_p)
287
+ define_method md[2] do
288
+ # Split the char[] in the place where the first \0 appears
289
+ raw = super()
290
+ raw = raw[0...raw.index(0)] if raw.index(0)
291
+ raw.pack("c*")
292
+ end
287
293
  end
288
294
  end
289
295
  klass.prepend m
data/lib/rbbcc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module RbBCC
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.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.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uchio Kondo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-21 00:00:00.000000000 Z
11
+ date: 2020-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -81,6 +81,10 @@ files:
81
81
  - examples/hello_world.rb
82
82
  - examples/kvm_hypercall.rb
83
83
  - examples/mallocstack.rb
84
+ - examples/tools/execsnoop.rb
85
+ - examples/tools/runqlat.rb
86
+ - examples/urandomread-explicit.rb
87
+ - examples/urandomread.rb
84
88
  - examples/usdt-test.rb
85
89
  - examples/usdt.rb
86
90
  - lib/rbbcc.rb
@@ -114,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
118
  - !ruby/object:Gem::Version
115
119
  version: '0'
116
120
  requirements: []
117
- rubygems_version: 3.0.3
121
+ rubygems_version: 3.0.6
118
122
  signing_key:
119
123
  specification_version: 4
120
124
  summary: BCC port for MRI