rbbcc 0.6.1 → 0.6.2
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/Gemfile.lock +7 -7
- data/docs/answers/14-strlen_count.rb +1 -1
- data/examples/collectsyscall.rb +155 -0
- data/exe/rbbcc +4 -0
- data/lib/rbbcc/bcc.rb +28 -3
- data/lib/rbbcc/clib.rb +1 -1
- data/lib/rbbcc/fiddle_ext.rb +34 -2
- data/lib/rbbcc/table.rb +26 -4
- data/lib/rbbcc/version.rb +1 -1
- data/rbbcc.gemspec +2 -2
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb13ef05006088db3a3d16fdf3b5f444cd36a51c1d88278d5ddcb3ccb9e69663
|
4
|
+
data.tar.gz: 9cded3bc218135ef79af4376a8973dedad2e3614ab5c93f49850799842c0e5c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a05af6a130f05942ce7f9901afc8d65434b7b5851483522f875ef3a9d75d1bcb30cafb1b8ce972feea2b273329e736182740d1cc99e561139d82fd1cccd297c4
|
7
|
+
data.tar.gz: '0938ed389aa2b4159618c2c79ebd841e9c74160a8b40877b54075dff9590713f6ccc1d67c27593c13bd0e1bf9fe57d4296eb1a7d58e7f357526de7144e0b8ba4'
|
data/Gemfile.lock
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rbbcc (0.6.
|
4
|
+
rbbcc (0.6.2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
coderay (1.1.
|
10
|
-
method_source (0.
|
11
|
-
minitest (5.14.
|
12
|
-
pry (0.
|
13
|
-
coderay (~> 1.1
|
14
|
-
method_source (~>
|
9
|
+
coderay (1.1.3)
|
10
|
+
method_source (1.0.0)
|
11
|
+
minitest (5.14.2)
|
12
|
+
pry (0.13.1)
|
13
|
+
coderay (~> 1.1)
|
14
|
+
method_source (~> 1.0)
|
15
15
|
rake (13.0.1)
|
16
16
|
|
17
17
|
PLATFORMS
|
@@ -42,5 +42,5 @@ end
|
|
42
42
|
puts("%10s %s" % ["COUNT", "STRING"])
|
43
43
|
counts = b.get_table("counts")
|
44
44
|
counts.items.sort_by{|k, v| v.to_bcc_value }.each do |k, v|
|
45
|
-
puts("%10d %s" % [v.to_bcc_value, k
|
45
|
+
puts("%10d %s" % [v.to_bcc_value, k.to_bcc_value.c])
|
46
46
|
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Example to use complecated structure in BPF Map key:
|
4
|
+
# This program collects and shows raw syscall usage summary.
|
5
|
+
#
|
6
|
+
# Usage:
|
7
|
+
# bundle exec ruby examples/collectsyscall.rb
|
8
|
+
#
|
9
|
+
# Output example:
|
10
|
+
# Collecting syscalls...
|
11
|
+
# ^C
|
12
|
+
# PID=1098(maybe: gmain) --->
|
13
|
+
# inotify_add_watch 4 0.019 ms
|
14
|
+
# poll 1 0.000 ms
|
15
|
+
#
|
16
|
+
# PID=1114(maybe: dbus-daemon) --->
|
17
|
+
# stat 12 0.021 ms
|
18
|
+
# openat 3 0.015 ms
|
19
|
+
# getdents 2 0.013 ms
|
20
|
+
# recvmsg 2 0.006 ms
|
21
|
+
# sendmsg 1 0.008 ms
|
22
|
+
# close 1 0.002 ms
|
23
|
+
# fstat 1 0.002 ms
|
24
|
+
# epoll_wait 1 0.000 ms
|
25
|
+
#
|
26
|
+
# PID=1175(maybe: memcached) --->
|
27
|
+
# epoll_wait 3 2012.455 ms
|
28
|
+
#
|
29
|
+
# PID=1213(maybe: redis-server) --->
|
30
|
+
# read 64 0.736 ms
|
31
|
+
# epoll_wait 32 3782.098 ms
|
32
|
+
# openat 32 1.149 ms
|
33
|
+
# getpid 32 0.074 ms
|
34
|
+
# close 32 0.045 ms
|
35
|
+
# ....
|
36
|
+
|
37
|
+
require 'rbbcc'
|
38
|
+
include RbBCC
|
39
|
+
|
40
|
+
$pid = nil
|
41
|
+
|
42
|
+
if ARGV.size == 2 &&
|
43
|
+
ARGV[0] == '-p'
|
44
|
+
$pid = ARGV[1].to_i
|
45
|
+
elsif ARGV[0] == '-h' ||
|
46
|
+
ARGV[0] == '--help'
|
47
|
+
$stderr.puts "Usage: #{$0} [-p PID]"
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
|
51
|
+
SYSCALL_MAP = `ausyscall --dump`
|
52
|
+
.lines
|
53
|
+
.map{|l| l.chomp.split }
|
54
|
+
.each_with_object(Hash.new) {|(k, v), ha| ha[k.to_i] = v }
|
55
|
+
|
56
|
+
# if no ausyscall(8) then shows number itself
|
57
|
+
# it is included in auditd package (e.g. Ubuntu)
|
58
|
+
def to_name(nr)
|
59
|
+
SYSCALL_MAP[nr] || nr.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
prog = <<BPF
|
63
|
+
#include <uapi/linux/ptrace.h>
|
64
|
+
|
65
|
+
struct key_t {
|
66
|
+
u32 pid;
|
67
|
+
u64 syscall_nr;
|
68
|
+
};
|
69
|
+
struct leaf_t{
|
70
|
+
u64 count;
|
71
|
+
u64 elapsed_ns;
|
72
|
+
u64 enter_ns;
|
73
|
+
char comm[16];
|
74
|
+
};
|
75
|
+
BPF_HASH(store, struct key_t, struct leaf_t);
|
76
|
+
|
77
|
+
TRACEPOINT_PROBE(raw_syscalls, sys_enter) {
|
78
|
+
struct key_t key = {0};
|
79
|
+
struct leaf_t initial = {0}, *val_;
|
80
|
+
|
81
|
+
key.pid = bpf_get_current_pid_tgid();
|
82
|
+
key.syscall_nr = args->id;
|
83
|
+
|
84
|
+
DO_FILTER_BY_PID
|
85
|
+
|
86
|
+
val_ = store.lookup_or_try_init(&key, &initial);
|
87
|
+
if (val_) {
|
88
|
+
struct leaf_t val = *val_;
|
89
|
+
val.count++;
|
90
|
+
val.enter_ns = bpf_ktime_get_ns();
|
91
|
+
bpf_get_current_comm(&val.comm, sizeof(val.comm));
|
92
|
+
store.update(&key, &val);
|
93
|
+
}
|
94
|
+
return 0;
|
95
|
+
}
|
96
|
+
|
97
|
+
TRACEPOINT_PROBE(raw_syscalls, sys_exit) {
|
98
|
+
struct key_t key = {0};
|
99
|
+
struct leaf_t *val_;
|
100
|
+
|
101
|
+
key.pid = bpf_get_current_pid_tgid();
|
102
|
+
key.syscall_nr = args->id;
|
103
|
+
|
104
|
+
val_ = store.lookup(&key);
|
105
|
+
if (val_) {
|
106
|
+
struct leaf_t val = *val_;
|
107
|
+
u64 delta = bpf_ktime_get_ns() - val.enter_ns;
|
108
|
+
val.enter_ns = 0;
|
109
|
+
val.elapsed_ns += delta;
|
110
|
+
store.update(&key, &val);
|
111
|
+
}
|
112
|
+
return 0;
|
113
|
+
}
|
114
|
+
BPF
|
115
|
+
|
116
|
+
if $pid
|
117
|
+
prog.sub!('DO_FILTER_BY_PID', <<~FILTER)
|
118
|
+
if (key.pid != #{$pid}) return 0;
|
119
|
+
FILTER
|
120
|
+
else
|
121
|
+
prog.sub!('DO_FILTER_BY_PID', '')
|
122
|
+
end
|
123
|
+
|
124
|
+
b = BCC.new(text: prog)
|
125
|
+
|
126
|
+
puts "Collecting syscalls..."
|
127
|
+
begin
|
128
|
+
sleep(99999999)
|
129
|
+
rescue Interrupt
|
130
|
+
puts
|
131
|
+
end
|
132
|
+
|
133
|
+
info_by_pids = {}
|
134
|
+
comms = {}
|
135
|
+
store = b.get_table("store")
|
136
|
+
store.items.each do |k, v|
|
137
|
+
# require 'pry'; binding.pry
|
138
|
+
info_by_pids[k.pid] ||= {}
|
139
|
+
info_by_pids[k.pid][k.syscall_nr] = {
|
140
|
+
name: to_name(k.syscall_nr),
|
141
|
+
count: v.count,
|
142
|
+
elapsed_ms: v.elapsed_ns / 1000000.0
|
143
|
+
}
|
144
|
+
comms[k.pid] ||= v.comm
|
145
|
+
end
|
146
|
+
|
147
|
+
pids = info_by_pids.keys.sort
|
148
|
+
pids.each do |pid|
|
149
|
+
puts "PID=#{pid}(maybe: #{comms[pid]}) --->"
|
150
|
+
i = info_by_pids[pid]
|
151
|
+
i.to_a.sort_by {|k, v| [-v[:count], -v[:elapsed_ms]] }.each do |nr, record|
|
152
|
+
puts "\t%<name>-20s %<count>3d %<elapsed_ms>8.3f ms" % record
|
153
|
+
end
|
154
|
+
puts
|
155
|
+
end
|
data/exe/rbbcc
ADDED
data/lib/rbbcc/bcc.rb
CHANGED
@@ -121,7 +121,6 @@ module RbBCC
|
|
121
121
|
|
122
122
|
def decode_table_type(desc)
|
123
123
|
return desc if desc.is_a?(String)
|
124
|
-
|
125
124
|
anon = []
|
126
125
|
fields = []
|
127
126
|
# e.g. ["bpf_stacktrace", [["ip", "unsigned long long", [127]]], "struct_packed"]
|
@@ -153,11 +152,37 @@ module RbBCC
|
|
153
152
|
raise("Failed to decode type #{field.inspect}")
|
154
153
|
end
|
155
154
|
end
|
155
|
+
c = nil
|
156
156
|
if data_type == "union"
|
157
|
-
|
157
|
+
c = Fiddle::Importer.union(fields)
|
158
158
|
else
|
159
|
-
|
159
|
+
c = Fiddle::Importer.struct(fields)
|
160
|
+
end
|
161
|
+
|
162
|
+
fields.each do |field|
|
163
|
+
md = /^char\[(\d+)\] ([_a-zA-Z0-9]+)/.match(field)
|
164
|
+
if md
|
165
|
+
c.alias_method "__super_#{md[2]}", md[2]
|
166
|
+
c.define_method md[2] do
|
167
|
+
# Split the char[] in the place where the first \0 appears
|
168
|
+
raw = __send__("__super_#{md[2]}")
|
169
|
+
raw = raw[0...raw.index(0)] if raw.index(0)
|
170
|
+
raw.pack("c*")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
c.define_singleton_method :original_desc do
|
176
|
+
desc
|
177
|
+
end
|
178
|
+
c.define_singleton_method :fields do
|
179
|
+
fields
|
180
|
+
end
|
181
|
+
orig_name = c.inspect
|
182
|
+
c.define_singleton_method :inspect do
|
183
|
+
orig_name.sub /(?=>$)/, " original_desc=#{desc.inspect}" rescue super
|
160
184
|
end
|
185
|
+
c
|
161
186
|
end
|
162
187
|
|
163
188
|
def sym(addr, pid, show_module: false, show_offset: false, demangle: true)
|
data/lib/rbbcc/clib.rb
CHANGED
@@ -25,7 +25,7 @@ module RbBCC
|
|
25
25
|
end
|
26
26
|
|
27
27
|
extend Fiddle::Importer
|
28
|
-
targets = %w(0.14.0 0.13.0 0.12.0 0.11.0 0.10.0)
|
28
|
+
targets = %w(0.17.0 0.16.0 0.15.0 0.14.0 0.13.0 0.12.0 0.11.0 0.10.0)
|
29
29
|
if default_load = ENV['LIBBCC_VERSION']
|
30
30
|
targets.unshift(default_load)
|
31
31
|
targets.uniq!
|
data/lib/rbbcc/fiddle_ext.rb
CHANGED
@@ -2,8 +2,17 @@ require 'fiddle'
|
|
2
2
|
require 'fiddle/import'
|
3
3
|
|
4
4
|
class Fiddle::Pointer
|
5
|
-
def
|
6
|
-
|
5
|
+
def bcc_value
|
6
|
+
@bcc_value ||= _bcc_value
|
7
|
+
end
|
8
|
+
alias to_bcc_value bcc_value
|
9
|
+
|
10
|
+
def _bcc_value
|
11
|
+
if self.bcc_value_type.is_a?(Class)
|
12
|
+
return self.bcc_value_type.new(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
case self.bcc_size
|
7
16
|
when Fiddle::Importer.sizeof("int")
|
8
17
|
self[0, self.size].unpack("i!").first
|
9
18
|
when Fiddle::Importer.sizeof("long")
|
@@ -12,4 +21,27 @@ class Fiddle::Pointer
|
|
12
21
|
self[0, self.size].unpack("Z*").first
|
13
22
|
end
|
14
23
|
end
|
24
|
+
|
25
|
+
def method_missing(name, *a)
|
26
|
+
fields = \
|
27
|
+
if self.respond_to?(:bcc_value_type) && \
|
28
|
+
self.bcc_value_type.respond_to?(:fields)
|
29
|
+
self.bcc_value_type.fields.map{|v| v.split.last.to_sym }
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
return super unless fields
|
34
|
+
|
35
|
+
if fields.include?(name) && bcc_value.respond_to?(name)
|
36
|
+
bcc_value.send(name)
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_accessor :bcc_value_type
|
43
|
+
attr_writer :bcc_size
|
44
|
+
def bcc_size
|
45
|
+
@bcc_size || self.size
|
46
|
+
end
|
15
47
|
end
|
data/lib/rbbcc/table.rb
CHANGED
@@ -50,12 +50,13 @@ module RbBCC
|
|
50
50
|
def initialize(bpf, map_id, map_fd, keytype, leaftype, name: nil)
|
51
51
|
@bpf, @map_id, @map_fd, @keysize, @leafsize = \
|
52
52
|
bpf, map_id, map_fd, sizeof(keytype), sizeof(leaftype)
|
53
|
+
@keytype = keytype
|
53
54
|
@leaftype = leaftype
|
54
55
|
@ttype = Clib.bpf_table_type_id(self.bpf.module, self.map_id)
|
55
56
|
@flags = Clib.bpf_table_flags_id(self.bpf.module, self.map_id)
|
56
57
|
@name = name
|
57
58
|
end
|
58
|
-
attr_reader :bpf, :map_id, :map_fd, :keysize, :leafsize, :leaftype, :ttype, :flags, :name
|
59
|
+
attr_reader :bpf, :map_id, :map_fd, :keysize, :keytype, :leafsize, :leaftype, :ttype, :flags, :name
|
59
60
|
|
60
61
|
def next(key)
|
61
62
|
next_key = Fiddle::Pointer.malloc(self.keysize)
|
@@ -86,6 +87,7 @@ module RbBCC
|
|
86
87
|
if res < 0
|
87
88
|
nil
|
88
89
|
end
|
90
|
+
leaf.bcc_value_type = leaftype
|
89
91
|
return leaf
|
90
92
|
end
|
91
93
|
|
@@ -93,8 +95,9 @@ module RbBCC
|
|
93
95
|
self[key] || raise(KeyError, "key not found")
|
94
96
|
end
|
95
97
|
|
96
|
-
def []=(_key,
|
98
|
+
def []=(_key, _leaf)
|
97
99
|
key = normalize_key(_key)
|
100
|
+
leaf = normalize_leaf(_leaf)
|
98
101
|
res = Clib.bpf_update_elem(self.map_fd, key, leaf, 0)
|
99
102
|
if res < 0
|
100
103
|
raise SystemCallError.new("Could not update table", Fiddle.last_error)
|
@@ -186,11 +189,29 @@ module RbBCC
|
|
186
189
|
def normalize_key(key)
|
187
190
|
case key
|
188
191
|
when Fiddle::Pointer
|
192
|
+
key.bcc_value_type = keytype
|
193
|
+
key.bcc_size = keysize
|
189
194
|
key
|
190
195
|
when Integer
|
191
|
-
byref(key, keysize)
|
196
|
+
ret = byref(key, keysize)
|
197
|
+
ret.bcc_value_type = keytype
|
198
|
+
ret
|
192
199
|
else
|
193
|
-
raise
|
200
|
+
raise ArgumentError, "#{key.inspect} must be integer or pointor"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def normalize_leaf(leaf)
|
205
|
+
case leaf
|
206
|
+
when Fiddle::Pointer
|
207
|
+
leaf.bcc_value_type = leaftype
|
208
|
+
leaf
|
209
|
+
when Integer
|
210
|
+
ret = byref(leaf, keysize)
|
211
|
+
ret.bcc_value_type = leaftype
|
212
|
+
ret
|
213
|
+
else
|
214
|
+
raise KeyError, "#{leaf.inspect} must be integer or pointor"
|
194
215
|
end
|
195
216
|
end
|
196
217
|
|
@@ -202,6 +223,7 @@ module RbBCC
|
|
202
223
|
end
|
203
224
|
ptr = Fiddle::Pointer.malloc(size)
|
204
225
|
ptr[0, size] = [value].pack(pack_fmt)
|
226
|
+
ptr.bcc_size = size
|
205
227
|
ptr
|
206
228
|
end
|
207
229
|
end
|
data/lib/rbbcc/version.rb
CHANGED
data/rbbcc.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
19
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
20
|
end
|
21
|
-
|
22
|
-
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
end
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbcc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Uchio Kondo
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: BCC port for MRI. See https://github.com/iovisor/bcc
|
14
14
|
email:
|
15
15
|
- udzura@udzura.jp
|
16
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- rbbcc
|
17
18
|
extensions: []
|
18
19
|
extra_rdoc_files: []
|
19
20
|
files:
|
@@ -56,6 +57,7 @@ files:
|
|
56
57
|
- examples/charty/Gemfile
|
57
58
|
- examples/charty/Gemfile.lock
|
58
59
|
- examples/charty/bitehist-unicode.rb
|
60
|
+
- examples/collectsyscall.rb
|
59
61
|
- examples/dddos.rb
|
60
62
|
- examples/disksnoop.rb
|
61
63
|
- examples/example.gif
|
@@ -76,6 +78,7 @@ files:
|
|
76
78
|
- examples/urandomread.rb
|
77
79
|
- examples/usdt-test.rb
|
78
80
|
- examples/usdt.rb
|
81
|
+
- exe/rbbcc
|
79
82
|
- lib/rbbcc.rb
|
80
83
|
- lib/rbbcc/bcc.rb
|
81
84
|
- lib/rbbcc/clib.rb
|