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 +4 -4
- data/README.md +7 -3
- data/examples/read-simple.rb +9 -0
- data/lib/bpfql.rb +1 -0
- data/lib/bpfql/cli.rb +10 -2
- data/lib/bpfql/dsl.rb +71 -0
- data/lib/bpfql/query.rb +1 -1
- data/lib/bpfql/runner.rb +64 -1
- data/lib/bpfql/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b134a056eb4c5c02472903a1f49c68ebd9c3a9af551ce8024c681ac4113581ae
|
4
|
+
data.tar.gz: e0d3921a0b4861c7edd9c392f4117470ae4d6165b40298d0a9bd06bc3d232f56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
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:
|
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:
|
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.
|
data/lib/bpfql.rb
CHANGED
data/lib/bpfql/cli.rb
CHANGED
@@ -14,8 +14,16 @@ module Bpfql
|
|
14
14
|
exit 1
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
q =
|
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
|
data/lib/bpfql/dsl.rb
ADDED
@@ -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
|
data/lib/bpfql/query.rb
CHANGED
data/lib/bpfql/runner.rb
CHANGED
@@ -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
|
-
|
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] == '*'
|
data/lib/bpfql/version.rb
CHANGED
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.
|
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-
|
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
|