Skip to content

Commit 71aa832

Browse files
committed
perf(rule-engine,eventsource): Event bitmask and bitsets
The new bitmask implementation is introduced to deal with event id bits testing.
1 parent 907e3c1 commit 71aa832

File tree

7 files changed

+295
-194
lines changed

7 files changed

+295
-194
lines changed

pkg/event/bitset.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2021-2022 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 event
20+
21+
import (
22+
"github.com/bits-and-blooms/bitset"
23+
"github.com/rabbitstack/fibratus/pkg/util/bitmask"
24+
)
25+
26+
// BitSetType defines the bitset type
27+
type BitSetType uint8
28+
29+
const (
30+
// BitmaskBitSet designates the mask-based event id bitset
31+
BitmaskBitSet BitSetType = iota + 1
32+
// TypeBitSet designates the uint16 number space event type bitset
33+
TypeBitSet
34+
// CategoryBitSet designates the event category bitset
35+
CategoryBitSet
36+
)
37+
38+
// BitSets handles the group of category/event type bitsets
39+
// and the bitmask for evaluating event ids bits.
40+
type BitSets struct {
41+
bitmask *bitmask.Bitmask
42+
cats *bitset.BitSet
43+
types *bitset.BitSet
44+
}
45+
46+
// SetBit sets the bit dictated by the bitset type.
47+
func (b *BitSets) SetBit(bs BitSetType, typ Type) {
48+
switch bs {
49+
case BitmaskBitSet:
50+
if b.bitmask == nil {
51+
b.bitmask = bitmask.New()
52+
}
53+
b.bitmask.Set(typ.ID())
54+
55+
case TypeBitSet:
56+
if b.types == nil {
57+
b.types = bitset.New(uint(MaxTypeID() + 1))
58+
}
59+
b.types.Set(uint(typ.HookID()))
60+
61+
case CategoryBitSet:
62+
if b.cats == nil {
63+
b.cats = bitset.New(MaxCategoryIndex + 1)
64+
}
65+
b.cats.Set(uint(typ.Category().Index()))
66+
}
67+
}
68+
69+
// SetCategoryBit toggles the category bit in the bitset.
70+
func (b *BitSets) SetCategoryBit(c Category) {
71+
if b.cats == nil {
72+
b.cats = bitset.New(MaxCategoryIndex + 1)
73+
}
74+
b.cats.Set(uint(c.Index()))
75+
}
76+
77+
// IsBitSet checks if any of the populated bitsets
78+
// contain the type, event ID, or category bit.
79+
// This method evaluates first the event type bitset.
80+
// The event type bitset should only be initialized
81+
// if all event types pertain to the same category.
82+
// Otherwise, event id bitset and last category bitset
83+
// are tested for respective bits.
84+
func (b *BitSets) IsBitSet(evt *Event) bool {
85+
if b.types != nil && b.types.Test(uint(evt.Type.HookID())) {
86+
return true
87+
}
88+
return (b.bitmask != nil && b.bitmask.IsSet(evt.Type.ID())) ||
89+
(b.cats != nil && b.cats.Test(uint(evt.Category.Index())))
90+
}
91+
92+
func (b *BitSets) IsBitmaskInitialized() bool { return b.bitmask != nil }
93+
func (b *BitSets) IsTypesInitialized() bool { return b.types != nil }
94+
func (b *BitSets) IsCategoryInitialized() bool { return b.cats != nil }

pkg/event/bitset_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2021-2022 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 event
20+
21+
import (
22+
"github.com/rabbitstack/fibratus/pkg/util/bitmask"
23+
"testing"
24+
25+
"github.com/rabbitstack/fibratus/pkg/sys/etw"
26+
"github.com/stretchr/testify/assert"
27+
)
28+
29+
func TestBitmask(t *testing.T) {
30+
var tests = []struct {
31+
typ Type
32+
expected bool
33+
}{
34+
{TerminateThread, true},
35+
{TerminateProcess, true},
36+
{CreateThread, true},
37+
{CreateFile, false},
38+
{WriteFile, false},
39+
{LoadImage, false},
40+
{MapFileRundown, true},
41+
{ProcessRundown, true},
42+
}
43+
44+
b := bitmask.New()
45+
for _, typ := range AllWithState() {
46+
if typ == WriteFile || typ == LoadImage || typ == CreateFile {
47+
continue
48+
}
49+
b.Set(typ.ID())
50+
}
51+
52+
for _, tt := range tests {
53+
t.Run(tt.typ.String(), func(t *testing.T) {
54+
assert.Equal(t, tt.expected, b.IsSet(tt.typ.ID()))
55+
})
56+
}
57+
}
58+
59+
func TestBitSets(t *testing.T) {
60+
var tests = []struct {
61+
evt *Event
62+
expected bool
63+
}{
64+
{&Event{Type: TerminateThread}, true},
65+
{&Event{Type: TerminateProcess}, true},
66+
{&Event{Type: CreateThread, Category: Thread}, true},
67+
{&Event{Type: CreateFile}, false},
68+
{&Event{Type: WriteFile}, false},
69+
{&Event{Type: LoadImage}, false},
70+
{&Event{Type: MapFileRundown}, true},
71+
{&Event{Type: ProcessRundown}, true},
72+
}
73+
74+
var bitsets BitSets
75+
76+
bitsets.SetBit(BitmaskBitSet, TerminateThread)
77+
bitsets.SetBit(TypeBitSet, TerminateProcess)
78+
bitsets.SetBit(CategoryBitSet, CreateThread)
79+
bitsets.SetBit(TypeBitSet, MapFileRundown)
80+
bitsets.SetBit(BitmaskBitSet, ProcessRundown)
81+
82+
for _, tt := range tests {
83+
t.Run(tt.evt.Type.String(), func(t *testing.T) {
84+
assert.Equal(t, tt.expected, bitsets.IsBitSet(tt.evt))
85+
})
86+
}
87+
}
88+
89+
func BenchmarkBitmask(b *testing.B) {
90+
b.ReportAllocs()
91+
92+
bm := bitmask.New()
93+
bm.Set(TerminateThread.ID())
94+
bm.Set(CreateThread.ID())
95+
bm.Set(TerminateProcess.ID())
96+
bm.Set(CreateFile.ID())
97+
98+
evt := &etw.EventRecord{Header: etw.EventHeader{ProviderID: ThreadEventGUID, EventDescriptor: etw.EventDescriptor{Opcode: 2}}}
99+
100+
b.ResetTimer()
101+
102+
for i := 0; i < b.N; i++ {
103+
if !bm.IsSet(evt.ID()) {
104+
panic("mask should be present")
105+
}
106+
}
107+
}
108+
109+
func BenchmarkStdlibMap(b *testing.B) {
110+
b.ReportAllocs()
111+
112+
evts := make(map[Type]bool)
113+
evts[TerminateThread] = true
114+
evts[CreateThread] = true
115+
evts[TerminateProcess] = true
116+
evts[CreateFile] = true
117+
118+
evt := etw.EventRecord{Header: etw.EventHeader{ProviderID: ThreadEventGUID, EventDescriptor: etw.EventDescriptor{Opcode: 2}}}
119+
etype := NewTypeFromEventRecord(&evt)
120+
121+
b.ResetTimer()
122+
123+
for i := 0; i < b.N; i++ {
124+
if !evts[etype] {
125+
panic("event should be present")
126+
}
127+
}
128+
}

pkg/event/eventset.go

Lines changed: 0 additions & 94 deletions
This file was deleted.

pkg/event/eventset_test.go

Lines changed: 0 additions & 80 deletions
This file was deleted.

0 commit comments

Comments
 (0)