rbbcc 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -1
  3. data/.semaphore/semaphore.yml +19 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +6 -4
  6. data/README.md +5 -1
  7. data/Rakefile +8 -1
  8. data/docs/README.md +2 -0
  9. data/docs/answers/01-hello-world.rb +16 -0
  10. data/docs/answers/02-sys_sync.rb +18 -0
  11. data/docs/answers/03-hello_fields.rb +33 -0
  12. data/docs/answers/04-sync_timing.rb +46 -0
  13. data/docs/answers/05-sync_count.rb +54 -0
  14. data/docs/answers/06-disksnoop.rb +71 -0
  15. data/docs/answers/07-hello_perf_output.rb +59 -0
  16. data/docs/answers/08-sync_perf_output.rb +60 -0
  17. data/docs/answers/09-bitehist.rb +32 -0
  18. data/docs/answers/10-disklatency.rb +51 -0
  19. data/docs/answers/11-vfsreadlat.c +46 -0
  20. data/docs/answers/11-vfsreadlat.rb +66 -0
  21. data/docs/answers/12-urandomread.rb +38 -0
  22. data/docs/answers/13-disksnoop_fixed.rb +108 -0
  23. data/docs/answers/14-strlen_count.rb +46 -0
  24. data/docs/answers/15-nodejs_http_server.rb +44 -0
  25. data/docs/answers/16-task_switch.c +23 -0
  26. data/docs/answers/16-task_switch.rb +17 -0
  27. data/docs/answers/node-server.js +11 -0
  28. data/docs/projects_using_rbbcc.md +43 -0
  29. data/docs/tutorial_bcc_ruby_developer.md +774 -0
  30. data/docs/tutorial_bcc_ruby_developer_japanese.md +770 -0
  31. data/examples/networking/http_filter/http-parse-simple.c +114 -0
  32. data/examples/networking/http_filter/http-parse-simple.rb +85 -0
  33. data/examples/ruby_usdt.rb +105 -0
  34. data/examples/sbrk_trace.rb +204 -0
  35. data/lib/rbbcc/bcc.rb +67 -20
  36. data/lib/rbbcc/clib.rb +23 -10
  37. data/lib/rbbcc/debug.rb +17 -0
  38. data/lib/rbbcc/table.rb +36 -16
  39. data/lib/rbbcc/usdt.rb +21 -4
  40. data/lib/rbbcc/version.rb +1 -1
  41. data/rbbcc.gemspec +1 -4
  42. data/semaphore.sh +73 -0
  43. metadata +34 -46
@@ -1,6 +1,7 @@
1
1
  require 'rbbcc/consts'
2
2
  require 'rbbcc/table'
3
3
  require 'rbbcc/symbol_cache'
4
+ require 'rbbcc/debug'
4
5
 
5
6
  module RbBCC
