rbbcc 0.3.0 → 0.5.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.
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