bpfql 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd92fdeedd7501b48699058bf9a466287d511beb9ebebaa865dd53f171f4f0e7
4
- data.tar.gz: 485ece1c48041ea20293838d9bfc6c5ee88fac2b6edfffccc6d696de6f425520
3
+ metadata.gz: b134a056eb4c5c02472903a1f49c68ebd9c3a9af551ce8024c681ac4113581ae
4
+ data.tar.gz: e0d3921a0b4861c7edd9c392f4117470ae4d6165b40298d0a9bd06bc3d232f56
5
5
  SHA512:
6
- metadata.gz: 852f267f0bc56295861c35eb7a18fce76c228f4a896589cf5f51ecf3e69e9b1d0ee19b78192e8e45a1a801b9b778e121af8dc43e9f0eabf20076efa935c28d8a
7
- data.tar.gz: 1d013db8052f28175fde7f166e3afa350b74a840a2b4012ba4435d629140352a10a01da5c94787c5f1185a92a26805a91d867d416b2f7fd0c31a198bd2141165
6
+ metadata.gz: a27deed2b4d0bcef159899d388fee10cfcdf7ba40c2f21f6f2226ddba29df53f2348ef0f09db321d5338f2b6cb3357d4d7b9f73cccad2b5af2aac0100353a59a
7
+ data.tar.gz: c62284c1237a94a3b1831d5320cff0240838ab1efbcc47ee4bec56a21754ee5149da1d711a604b8d0dad917dfc9aa2ded4000b2d4c159985e2641de6f4e2c3d1
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # BPFQL
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/bpfql.svg)](https://badge.fury.io/rb/bpfql)
4
+
3
5
  eBPF query runner. Choose a format in:
4
6
 
5
7
  * Ruby DSL
@@ -36,7 +38,7 @@ end
36
38
  ```ruby
37
39
  BPFQL do
38
40
  select "count()"
39
- from "tracepoint:syscalls:sys_clone_enter"
41
+ from "tracepoint:syscalls:sys_enter_clone"
40
42
  group_by "comm"
41
43
  interval "15s"
42
44
  end
@@ -47,7 +49,7 @@ end
47
49
  ```yaml
48
50
  BPFQL:
49
51
  - select: count()
50
- from: tracepoint:syscalls:sys_clone_enter
52
+ from: tracepoint:syscalls:sys_enter_clone
51
53
  group_by: comm
52
54
  stop_after: "30s"
53
55
  ```
@@ -55,12 +57,14 @@ BPFQL:
55
57
  ```yaml
56
58
  BPFQL:
57
59
  - select: count()
58
- from: tracepoint:syscalls:sys_clone_enter
60
+ from: tracepoint:syscalls:sys_enter_clone
59
61
  where:
60
62
  - comm is "ruby"
61
63
  - pid is 12345
62
64
  ```
63
65
 
66
+ * See [examples/](examples/) to find working examples.
67
+
64
68
  ## Development
65
69
 
66
70
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -0,0 +1,9 @@
1
+ BPFQL do
2
+ select "ts", "comm", "pid", "__syscall_nr", "ret"
3
+ from "tracepoint:syscalls:sys_exit_read"
4
+ if ENV['TARGET_COMM']
5
+ where "comm", is: ENV['TARGET_COMM']
6
+ elsif ENV['TARGET_PID']
7
+ where "pid", is: ENV['TARGET_PID']
8
+ end
9
+ end
@@ -1,5 +1,6 @@
1
1
  require "bpfql/version"
2
2
  require "bpfql/query"
3
+ require "bpfql/dsl"
3
4
  require "bpfql/runner"
4
5
 
5
6
  module Bpfql
@@ -14,8 +14,16 @@ module Bpfql
14
14
  exit 1
15
15
  end
16
16
 
17
- yaml_file = argv[0]
18
- q = Bpfql::Query.parse_yaml(File.read yaml_file)
17
+ file = argv[0]
18
+ q = nil
19
+ case File.extname(file)
20
+ when ".rb", ".bpfql"
21
+ dsl = File.read(file)
22
+ q = [eval(dsl)]
23
+ when ".yml", ".yaml"
24
+ q = Bpfql::Query.parse_yaml(File.read file)
25
+ end
26
+
19
27
  r = Bpfql::Runner.new(q[0])
20
28
  r.run
21
29
  end
@@ -0,0 +1,71 @@
1
+ require 'bpfql/query'
2
+
3
+ module Bpfql
4
+ class DSL
5
+ def initialize(qb)
6
+ @builder = qb
7
+ @_where_continue = false
8
+ end
9
+ attr_reader :builder
10
+
11
+ def select(*members)
12
+ builder.select = Query::SelectOption.new(members)
13
+ end
14
+
15
+ def from(probe)
16
+ builder.from = Query::ProbeOption.new(probe)
17
+ end
18
+
19
+ def where(*filter, **options)
20
+ builder.where = []
21
+ builder.where << eval_where(*filter, **options)
22
+ @_where_continue = true
23
+ end
24
+
25
+ def _and(*filter, **options)
26
+ if @_where_continue
27
+ builder.where << eval_where(*filter, **options)
28
+ else
29
+ raise ArgumentError, "Cannot invoke _and() before where()"
30
+ end
31
+ end
32
+
33
+ def group_by(var)
34
+ builder.group_by = var
35
+ end
36
+
37
+ def interval(timing)
38
+ builder.interval = timing
39
+ end
40
+
41
+ def stop_after(timing)
42
+ builder.stop = Query::StopOption.new(:after, timing)
43
+ end
44
+
45
+ private
46
+ def eval_where(*filter, **options)
47
+ case filter.size
48
+ when 1
49
+ if (options.keys & %i(is not lt gt lteq gteq)).empty?
50
+ raise ArgumentError, "Invalid parameter: #{filter}, #{options}"
51
+ end
52
+ %i(is not lt gt lteq gteq).each do |ope|
53
+ if options.has_key?(ope)
54
+ return Query::FilterOption.new(filter[0], ope.to_s, options[ope])
55
+ end
56
+ end
57
+ when 3
58
+ return Query::FilterOption.new(*filter)
59
+ else
60
+ raise ArgumentError, "Invalid parameter: #{filter}, #{options}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def BPFQL(&b)
67
+ query = Bpfql::Query.new
68
+ evaluator = Bpfql::DSL.new(query)
69
+ evaluator.instance_eval(&b)
70
+ query
71
+ end
@@ -21,7 +21,7 @@ module Bpfql
21
21
  end
22
22
 
23
23
  def initialize(&b)
24
- b.call(self)
24
+ b.call(self) if b
25
25
  end
26
26
  attr_accessor :select, :from, :where, :group_by, :interval, :stop
27
27
  alias probe from
@@ -10,6 +10,7 @@ module Bpfql
10
10
  end
11
11
 
12
12
  def run
13
+ $stderr.puts bpf_source if ENV['BPFQL_DEBUG']
13
14
  @module = RbBCC::BCC.new(text: bpf_source)
14
15
  puts(@fmt.gsub(/(\.\d+f|d)/, 's') % tracepoint_fields_sorted.map(&:upcase))
15
16
 
@@ -83,12 +84,16 @@ module Bpfql
83
84
  src = <<~FUNCTION
84
85
  TRACEPOINT_PROBE(#{qb.probe.arg1}, #{qb.probe.arg2}) {
85
86
  struct data_t data = {};
87
+
86
88
  __ASSIGN_PID__
87
89
  __ASSIGN_TS__
88
90
  __ASSIGN_COMM__
89
91
 
90
92
  __ASSIGN_FIELDS__
91
- events.perf_submit(args, &data, sizeof(data));
93
+
94
+ __FILTER__
95
+ events.perf_submit(args, &data, sizeof(data));
96
+ __FILTER_END__
92
97
  return 0;
93
98
  }
94
99
  FUNCTION
@@ -104,12 +109,70 @@ module Bpfql
104
109
  }.join("\n")
105
110
  src.sub!('__ASSIGN_FIELDS__', assigner)
106
111
  end
112
+
113
+ if qb.where && !qb.where.empty?
114
+ src.sub!('__FILTER__', filter_clause)
115
+ src.sub!('__FILTER_END__', filter_end_clause)
116
+ else
117
+ src.sub!('__FILTER__', "")
118
+ src.sub!('__FILTER_END__', "")
119
+ end
107
120
  src
108
121
  else
109
122
  raise NotImplementedError, "unsupported probe: #{qb.probe.to_s}"
110
123
  end
111
124
  end
112
125
 
126
+ def filter_clause
127
+ conds = []
128
+ needle_section = ""
129
+ qb.where.each do |filter|
130
+ if filter[0] == "comm" # || field_maps[filter[0]] =~ /^const char \* #{filter[0]}$/
131
+ ope = case filter[1].to_s
132
+ when "is"; ""
133
+ when "not"; "!"
134
+ else
135
+ raise "String comparation supports only == or !=; You put: #{filter}"
136
+ end
137
+ filter_string = %Q<"#{filter[2]}">
138
+ needle_section = <<~NEEDLE
139
+ bool matched = true;
140
+ char needle[] = #{filter_string};
141
+ char haystack[sizeof(needle)] = {};
142
+ bpf_probe_read(&haystack, sizeof(haystack), (void*)data.#{filter[0]});
143
+ for (int i = 0; i < sizeof(needle) - 1; ++i) {
144
+ if (needle[i] != haystack[i]) {
145
+ matched = false;
146
+ }
147
+ }
148
+ NEEDLE
149
+ conds << %Q<#{ope}matched>
150
+ else
151
+ lhs = "data.#{filter[0]}"
152
+ ope = case filter[1].to_s
153
+ when "is"; "=="
154
+ when "not"; "!="
155
+ when "lt"; "<"
156
+ when "gt"; ">"
157
+ when "lteq"; "<="
158
+ when "gteq"; ">="
159
+ else
160
+ raise "[BPFQL BUG] Cannot evaluate ope: #{filter}; This may be a bug"
161
+ end
162
+ rhs = filter[2]
163
+ conds << [lhs, ope, rhs].join(" ")
164
+ end
165
+ end
166
+ return <<~FILTER
167
+ #{needle_section}
168
+ if (#{conds.join("&&")}) {
169
+ FILTER
170
+ end
171
+
172
+ def filter_end_clause
173
+ return "}"
174
+ end
175
+
113
176
  def tracepoint_fields
114
177
  @fields ||= begin
115
178
  if qb.select.members[0] == '*'
@@ -1,3 +1,3 @@
1
1
  module Bpfql
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bpfql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Uchio Kondo
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-03 00:00:00.000000000 Z
11
+ date: 2020-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbbcc
@@ -42,9 +42,11 @@ files:
42
42
  - bin/setup
43
43
  - bpfql.gemspec
44
44
  - examples/random-simple.yml
45
+ - examples/read-simple.rb
45
46
  - exe/bpfql
46
47
  - lib/bpfql.rb
47
48
  - lib/bpfql/cli.rb
49
+ - lib/bpfql/dsl.rb
48
50
  - lib/bpfql/query.rb
49
51
  - lib/bpfql/runner.rb
50
52
  - lib/bpfql/version.rb