Skip to content

Commit dd73ccb

Browse files
committed
new benchmark tests
1 parent 467333e commit dd73ccb

2 files changed

Lines changed: 311 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
│ │ │ deny_* / allow_* │ │ │
8686
│ │ │ net_* / survival_* │ │ │
8787
│ │ │ agent_meta / stats │ │ │
88-
│ │ │ events ring buffer │ │ │
88+
│ │ │ events ring buffer │ │ │
8989
│ │ └──────────────────────┘ │ │
9090
│ └───────────────────────────────────────────────────────────────────────┘ │
9191
│ │ │

tests/bench_policy.cpp

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
// cppcheck-suppress-file missingInclude
33
// cppcheck-suppress-file unknownMacro
44
#include <benchmark/benchmark.h>
5+
#include <arpa/inet.h>
6+
#include <cstring>
57
#include <fstream>
68
#include <filesystem>
9+
#include <sys/sysmacros.h>
10+
#include "network_ops.hpp"
711
#include "policy.hpp"
812
#include "sha256.hpp"
913
#include "utils.hpp"
@@ -105,6 +109,312 @@ static void BM_ParseInodeId(benchmark::State& state)
105109
}
106110
BENCHMARK(BM_ParseInodeId);
107111

112+
// ============================================================================
113+
// BPF-Specific Benchmarks
114+
// These test userspace hot paths that interact with BPF maps
115+
// ============================================================================
116+
117+
// InodeIdHash - Critical for deny_inode_map lookups
118+
static void BM_InodeIdHash(benchmark::State& state)
119+
{
120+
InodeIdHash hasher;
121+
InodeId id{.ino = 12345678901234ULL, .dev = 259, .pad = 0};
122+
for (auto _ : state) {
123+
size_t hash = hasher(id);
124+
benchmark::DoNotOptimize(hash);
125+
}
126+
}
127+
BENCHMARK(BM_InodeIdHash);
128+
129+
// InodeIdHash with varying inodes (simulates map iteration)
130+
static void BM_InodeIdHashVarying(benchmark::State& state)
131+
{
132+
InodeIdHash hasher;
133+
uint64_t ino = 1000000;
134+
for (auto _ : state) {
135+
InodeId id{.ino = ino++, .dev = 259, .pad = 0};
136+
size_t hash = hasher(id);
137+
benchmark::DoNotOptimize(hash);
138+
}
139+
state.SetItemsProcessed(state.iterations());
140+
}
141+
BENCHMARK(BM_InodeIdHashVarying);
142+
143+
// fill_path_key - Used when adding paths to deny_path_map
144+
static void BM_FillPathKeyShort(benchmark::State& state)
145+
{
146+
std::string path = "/usr/bin/malware";
147+
PathKey key{};
148+
for (auto _ : state) {
149+
fill_path_key(path, key);
150+
benchmark::DoNotOptimize(key);
151+
}
152+
state.SetBytesProcessed(state.iterations() * path.size());
153+
}
154+
BENCHMARK(BM_FillPathKeyShort);
155+
156+
static void BM_FillPathKeyLong(benchmark::State& state)
157+
{
158+
// Near-maximum length path
159+
std::string path = "/var/lib/containers/storage/overlay/abcdef1234567890/" +
160+
std::string(150, 'x') + "/binary";
161+
PathKey key{};
162+
for (auto _ : state) {
163+
fill_path_key(path, key);
164+
benchmark::DoNotOptimize(key);
165+
}
166+
state.SetBytesProcessed(state.iterations() * path.size());
167+
}
168+
BENCHMARK(BM_FillPathKeyLong);
169+
170+
// encode_dev - Converts dev_t to BPF-compatible format
171+
static void BM_EncodeDev(benchmark::State& state)
172+
{
173+
dev_t dev = makedev(259, 1); // Typical NVMe device
174+
for (auto _ : state) {
175+
uint32_t encoded = encode_dev(dev);
176+
benchmark::DoNotOptimize(encoded);
177+
}
178+
}
179+
BENCHMARK(BM_EncodeDev);
180+
181+
// DenyEntries map operations (unordered_map with InodeId keys)
182+
static void BM_DenyEntriesInsert(benchmark::State& state)
183+
{
184+
const size_t map_size = state.range(0);
185+
for (auto _ : state) {
186+
state.PauseTiming();
187+
DenyEntries entries;
188+
entries.reserve(map_size);
189+
state.ResumeTiming();
190+
191+
for (size_t i = 0; i < map_size; ++i) {
192+
InodeId id{.ino = i + 1000000, .dev = 259, .pad = 0};
193+
entries.emplace(id, "/path/to/file" + std::to_string(i));
194+
}
195+
benchmark::DoNotOptimize(entries);
196+
}
197+
state.SetItemsProcessed(state.iterations() * map_size);
198+
}
199+
BENCHMARK(BM_DenyEntriesInsert)->Range(100, 10000);
200+
201+
static void BM_DenyEntriesLookup(benchmark::State& state)
202+
{
203+
const size_t map_size = state.range(0);
204+
DenyEntries entries;
205+
entries.reserve(map_size);
206+
for (size_t i = 0; i < map_size; ++i) {
207+
InodeId id{.ino = i + 1000000, .dev = 259, .pad = 0};
208+
entries.emplace(id, "/path/to/file" + std::to_string(i));
209+
}
210+
211+
size_t lookup_idx = 0;
212+
for (auto _ : state) {
213+
InodeId id{.ino = (lookup_idx % map_size) + 1000000, .dev = 259, .pad = 0};
214+
auto it = entries.find(id);
215+
benchmark::DoNotOptimize(it);
216+
++lookup_idx;
217+
}
218+
}
219+
BENCHMARK(BM_DenyEntriesLookup)->Range(100, 10000);
220+
221+
// PortKey hash - For deny_port map lookups
222+
static void BM_PortKeyHash(benchmark::State& state)
223+
{
224+
PortKeyHash hasher;
225+
PortKey key{.port = 443, .protocol = 6, .direction = 0};
226+
for (auto _ : state) {
227+
size_t hash = hasher(key);
228+
benchmark::DoNotOptimize(hash);
229+
}
230+
}
231+
BENCHMARK(BM_PortKeyHash);
232+
233+
// IPv4 parsing - Hot path for network deny rules
234+
static void BM_ParseIpv4(benchmark::State& state)
235+
{
236+
std::string ip = "192.168.1.100";
237+
for (auto _ : state) {
238+
uint32_t ip_be;
239+
bool result = parse_ipv4(ip, ip_be);
240+
benchmark::DoNotOptimize(result);
241+
benchmark::DoNotOptimize(ip_be);
242+
}
243+
}
244+
BENCHMARK(BM_ParseIpv4);
245+
246+
// IPv6 parsing - For IPv6 deny rules
247+
static void BM_ParseIpv6(benchmark::State& state)
248+
{
249+
std::string ip = "2001:db8::8a2e:370:7334";
250+
for (auto _ : state) {
251+
Ipv6Key key{};
252+
bool result = parse_ipv6(ip, key);
253+
benchmark::DoNotOptimize(result);
254+
benchmark::DoNotOptimize(key);
255+
}
256+
}
257+
BENCHMARK(BM_ParseIpv6);
258+
259+
// IPv6 parsing - Full address
260+
static void BM_ParseIpv6Full(benchmark::State& state)
261+
{
262+
std::string ip = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
263+
for (auto _ : state) {
264+
Ipv6Key key{};
265+
bool result = parse_ipv6(ip, key);
266+
benchmark::DoNotOptimize(result);
267+
benchmark::DoNotOptimize(key);
268+
}
269+
}
270+
BENCHMARK(BM_ParseIpv6Full);
271+
272+
// CIDR v4 parsing - For LPM trie operations
273+
static void BM_ParseCidrV4(benchmark::State& state)
274+
{
275+
std::string cidr = "10.0.0.0/8";
276+
for (auto _ : state) {
277+
uint32_t ip_be;
278+
uint8_t prefix_len;
279+
bool result = parse_cidr_v4(cidr, ip_be, prefix_len);
280+
benchmark::DoNotOptimize(result);
281+
benchmark::DoNotOptimize(ip_be);
282+
benchmark::DoNotOptimize(prefix_len);
283+
}
284+
}
285+
BENCHMARK(BM_ParseCidrV4);
286+
287+
// CIDR v6 parsing
288+
static void BM_ParseCidrV6(benchmark::State& state)
289+
{
290+
std::string cidr = "2001:db8::/32";
291+
for (auto _ : state) {
292+
Ipv6Key ip{};
293+
uint8_t prefix_len;
294+
bool result = parse_cidr_v6(cidr, ip, prefix_len);
295+
benchmark::DoNotOptimize(result);
296+
benchmark::DoNotOptimize(ip);
297+
benchmark::DoNotOptimize(prefix_len);
298+
}
299+
}
300+
BENCHMARK(BM_ParseCidrV6);
301+
302+
// LPM key construction - IPv4
303+
static void BM_Ipv4LpmKeyConstruction(benchmark::State& state)
304+
{
305+
uint32_t ip_be = htonl(0x0A000000); // 10.0.0.0
306+
uint8_t prefix_len = 8;
307+
for (auto _ : state) {
308+
Ipv4LpmKey key = {
309+
.prefixlen = prefix_len,
310+
.addr = ip_be};
311+
benchmark::DoNotOptimize(key);
312+
}
313+
}
314+
BENCHMARK(BM_Ipv4LpmKeyConstruction);
315+
316+
// LPM key construction - IPv6
317+
static void BM_Ipv6LpmKeyConstruction(benchmark::State& state)
318+
{
319+
Ipv6Key ip{};
320+
parse_ipv6("2001:db8::", ip);
321+
uint8_t prefix_len = 32;
322+
for (auto _ : state) {
323+
Ipv6LpmKey key = {
324+
.prefixlen = prefix_len,
325+
.addr = {0}};
326+
std::memcpy(key.addr, ip.addr, sizeof(key.addr));
327+
benchmark::DoNotOptimize(key);
328+
}
329+
}
330+
BENCHMARK(BM_Ipv6LpmKeyConstruction);
331+
332+
// IP formatting (for logging/output)
333+
static void BM_FormatIpv4(benchmark::State& state)
334+
{
335+
uint32_t ip_be = htonl(0xC0A80164); // 192.168.1.100
336+
for (auto _ : state) {
337+
std::string result = format_ipv4(ip_be);
338+
benchmark::DoNotOptimize(result);
339+
}
340+
}
341+
BENCHMARK(BM_FormatIpv4);
342+
343+
static void BM_FormatIpv6(benchmark::State& state)
344+
{
345+
Ipv6Key ip{};
346+
parse_ipv6("2001:db8::8a2e:370:7334", ip);
347+
for (auto _ : state) {
348+
std::string result = format_ipv6(ip);
349+
benchmark::DoNotOptimize(result);
350+
}
351+
}
352+
BENCHMARK(BM_FormatIpv6);
353+
354+
// inode_to_string - Used in logging and output
355+
static void BM_InodeToString(benchmark::State& state)
356+
{
357+
InodeId id{.ino = 12345678901234ULL, .dev = 259, .pad = 0};
358+
for (auto _ : state) {
359+
std::string result = inode_to_string(id);
360+
benchmark::DoNotOptimize(result);
361+
}
362+
}
363+
BENCHMARK(BM_InodeToString);
364+
365+
// build_exec_id - Used for event correlation
366+
static void BM_BuildExecId(benchmark::State& state)
367+
{
368+
uint32_t pid = 12345;
369+
uint64_t start_time = 1234567890123456789ULL;
370+
for (auto _ : state) {
371+
std::string result = build_exec_id(pid, start_time);
372+
benchmark::DoNotOptimize(result);
373+
}
374+
}
375+
BENCHMARK(BM_BuildExecId);
376+
377+
// InodeId comparison (for equality checks in maps)
378+
static void BM_InodeIdComparison(benchmark::State& state)
379+
{
380+
InodeId id1{.ino = 12345678901234ULL, .dev = 259, .pad = 0};
381+
InodeId id2{.ino = 12345678901234ULL, .dev = 259, .pad = 0};
382+
InodeId id3{.ino = 12345678901235ULL, .dev = 259, .pad = 0};
383+
bool toggle = false;
384+
for (auto _ : state) {
385+
bool result = toggle ? (id1 == id2) : (id1 == id3);
386+
benchmark::DoNotOptimize(result);
387+
toggle = !toggle;
388+
}
389+
}
390+
BENCHMARK(BM_InodeIdComparison);
391+
392+
// Protocol name lookup (for output formatting)
393+
static void BM_ProtocolName(benchmark::State& state)
394+
{
395+
uint8_t protocols[] = {0, 6, 17, 99};
396+
size_t idx = 0;
397+
for (auto _ : state) {
398+
std::string name = protocol_name(protocols[idx % 4]);
399+
benchmark::DoNotOptimize(name);
400+
++idx;
401+
}
402+
}
403+
BENCHMARK(BM_ProtocolName);
404+
405+
// Direction name lookup
406+
static void BM_DirectionName(benchmark::State& state)
407+
{
408+
uint8_t directions[] = {0, 1, 2, 255};
409+
size_t idx = 0;
410+
for (auto _ : state) {
411+
std::string name = direction_name(directions[idx % 4]);
412+
benchmark::DoNotOptimize(name);
413+
++idx;
414+
}
415+
}
416+
BENCHMARK(BM_DirectionName);
417+
108418
} // namespace
109419
} // namespace aegis
110420

0 commit comments

Comments
 (0)