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 +4 -4
- data/docs/README.md +1 -1
- data/examples/extract_arg.rb +15 -7
- data/examples/tools/execsnoop.rb +229 -0
- data/examples/tools/runqlat.rb +148 -0
- data/examples/urandomread-explicit.rb +56 -0
- data/examples/urandomread.rb +37 -0
- data/lib/rbbcc/bcc.rb +126 -7
- data/lib/rbbcc/clib.rb +3 -0
- data/lib/rbbcc/table.rb +10 -4
- data/lib/rbbcc/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d467cf3f971af1d5a6cc25342768dc87c50edd97cc236fa2610e4de5a49fe45
|
4
|
+
data.tar.gz: 83495ff5a1dc88c671db426690e1075641cb50dd6734f078a8e082f32a148e4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
5
|
+
**Please see also [BCC itself's README](https://github.com/iovisor/bcc/#readme) and [docs](https://github.com/iovisor/bcc/tree/master/docs).**
|
data/examples/extract_arg.rb
CHANGED
@@ -5,14 +5,22 @@ include RbBCC
|
|
5
5
|
|
6
6
|
code = <<CLANG
|
7
7
|
#include <uapi/linux/ptrace.h>
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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)
|
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
|
-
|
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
|
-
|
283
|
-
|
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
|
-
|
286
|
-
|
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
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.
|
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:
|
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.
|
121
|
+
rubygems_version: 3.0.6
|
118
122
|
signing_key:
|
119
123
|
specification_version: 4
|
120
124
|
summary: BCC port for MRI
|