-
Notifications
You must be signed in to change notification settings - Fork 6
/
bench.lua
147 lines (132 loc) · 4.25 KB
/
bench.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
module("bench",package.seeall)
local function dirname(s)
local slash = s:find("/()[^/]+$")
if slash then return s:sub(1, slash - 1) end
return "."
end
local dot = dirname(arg[0])
package.path = package.path .. ";" .. dot .. "/deps/pflua/src/?.lua"
.. ";" .. dot .. "/deps/pflua/deps/dynasm/?.lua"
local ffi = require("ffi")
local pf = require("pf")
local savefile = require("pf.savefile")
local libpcap = require("pf.libpcap")
local now = require('pf.utils').now
-- For the Linux JIT
ffi.cdef[[
struct sock_fprog {
uint16_t len;
// Our struct bpf_insn is the same as struct sock_filter.
struct bpf_insn *code;
};
void* compile_filter(struct sock_fprog *prog);
int run_filter(void *filter, const uint8_t *pkt, uint32_t pkt_len);
]]
local linux_bpf_jit = ffi.load(dot .. "/linux-bpf-jit/linux-bpf-jit.so")
local linux_ebpf_jit = ffi.load(dot .. "/linux-ebpf-jit/linux-ebpf-jit.so")
local function compile_linux_jit_filter(filter_str, jit_lib)
local dlt = "EN10MB"
local bytecode = libpcap.compile(filter_str, dlt)
assert(bytecode.bf_len < 2^16)
local prog = ffi.new("struct sock_fprog")
prog.len = bytecode.bf_len
-- FIXME: need to keep insns alive?
prog.code = bytecode.bf_insns
local filter = jit_lib.compile_filter(prog)
return function(P, len)
return jit_lib.run_filter(filter, P, len) ~= 0
end
end
local compilers = {
libpcap = function (filter)
return pf.compile_filter(filter, {libpcap=true})
end,
["linux-bpf"] = function (filter)
return compile_linux_jit_filter(filter, linux_bpf_jit)
end,
["linux-ebpf"] = function (filter)
return compile_linux_jit_filter(filter, linux_ebpf_jit)
end,
["bpf-lua"] = function (filter)
return pf.compile_filter(filter, {bpf=true})
end,
pflua = function (filter)
return pf.compile_filter(filter)
end,
["pflua-native"] = function (filter)
return pf.compile_filter(filter, {native=true})
end
}
local function load_tests(filters, engine)
local tests = {}
local compile = assert(compilers[engine])
for line in io.lines(filters) do
local description, count, filter = line:match("^([^:]+): (%d+):(.*)$")
assert(filter, "failed to parse line "..line)
if #tests > 0 then io.write('\t') end
io.write(description)
local test = {
description=description,
count=assert(tonumber(count)),
filter=filter,
pred=compile(filter)
}
table.insert(tests, test)
end
io.write('\n')
io.flush()
return tests
end
local function run_filter(min_time, packets, pred)
local start = now()
local finish = start
local seen, matched
local iterations = 0
while finish - start < min_time do
seen, matched = 0, 0
for i = 1,#packets do
seen = seen + 1
if pred(packets[i].packet, packets[i].len) then
matched = matched + 1
end
end
iterations = iterations + 1
finish = now()
end
return seen, matched, (finish - start), iterations
end
-- The total time for the test is # of tests * # of samples * # of
-- scenarios * test_time. So about 500 times the run_filter number. I
-- set it to 100ms so that we finish in under a minute.
local function filter_time(pred, packets, expected)
local total, matched, lapse, iterations = run_filter(0.1, packets, pred)
if matched ~= expected then
error("expected "..expected.." matching packets, but got "..matched)
end
return total * iterations / lapse / 1e6
end
function run_filters(tests, packets)
local results = {}
for i, test in ipairs(tests) do
results[i] = filter_time(test.pred, packets, test.count)
end
return results
end
function run_tests(tests, packets, iterations)
run_filters(tests, packets) -- Warmup
for i=1,iterations do
local scores = run_filters(tests, packets)
print(table.concat(scores, '\t'))
io.flush()
end
end
function main(...)
local capture, filters, engine, iterations = ...
assert(engine,
"usage: bench.lua PATH/TO/CAPTURE.PCAP FILTERS ENGINE [ITERATIONS]")
iterations = tonumber(iterations) or 50
local tests = load_tests(filters, engine)
local packets = savefile.load_packets(capture)
run_tests(tests, packets, iterations)
end
main(...)