Skip to content

Commit e079b38

Browse files
committed
perf(rule-engine): Use bitsets for determining if the event is evaluable by the expression
1 parent 45e9dba commit e079b38

File tree

2 files changed

+123
-8
lines changed

2 files changed

+123
-8
lines changed

pkg/filter/ql/literal.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,8 @@ type SequenceExpr struct {
278278
// Alias represents the sequence expression alias.
279279
Alias string
280280

281-
emasks event.EventsetMasks
282-
cmasks event.CategoryMasks
283-
284-
types []event.Type
281+
bitsets event.BitSets
282+
types []event.Type
285283
}
286284

287285
func (e *SequenceExpr) init() {
@@ -335,28 +333,47 @@ func (e *SequenceExpr) walk() {
335333

336334
WalkFunc(e.Expr, walk)
337335

336+
uniqCats := make(map[event.Category]bool)
337+
338338
// initialize event type/category buckets for every such field
339339
for name, values := range stringFields {
340340
for _, v := range values {
341341
switch name {
342342
case fields.EvtName:
343343
for _, typ := range event.NameToTypes(v) {
344-
e.emasks.Set(typ)
344+
if typ == event.UnknownType {
345+
continue
346+
}
345347
e.types = append(e.types, typ)
348+
uniqCats[event.TypeToEventInfo(typ).Category] = true
346349
}
347350
case fields.EvtCategory:
348-
e.cmasks.Set(event.Category(v))
351+
e.bitsets.SetCategoryBit(event.Category(v))
349352
}
350353
}
351354
}
355+
356+
for _, t := range e.types {
357+
switch len(uniqCats) {
358+
case 0:
359+
continue
360+
case 1:
361+
// happy path can use a single bitmask for all
362+
// event types pertaining to the same category
363+
e.bitsets.SetBit(event.TypeBitSet, t)
364+
default:
365+
// use map-backed bitmask for event identifiers
366+
e.bitsets.SetBit(event.BitmaskBitSet, t)
367+
}
368+
}
352369
}
353370

354371
// IsEvaluable determines if the expression should be evaluated by inspecting
355372
// the event type filter fields defined in the expression. We permit the expression
356-
// to be evaluated when the incoming event type or category pertains to the one
373+
// to be evaluated when the incoming event type, ID, or category pertains to the one
357374
// defined in the field literal.
358375
func (e *SequenceExpr) IsEvaluable(evt *event.Event) bool {
359-
return e.emasks.Test(evt.Type.GUID(), evt.Type.HookID()) || e.cmasks.Test(evt.Category)
376+
return e.bitsets.IsBitSet(evt)
360377
}
361378

362379
// HasBoundFields determines if this sequence expression references any bound field.

pkg/filter/ql/literal_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2021-present by Nedim Sabic Sabic
3+
* https://www.fibratus.io
4+
* All Rights Reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package ql
20+
21+
import (
22+
"github.com/rabbitstack/fibratus/pkg/event"
23+
"github.com/stretchr/testify/assert"
24+
"github.com/stretchr/testify/require"
25+
"testing"
26+
)
27+
28+
func TestSequenceExprIsEvaluable(t *testing.T) {
29+
var tests = []struct {
30+
expr string
31+
evt *event.Event
32+
isEval bool
33+
assertions func(t *testing.T, sexpr *SequenceExpr)
34+
}{
35+
{"evt.name = 'CreateProcess'", &event.Event{Type: event.CreateProcess, Category: event.Process}, true,
36+
func(t *testing.T, sexpr *SequenceExpr) {
37+
assert.True(t, sexpr.bitsets.IsTypesInitialized())
38+
assert.False(t, sexpr.bitsets.IsBitmaskInitialized())
39+
assert.False(t, sexpr.bitsets.IsCategoryInitialized())
40+
},
41+
},
42+
{"evt.name = 'CreateProcess'", &event.Event{Type: event.TerminateProcess, Category: event.Process}, false, nil},
43+
{"evt.name = 'CreateProcess' or evt.name = 'TerminateThread'", &event.Event{Type: event.TerminateProcess, Category: event.Process}, false, nil},
44+
{"evt.name = 'CreateProcess' or evt.category = 'object'", &event.Event{Type: event.TerminateProcess, Category: event.Process}, false, nil},
45+
{"evt.name = 'CreateProcess' or evt.name = 'OpenProcess'", &event.Event{Type: event.OpenProcess, Category: event.Process}, true,
46+
func(t *testing.T, sexpr *SequenceExpr) {
47+
assert.True(t, sexpr.bitsets.IsTypesInitialized())
48+
assert.False(t, sexpr.bitsets.IsBitmaskInitialized())
49+
assert.False(t, sexpr.bitsets.IsCategoryInitialized())
50+
},
51+
},
52+
{"evt.name = 'CreateProcess' or evt.name = 'CreateThread'", &event.Event{Type: event.CreateThread, Category: event.Thread}, true,
53+
func(t *testing.T, sexpr *SequenceExpr) {
54+
assert.False(t, sexpr.bitsets.IsTypesInitialized())
55+
assert.True(t, sexpr.bitsets.IsBitmaskInitialized())
56+
assert.False(t, sexpr.bitsets.IsCategoryInitialized())
57+
},
58+
},
59+
{"evt.name = 'CreateProcess' or evt.category = 'registry'", &event.Event{Type: event.RegSetValue, Category: event.Registry}, true,
60+
func(t *testing.T, sexpr *SequenceExpr) {
61+
assert.True(t, sexpr.bitsets.IsTypesInitialized())
62+
assert.False(t, sexpr.bitsets.IsBitmaskInitialized())
63+
assert.True(t, sexpr.bitsets.IsCategoryInitialized())
64+
},
65+
},
66+
{"evt.name = 'CreateProcess' or evt.name = 'OpenProcess' or evt.category = 'registry'", &event.Event{Type: event.OpenProcess, Category: event.Process}, true,
67+
func(t *testing.T, sexpr *SequenceExpr) {
68+
assert.True(t, sexpr.bitsets.IsTypesInitialized())
69+
assert.False(t, sexpr.bitsets.IsBitmaskInitialized())
70+
assert.True(t, sexpr.bitsets.IsCategoryInitialized())
71+
},
72+
},
73+
{"evt.name = 'CreateProcess' or evt.name = 'SetThreadContext' or evt.category = 'registry'", &event.Event{Type: event.CreateProcess, Category: event.Process}, true,
74+
func(t *testing.T, sexpr *SequenceExpr) {
75+
assert.False(t, sexpr.bitsets.IsTypesInitialized())
76+
assert.True(t, sexpr.bitsets.IsBitmaskInitialized())
77+
assert.True(t, sexpr.bitsets.IsCategoryInitialized())
78+
},
79+
},
80+
}
81+
82+
for _, tt := range tests {
83+
t.Run(tt.expr, func(t *testing.T) {
84+
p := NewParser(tt.expr)
85+
expr, err := p.ParseExpr()
86+
require.NoError(t, err)
87+
88+
sexpr := &SequenceExpr{Expr: expr}
89+
sexpr.init()
90+
sexpr.walk()
91+
92+
assert.Equal(t, tt.isEval, sexpr.IsEvaluable(tt.evt))
93+
if tt.assertions != nil {
94+
tt.assertions(t, sexpr)
95+
}
96+
})
97+
}
98+
}

0 commit comments

Comments
 (0)