bpfql 0.1.0 → 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 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