rbbcc 0.1.1 → 0.2.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: 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