Skip to content

Improve cpu/memory (proctree wise) #4503

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c83828f
chore(bufferdecode): add DecodeArguments benchmark
geyslan Jan 13, 2025
5f1275c
chore(bufferdecoder): set zero from def fields
geyslan Jan 13, 2025
0a698fa
perf: reduce events.Core lock contention
geyslan Jan 13, 2025
7eb6b91
chore(ebpf): add Benchmark_procTreeForkProcessor
geyslan Jan 14, 2025
2584505
perf(ebpf): improve procTreeForkProcessor
geyslan Jan 14, 2025
fd9a666
chore(controlplane): add procTreeForkProcessor bench
geyslan Jan 14, 2025
c2223ff
perf(controlplane): improve procTreeForkProcessor
geyslan Jan 14, 2025
c9c8723
chore(ebpf): add Benchmark_procTreeExecProcessor
geyslan Jan 14, 2025
255fec0
perf(ebpf): improve procTreeExecProcessor
geyslan Jan 14, 2025
42e1c5e
chore(controlplane): add Benchmark_procTreeExecProcessor
geyslan Jan 14, 2025
5c109d6
perf(controlplane): improve procTreeExecProcessor
geyslan Jan 14, 2025
3156921
perf: remove unused ExecFeed interpreter fields
geyslan Jan 14, 2025
70717e9
chore: add Benchmark_procTreeExitProcessor
geyslan Jan 14, 2025
f39eaa1
perf: improve procTreeExitProcessor
geyslan Jan 14, 2025
617fe40
chore(events): add BenchmarkArgVal
geyslan Jan 14, 2025
a1ceb10
perf(events): improve ArgVal
geyslan Jan 14, 2025
1c9f52a
perf(proctree): move functions from FeedFromFork
geyslan Jan 14, 2025
2457a6d
perf(proctree): introduce feed pools
geyslan Jan 14, 2025
4d7fe28
chore/perf(proctree): comment out exit fields
geyslan Jan 15, 2025
fbfbe99
chore(proctree): remove leftover
geyslan Jan 16, 2025
8cc3abf
perf(proctree): reduce lock contention
geyslan Jan 16, 2025
5ea4bee
perf(proctree): change Thread concurrency control
geyslan Jan 16, 2025
2b6d41c
perf(proctree): improve Process concurrency ctrl
geyslan Jan 16, 2025
45d4ae8
perf(controlplane): introduce signal pool
geyslan Jan 17, 2025
5eba933
chore(cmd): add proctree disable-procfs
geyslan Jan 18, 2025
eaf0311
chore(proctree): set new default cache sizes
geyslan Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cmd/tracee/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ func initCmd() error {
rootCmd.Flags().StringArrayP(
"proctree",
"t",
[]string{"none"},
"[process|thread]\t\t\tControl process tree options",
[]string{"source=none"},
"[source=[events|signals|both]...]\tControl process tree options",
)
err = viper.BindPFlag("proctree", rootCmd.Flags().Lookup("proctree"))
if err != nil {
Expand Down
6 changes: 2 additions & 4 deletions docs/docs/advanced/data-sources/builtin/process-tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ The process tree query the procfs upon initialization and during runtime to fill
## Command Line Option

```bash
$ tracee --proctree help
Example:
--proctree source=[none|events|signals|both]
none | process tree is disabled (default).
Expand All @@ -35,9 +34,8 @@ Example:
both | process tree is built from both events and signals.
--proctree process-cache=8192 | will cache up to 8192 processes in the tree (LRU cache).
--proctree thread-cache=16384 | will cache up to 16384 threads in the tree (LRU cache).
--proctree process-cache-ttl=60 | will set the process cache element TTL to 60 seconds.
--proctree thread-cache-ttl=60 | will set the thread cache element TTL to 60 seconds.
--proctree disable-procfs-query | Will disable procfs quering during runtime
--proctree disable-procfs | will disable procfs entirely.
--proctree disable-procfs-query | will disable procfs quering during runtime.

Use comma OR use the flag multiple times to choose multiple options:
--proctree source=A,process-cache=B,thread-cache=C
Expand Down
2 changes: 1 addition & 1 deletion pkg/bufferdecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (decoder *EbpfDecoder) DecodeArguments(args []trace.Argument, argnum int, e
for i := 0; i < len(evtFields); i++ {
if args[i].Value == nil {
args[i].ArgMeta = evtFields[i]
args[i].Value = args[i].Zero
args[i].Value = evtFields[i].Zero
}
}
return nil
Expand Down
78 changes: 78 additions & 0 deletions pkg/bufferdecoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/aquasecurity/tracee/pkg/events"
"github.com/aquasecurity/tracee/types/trace"
)

Expand Down Expand Up @@ -1005,3 +1006,80 @@ func BenchmarkBinaryMprotectWriteMeta(*testing.B) {
binary.Read(binBuf, binary.LittleEndian, &s)
}
}

func BenchmarkDecodeArguments(b *testing.B) {
/*
args := []trace.Argument{
{
Name: "arg1",
Type: "u64",
Value: 1,
},
{
Name: "arg2",
Type: "u64",
Value: 2,
},
{
Name: "arg3",
Type: "u64",
Value: 3,
},
...
}
******************
buffer is the []byte representation of args instance
******************
*/

buffer := []byte{
0, 1, 0, 0, 0, 0, 0, 0, 0, // arg1
1, 2, 0, 0, 0, 0, 0, 0, 0, // arg2
2, 3, 0, 0, 0, 0, 0, 0, 0, // arg3
3, 4, 0, 0, 0, 0, 0, 0, 0, // arg4
4, 5, 0, 0, 0, 0, 0, 0, 0, // arg5
5, 6, 0, 0, 0, 0, 0, 0, 0, // arg6
6, 7, 0, 0, 0, 0, 0, 0, 0, // arg7
7, 8, 0, 0, 0, 0, 0, 0, 0, // arg8
}
evtFields := []trace.ArgMeta{
{Name: "arg1", Type: "u64", Zero: 0},
{Name: "arg2", Type: "u64", Zero: 0},
{Name: "arg3", Type: "u64", Zero: 0},
{Name: "arg4", Type: "u64", Zero: 0},
{Name: "arg5", Type: "u64", Zero: 0},
{Name: "arg6", Type: "u64", Zero: 0},
{Name: "arg7", Type: "u64", Zero: 0},
{Name: "arg8", Type: "u64", Zero: 0},
}

// decode half of the arguments leaving the rest to be populated as zero values
argnum := len(evtFields) / 2

evtVersion := events.NewVersion(1, 0, 0)
evtName := "test"
eventId := events.ID(0)
evtDef := events.NewDefinition(
eventId,
eventId+1000,
evtName,
evtVersion,
"",
"",
false,
false,
[]string{},
events.Dependencies{},
evtFields, // fields
nil,
)

events.Core.AddBatch(map[events.ID]events.Definition{eventId: evtDef})

b.ResetTimer()
for i := 0; i < b.N; i++ {
decoder := New(buffer)
args := make([]trace.Argument, len(evtFields))
_ = decoder.DecodeArguments(args, argnum, evtFields, evtName, eventId)
}
}
14 changes: 11 additions & 3 deletions pkg/cmd/flags/proctree.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Example:
both | process tree is built from both events and signals.
--proctree process-cache=8192 | will cache up to 8192 processes in the tree (LRU cache).
--proctree thread-cache=4096 | will cache up to 4096 threads in the tree (LRU cache).
--proctree disable-procfs-query | Will disable procfs queries during runtime
--proctree disable-procfs | will disable procfs entirely.
--proctree disable-procfs-query | will disable procfs quering during runtime.

Use comma OR use the flag multiple times to choose multiple options:
--proctree source=A,process-cache=B,thread-cache=C
Expand Down Expand Up @@ -66,7 +67,9 @@ func PrepareProcTree(cacheSlice []string) (proctree.ProcTreeConfig, error) {
default:
return config, fmt.Errorf("unrecognized proctree source option: %v", option)
}
cacheSet = true // at least the default ones
if config.Source != proctree.SourceNone {
cacheSet = true // at least the default ones
}
continue
}
if strings.HasPrefix(value, "process-cache=") {
Expand All @@ -93,7 +96,12 @@ func PrepareProcTree(cacheSlice []string) (proctree.ProcTreeConfig, error) {
cacheSet = true
continue
}
if strings.HasPrefix(value, "disable-procfs-query") {
if value == "disable-procfs" {
config.ProcfsInitialization = false
config.ProcfsQuerying = false
continue
}
if value == "disable-procfs-query" {
config.ProcfsQuerying = false
continue
}
Expand Down
34 changes: 31 additions & 3 deletions pkg/ebpf/controlplane/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package controlplane
import (
"context"
"fmt"
"sync"
"time"

"github.com/aquasecurity/libbpfgo"
Expand All @@ -25,6 +26,7 @@ type Controller struct {
lostSignalChan chan uint64
bpfModule *libbpfgo.Module
signalBuffer *libbpfgo.PerfBuffer
signalPool *sync.Pool
cgroupManager *containers.Containers
processTree *proctree.ProcessTree
enrichDisabled bool
Expand All @@ -43,6 +45,11 @@ func NewController(
signalChan: make(chan []byte, 100),
lostSignalChan: make(chan uint64),
bpfModule: bpfModule,
signalPool: &sync.Pool{
New: func() interface{} {
return &signal{}
},
},
cgroupManager: cgroupManager,
processTree: procTree,
enrichDisabled: enrichDisabled,
Expand All @@ -69,16 +76,22 @@ func (ctrl *Controller) Run(ctx context.Context) {
for {
select {
case signalData := <-ctrl.signalChan:
signal := signal{}
signal := ctrl.getSignalFromPool()

// NOTE: override all the fields of the signal, to avoid any previous data.
err := signal.Unmarshal(signalData)
if err != nil {
logger.Errorw("error unmarshaling signal ebpf buffer", "error", err)
ctrl.putSignalInPool(signal)
continue
}

err = ctrl.processSignal(signal)
if err != nil {
logger.Errorw("error processing control plane signal", "error", err)
}

ctrl.putSignalInPool(signal)
case lost := <-ctrl.lostSignalChan:
logger.Warnw(fmt.Sprintf("Lost %d control plane signals", lost))
case <-ctrl.ctx.Done():
Expand All @@ -93,8 +106,10 @@ func (ctrl *Controller) Stop() error {
return nil
}

// Private

// processSignal processes a signal from the control plane.
func (ctrl *Controller) processSignal(signal signal) error {
func (ctrl *Controller) processSignal(signal *signal) error {
switch signal.id {
case events.SignalCgroupMkdir:
return ctrl.processCgroupMkdir(signal.args)
Expand All @@ -111,7 +126,20 @@ func (ctrl *Controller) processSignal(signal signal) error {
return nil
}

// Private
// getSignalFromPool gets a signal from the pool.
// signal certainly contains old data, so it must be updated before use.
func (ctrl *Controller) getSignalFromPool() *signal {
// revive:disable:unchecked-type-assertion
sig := ctrl.signalPool.Get().(*signal)
// revive:enable:unchecked-type-assertion

return sig
}

// putSignalInPool puts a signal back in the pool.
func (ctrl *Controller) putSignalInPool(sig *signal) {
ctrl.signalPool.Put(sig)
}

// debug prints the process tree every 5 seconds (for debugging purposes).
func (ctrl *Controller) debug(enable bool) {
Expand Down
Loading
Loading