6
7
  SYSCALL_PREFIXES = [
@@ -14,6 +15,20 @@ module RbBCC
14
15
 
15
16
  class BCC
16
17
  class << self
18
+ def _find_file(filename)
19
+ if filename
20
+ unless File.exist?(filename)
21
+ t = File.expand_path "../#{filename}", $0
22
+ if File.exist?(t)
23
+ filename = t
24
+ else
25
+ raise "Could not find file #{filename}"
26
+ end
27
+ end
28
+ end
29
+ return filename
30
+ end
31
+
17
32
  def ksym(addr, show_module: false, show_offset: false)
18
33
  self.sym(addr, -1, show_module: show_module, show_offset: show_offset, demangle: false)
19
34
  end
@@ -161,25 +176,57 @@ module RbBCC
161
176
  module_ = (show_module && module_) ? " [#{File.basename.basename(module_)}]" : ""
162
177
  return name + module_
163
178
  end
179
+
180
+ def attach_raw_socket(fn, dev)
181
+ unless fn.is_a?(Hash)
182
+ raise "arg 1 must be of BPF.Function Hash"
183
+ end
184
+ sock = Clib.bpf_open_raw_sock(dev)
185
+ if sock < 0
186
+ raise SystemCallError.new("Failed to open raw device %s" % dev, Fiddle.last_error)
187
+ end
188
+
189
+ res = Clib.bpf_attach_socket(sock, fn[:fd])
190
+ if res < 0
191
+ raise SystemCallError.new("Failed to attach BPF to device %s" % dev, Fiddle.last_error)
192
+ end
193
+ fn[:sock] = sock
194
+ fn
195
+ end
164
196
  end
165
197
 
166
- def initialize(text:, debug: 0, cflags: [], usdt_contexts: [], allow_rlimit: 0)
198
+ def initialize(text: "", src_file: nil, hdr_file: nil, debug: 0, cflags: [], usdt_contexts: [], allow_rlimit: 0)
167
199
  @kprobe_fds = {}
168
200
  @uprobe_fds = {}
169
201
  @tracepoint_fds = {}
170
202
  @raw_tracepoint_fds = {}
171
- @usdt_contexts = usdt_contexts
172
- if code = gen_args_from_usdt
173
- text = code + text
174
- end
175
-
176
- @module = Clib.bpf_module_create_c_from_string(
177
- text,
178
- debug,
179
- cflags.pack('p*'),
180
- cflags.size,
181
- allow_rlimit
182
- )
203
+
204
+ if src_file
205
+ src_file = BCC._find_file(src_file)
206
+ hdr_file = BCC._find_file(hdr_file)
207
+ end
208
+
209
+ if src_file && src_file.end_with?(".b")
210
+ @module = Clib.bpf_module_create_b(src_file, hdr_file, debug, device)
211
+ else
212
+ if src_file
213
+ text = File.read(src_file)
214
+ end
215
+
216
+ @usdt_contexts = usdt_contexts
217
+ if code = gen_args_from_usdt
218
+ text = code + text
219
+ end
220
+
221
+ # Util.debug text
222
+ @module = Clib.bpf_module_create_c_from_string(
223
+ text,
224
+ debug,
225
+ cflags.pack('p*'),
226
+ cflags.size,
227
+ allow_rlimit
228
+ )
229
+ end
183
230
  @funcs = {}
184
231
  @tables = {}
185
232
  @perf_buffers = {}
@@ -237,7 +284,7 @@ module RbBCC
237
284
  if fd < 0
238
285
  raise SystemCallError.new("Failed to attach BPF program #{fn_name} to tracepoint #{tp}", Fiddle.last_error)
239
286
  end
240
- puts "Attach: #{tp}"
287
+ Util.debug "Attach: #{tp}"
241
288
  @tracepoint_fds[tp] = fd
242
289
  self
243
290
  end
@@ -252,7 +299,7 @@ module RbBCC
252
299
  if fd < 0
253
300
  raise SystemCallError.new("Failed to attach BPF program #{fn_name} to raw tracepoint #{tp}", Fiddle.last_error)
254
301
  end
255
- puts "Attach: #{tp}"
302
+ Util.debug "Attach: #{tp}"
256
303
  @raw_tracepoint_fds[tp] = fd
257
304
  self
258
305
  end
@@ -264,7 +311,7 @@ module RbBCC
264
311
  if fd < 0
265
312
  raise SystemCallError.new("Failed to attach BPF program #{fn_name} to kprobe #{event}", Fiddle.last_error)
266
313
  end
267
- puts "Attach: #{ev_name}"
314
+ Util.debug "Attach: #{ev_name}"
268
315
  @kprobe_fds[ev_name] = fd
269
316
  [ev_name, fd]
270
317
  end
@@ -277,7 +324,7 @@ module RbBCC
277
324
  if fd < 0
278
325
  raise SystemCallError.new("Failed to attach BPF program #{fn_name} to kretprobe #{event}", Fiddle.last_error)
279
326
  end
280
- puts "Attach: #{ev_name}"
327
+ Util.debug "Attach: #{ev_name}"
281
328
  @kprobe_fds[ev_name] = fd
282
329
  [ev_name, fd]
283
330
  end
@@ -291,7 +338,7 @@ module RbBCC
291
338
  if fd < 0
292
339
  raise SystemCallError.new(Fiddle.last_error)
293
340
  end
294
- puts "Attach: #{ev_name}"
341
+ Util.debug "Attach: #{ev_name}"
295
342
 
296
343
  @uprobe_fds[ev_name] = fd
297
344
  [ev_name, fd]
@@ -306,7 +353,7 @@ module RbBCC
306
353
  if fd < 0
307
354
  raise SystemCallError.new(Fiddle.last_error)
308
355
  end
309
- puts "Attach: #{ev_name}"
356
+ Util.debug "Attach: #{ev_name}"
310
357
 
311
358
  @uprobe_fds[ev_name] = fd
312
359
  [ev_name, fd]
@@ -507,7 +554,7 @@ module RbBCC
507
554
  else
508
555
  next
509
556
  end
510
- puts "Found fnc: #{func_name}"
557
+ Util.debug "Found fnc: #{func_name}"
511
558
  if func_name.start_with?("kprobe__")
512
559
  fn = load_func(func_name, BPF::KPROBE)
513
560
  attach_kprobe(
@@ -25,19 +25,27 @@ module RbBCC
25
25
  end
26
26
 
27
27
  extend Fiddle::Importer
28
- begin
29
- default_load = ENV['LIBBCC_VERSION'] || "0.11.0"
30
-
31
- dlload "libbcc.so.#{default_load}"
32
- self.libbcc_version = default_load
33
- rescue Fiddle::DLError => e
34
- warn "Fall back to libbcc 0.10.0: #{e.inspect}"
35
- dlload "libbcc.so.0.10.0"
36
- self.libbcc_version = "0.10.0"
28
+ targets = %w(0.14.0 0.13.0 0.12.0 0.11.0 0.10.0)
29
+ if default_load = ENV['LIBBCC_VERSION']
30
+ targets.unshift(default_load)
31
+ targets.uniq!
32
+ end
33
+
34
+ targets.each do |to_load|
35
+ begin
36
+ dlload "libbcc.so.#{to_load}"
37
+ self.libbcc_version = to_load
38
+ break
39
+ rescue Fiddle::DLError => e
40
+ if targets.last == to_load
41
+ raise LoadError, "no target libbcc to load"
42
+ end
43
+ end
37
44
  end
38
45
  typealias "size_t", "int"
39
46
 
40
47
  extern 'void * bpf_module_create_c_from_string(char *, unsigned int, char **, int, long)'
48
+ extern 'void * bpf_module_create_b(char *filename, char *proto_filename, unsigned int flags, char *dev_name)'
41
49
  extern 'int bpf_num_functions(void *)'
42
50
  extern 'char * bpf_function_name(void *, int)'
43
51
  extern 'void bpf_module_destroy(void *)'
@@ -70,6 +78,7 @@ module RbBCC
70
78
  extern 'char * bpf_table_key_desc(void *, char *)'
71
79
  extern 'char * bpf_table_leaf_desc(void *, char *)'
72
80
  extern 'int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)'
81
+ extern 'int bpf_delete_elem(int fd, void *key)'
73
82
 
74
83
  extern 'int bpf_attach_kprobe(int, int, char *, char *, unsigned long, int)'
75
84
  extern 'int bpf_detach_kprobe(char *)'
@@ -111,10 +120,11 @@ module RbBCC
111
120
  extern 'char * bpf_perf_event_field(void *program, const char *event, size_t i)'
112
121
 
113
122
  extern 'void * bcc_usdt_new_frompid(int, char *)'
123
+ extern 'void * bcc_usdt_new_frompath(char *path)'
114
124
  extern 'int bcc_usdt_enable_probe(void *, char *, char *)'
115
125
  extern 'char * bcc_usdt_genargs(void **, int)'
116
126
  extern 'void bcc_usdt_foreach_uprobe(void *, void *)'
117
-
127
+ extern 'void bcc_usdt_close(void *usdt)'
118
128
  BCCSymbol = struct([
119
129
  "const char *name",
120
130
  "const char *demangle_name",
@@ -147,6 +157,9 @@ module RbBCC
147
157
  extern 'int perf_reader_poll(int num_readers, struct perf_reader **readers, int timeout)'
148
158
 
149
159
  extern 'void bcc_procutils_free(const char *ptr)'
160
+
161
+ extern 'int bpf_open_raw_sock(const char *name)'
162
+ extern 'int bpf_attach_socket(int sockfd, int progfd)'
150
163
  end
151
164
  end
152
165
 
@@ -0,0 +1,17 @@
1
+ if ENV['RBBCC_DEBUG'] || ENV['BCC_DEBUG']
2
+ module RbBCC
3
+ module Util
4
+ def self.debug(msg)
5
+ puts msg
6
+ end
7
+ end
8
+ end
9
+ else
10
+ module RbBCC
11
+ module Util
12
+ def self.debug(msg)
13
+ # nop
14
+ end
15
+ end
16
+ end
17
+ end
@@ -78,7 +78,9 @@ module RbBCC
78
78
  return next_key
79
79
  end
80
80
 
81
- def [](key)
81
+ def [](_key)
82
+ key = normalize_key(_key)
83
+
82
84
  leaf = Fiddle::Pointer.malloc(self.leafsize)
83
85
  res = Clib.bpf_lookup_elem(self.map_fd, key, leaf)
84
86
  if res < 0
@@ -91,11 +93,20 @@ module RbBCC
91
93
  self[key] || raise(KeyError, "key not found")
92
94
  end
93
95
 
94
- def []=(key, leaf)
96
+ def []=(_key, leaf)
97
+ key = normalize_key(_key)
95
98
  res = Clib.bpf_update_elem(self.map_fd, key, leaf, 0)
96
99
  if res < 0
97
100
  raise SystemCallError.new("Could not update table", Fiddle.last_error)
98
101
  end
102
+ leaf
103
+ end
104
+
105
+ def delete(key)
106
+ res = Clib.bpf_delete_elem(self.map_fd, key)
107
+ if res < 0
108
+ raise KeyError, "key not found"
109
+ end
99
110
  res
100
111
  end
101
112
 
@@ -127,6 +138,11 @@ module RbBCC
127
138
  enum_for(:each_pair).to_a
128
139
  end
129
140
 
141
+ def clear
142
+ each_key {|key| self.delete(key) }
143
+ return items # reload contents
144
+ end
145
+
130
146
  def print_log2_hist(val_type="value",
131
147
  section_header: "Bucket ptr",
132
148
  section_print_fn: nil,
@@ -167,6 +183,17 @@ module RbBCC
167
183
  end
168
184
 
169
185
  private
186
+ def normalize_key(key)
187
+ case key
188
+ when Fiddle::Pointer
189
+ key
190
+ when Integer
191
+ byref(key, keysize)
192
+ else
193
+ raise KeyError, "#{key.inspect} must be integer or pointor"
194
+ end
195
+ end
196
+
170
197
  def byref(value, size=sizeof("int"))
171
198
  pack_fmt = case size
172
199
  when sizeof("int") ; "i!"
@@ -194,8 +221,13 @@ module RbBCC
194
221
  end
195
222
  alias length size
196
223
 
197
- def [](key)
198
- super(normalize_key(key))
224
+ def clearitem(key)
225
+ self[key] = byref(0, @leafsize)
226
+ end
227
+
228
+ def delete(key)
229
+ # Delete in Array type does not have an effect, so zero out instead
230
+ clearitem(key)
199
231
  end
200
232
 
201
233
  def each(&b)
@@ -203,18 +235,6 @@ module RbBCC
203
235
  b.call(v.to_bcc_value)
204
236
  end
205
237
  end
206
-
207
- private
208
- def normalize_key(key)
209
- case key
210
- when Fiddle::Pointer
211
- key
212
- when Integer
213
- byref(key, keysize)
214
- else
215
- raise KeyError, "#{key.inspect} must be integer or pointor"
216
- end
217
- end
218
238
  end
219
239
 
220
240
  class PerfEventArray < TableBase
@@ -4,15 +4,21 @@ module RbBCC
4
4
  USDTProbe = Struct.new(:binpath, :fn_name, :addr, :pid)
5
5
 
6
6
  class USDT
7
- # TODO path:
8
- def initialize(pid:)
7
+ def initialize(pid: nil, path: nil)
9
8
  @pid = pid
10
- @context = Clib.bcc_usdt_new_frompid(pid, nil)
9
+ @path = path
10
+ if pid
11
+ @context = Clib.bcc_usdt_new_frompid(pid, path)
12
+ elsif path
13
+ @context = Clib.bcc_usdt_new_frompath(path)
14
+ else
15
+ raise "Either a pid or a binary path must be specified"
16
+ end
11
17
  if !@context || @context.null?
12
18
  raise SystemCallError.new(Fiddle.last_error)
13
19
  end
14
20
  end
15
- attr_reader :pid, :context
21
+ attr_reader :pid, :path, :context
16
22
 
17
23
  def enable_probe(probe:, fn_name:)
18
24
  ret = Clib.bcc_usdt_enable_probe(@context, probe, fn_name)
@@ -33,5 +39,16 @@ module RbBCC
33
39
 
34
40
  return probes
35
41
  end
42
+
43
+ private
44
+ def __del__
45
+ lambda { Clib.bcc_usdt_close(@context); Util.debug("USDT GC'ed.") }
46
+ end
47
+ end
48
+ end
49
+
50
+ at_exit do
51
+ ObjectSpace.each_object(RbBCC::USDT) do |o|
52
+ o.send(:__del__).call
36
53
  end
37
54
  end
@@ -1,3 +1,3 @@
1
1
  module RbBCC
2
- VERSION = "0.3.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -7,6 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = RbBCC::VERSION
8
8
  spec.authors = ["Uchio Kondo"]
9
9
  spec.email = ["udzura@udzura.jp"]
10
+ spec.license = "Apache-2.0"
10
11
 
11
12
  spec.summary = %q{BCC port for MRI}
12
13
  spec.description = %q{BCC port for MRI. See https://github.com/iovisor/bcc}
@@ -20,8 +21,4 @@ Gem::Specification.new do |spec|
20
21
  #spec.bindir = "exe"
21
22
  #spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
23
  spec.require_paths = ["lib"]
23
-
24
- spec.add_development_dependency "bundler", "~> 2.0"
25
- spec.add_development_dependency "rake", "~> 10.0"
26
- spec.add_development_dependency "pry"
27
24
  end
@@ -0,0 +1,73 @@
1
+ #!/bin/bash
2
+
3
+ # packages
4
+
5
+ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4052245BD4284CDD
6
+ echo "deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/iovisor.list
7
+ sudo apt -y update
8
+ sudo apt -y install libbcc
9
+
10
+ # build libbcc 0.11/0.12
11
+ ORIG_DIR=$(pwd)
12
+ sudo mkdir -p /opt/bcc
13
+
14
+ cd /
15
+ sudo chown $(whoami) /opt/bcc
16
+ cache has_key libbcc-so && cache restore libbcc-so
17
+ sudo chown -R root /opt/bcc
18
+ cd -
19
+
20
+ if test "$(ls /opt/bcc | wc -l)" -le "0"; then
21
+ sudo apt -y install bison build-essential cmake flex git libedit-dev \
22
+ libllvm6.0 llvm-6.0-dev libclang-6.0-dev python zlib1g-dev libelf-dev
23
+
24
+ sudo mkdir -p /opt/bcc-work
25
+ sudo chown $(whoami) /opt/bcc-work
26
+ cd /opt/bcc-work
27
+ git clone https://github.com/iovisor/bcc.git
28
+ mkdir bcc/build
29
+ cd bcc
30
+
31
+ git checkout v0.11.0
32
+ git submodule init
33
+ git submodule sync
34
+ git submodule update
35
+ cd build
36
+ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/bcc
37
+ make -j$(nproc)
38
+ sudo make install
39
+ make clean
40
+ cd ..
41
+
42
+ V0_12_HASH=2d099cd8c5cb1598d6e911c0b389132ebc7c101b
43
+ git checkout $V0_12_HASH
44
+ git submodule init
45
+ git submodule sync
46
+ git submodule update
47
+ cd build
48
+ cmake .. -DCMAKE_INSTALL_PREFIX=/opt/bcc
49
+ make -j$(nproc)
50
+ sudo make install
51
+
52
+ cd /
53
+ cache has_key libbcc-so && cache clear libbcc-so
54
+ cache store libbcc-so opt/bcc
55
+ cd -
56
+ fi
57
+ cd $ORIG_DIR
58
+
59
+ # link all tha objects under /lib from /opt/bcc
60
+ sudo ln -sf /opt/bcc/lib/libbcc.so.0.11.0 /opt/bcc/lib/libbcc.so.0.12.0 /usr/lib/x86_64-linux-gnu/
61
+
62
+ # Doing tests
63
+ set -e
64
+
65
+ bundle install --path vendor/bundle
66
+
67
+ bundle exec ruby -e "require 'rbbcc'; puts 'Using rbbcc: %s && libbcc: %s' % [RbBCC::VERSION, RbBCC::Clib.libbcc_version.to_s]"
68
+ if test "$(bundle exec ruby -e 'require %q(rbbcc); print RbBCC::Clib.libbcc_version.to_s')" != "${LIBBCC_VERSION}"; then
69
+ echo "Test target mismatch"
70
+ exit 127
71
+ fi
72
+
73
+ sudo -E env PATH=$PATH bundle exec rake test