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 +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
|