Skip to content
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

Proctree improvements (RSS/Performance) #4261

Merged
merged 4 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions docs/docs/advanced/data-sources/builtin/process-tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 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

Use comma OR use the flag multiple times to choose multiple options:
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/install/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ proctree:
cache:
process: 8192
thread: 4096
cache-ttl:
process: 60
thread: 60

capabilities:
bypass: false
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/policies/usage/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ proctree:
cache:
process: 8192
thread: 8192
cache-ttl:
process: 120
thread: 120
# cri:
# - runtime:
# name: docker
Expand Down
3 changes: 3 additions & 0 deletions examples/config/global_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ proctree:
# cache:
# process: 8192
# thread: 4096
# cache-ttl:
# process: 120
# thread: 120

capabilities:
bypass: false
Expand Down
16 changes: 14 additions & 2 deletions pkg/cmd/cobra/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,21 @@ func (c *RegoConfig) flags() []string {
//

type ProcTreeConfig struct {
Source string `mapstructure:"source"`
Cache ProcTreeCacheConfig `mapstructure:"cache"`
Source string `mapstructure:"source"`
Cache ProcTreeCacheConfig `mapstructure:"cache"`
CacheTTL ProcTreeCacheTTLConfig `mapstructure:"cache-ttl"`
}

type ProcTreeCacheConfig struct {
Process int `mapstructure:"process"`
Thread int `mapstructure:"thread"`
}

type ProcTreeCacheTTLConfig struct {
Process int `mapstructure:"process"`
Thread int `mapstructure:"thread"`
}

func (c *ProcTreeConfig) flags() []string {
flags := make([]string, 0)

Expand All @@ -198,6 +204,12 @@ func (c *ProcTreeConfig) flags() []string {
if c.Cache.Thread != 0 {
flags = append(flags, fmt.Sprintf("thread-cache=%d", c.Cache.Thread))
}
if c.CacheTTL.Process != 0 {
flags = append(flags, fmt.Sprintf("process-cache-ttl=%d", c.CacheTTL.Process))
}
if c.CacheTTL.Thread != 0 {
flags = append(flags, fmt.Sprintf("thread-cache-ttl=%d", c.CacheTTL.Thread))
}

return flags
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/cmd/cobra/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,17 @@ proctree:
cache:
process: 8192
thread: 4096
cache-ttl:
process: 5
thread: 10
`,
key: "proctree",
expectedFlags: []string{
"source=events",
"process-cache=8192",
"thread-cache=4096",
"process-cache-ttl=5",
"thread-cache-ttl=10",
},
},
{
Expand Down Expand Up @@ -591,6 +596,20 @@ func TestProcTreeConfigFlags(t *testing.T) {
"thread-cache=4096",
},
},
{
name: "process cache ttl set",
config: ProcTreeConfig{
Source: "",
CacheTTL: ProcTreeCacheTTLConfig{
Process: 5,
Thread: 10,
},
},
expected: []string{
"process-cache-ttl=5",
"thread-cache-ttl=10",
},
},
{
name: "all fields set",
config: ProcTreeConfig{
Expand Down
23 changes: 23 additions & 0 deletions pkg/cmd/flags/proctree.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strconv"
"strings"
"time"

"github.com/aquasecurity/tracee/pkg/logger"
"github.com/aquasecurity/tracee/pkg/proctree"
Expand All @@ -20,6 +21,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 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 queries during runtime

Use comma OR use the flag multiple times to choose multiple options:
Expand All @@ -35,6 +38,8 @@ func PrepareProcTree(cacheSlice []string) (proctree.ProcTreeConfig, error) {
Source: proctree.SourceNone, // disabled by default
ProcessCacheSize: proctree.DefaultProcessCacheSize,
ThreadCacheSize: proctree.DefaultThreadCacheSize,
ProcessCacheTTL: proctree.DefaultProcessCacheTTL,
ThreadCacheTTL: proctree.DefaultThreadCacheTTL,
ProcfsInitialization: true,
ProcfsQuerying: true,
}
Expand Down Expand Up @@ -93,6 +98,24 @@ func PrepareProcTree(cacheSlice []string) (proctree.ProcTreeConfig, error) {
cacheSet = true
continue
}
if strings.HasPrefix(value, "process-cache-ttl=") {
num := strings.TrimPrefix(value, "process-cache-ttl=")
ttl, err := strconv.Atoi(num)
if err != nil {
return config, err
}
config.ProcessCacheTTL = time.Duration(ttl) * time.Second
continue
}
if strings.HasPrefix(value, "thread-cache-ttl=") {
num := strings.TrimPrefix(value, "thread-cache-ttl=")
ttl, err := strconv.Atoi(num)
if err != nil {
return config, err
}
config.ThreadCacheTTL = time.Duration(ttl) * time.Second
continue
}
if strings.HasPrefix(value, "disable-procfs-query") {
config.ProcfsQuerying = false
continue
Expand Down
53 changes: 24 additions & 29 deletions pkg/proctree/proctree.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ import (
"sync"
"time"

lru "github.com/hashicorp/golang-lru/v2"
"github.com/hashicorp/golang-lru/v2/expirable"

"github.com/aquasecurity/tracee/pkg/errfmt"
"github.com/aquasecurity/tracee/pkg/logger"
traceetime "github.com/aquasecurity/tracee/pkg/time"
)
Expand Down Expand Up @@ -38,6 +37,8 @@ import (
const (
DefaultProcessCacheSize = 32768
DefaultThreadCacheSize = 32768
DefaultProcessCacheTTL = time.Second * 120
DefaultThreadCacheTTL = time.Second * 120
)

type SourceType int
Expand Down Expand Up @@ -67,18 +68,19 @@ type ProcTreeConfig struct {
Source SourceType
ProcessCacheSize int
ThreadCacheSize int
ProcessCacheTTL time.Duration
ThreadCacheTTL time.Duration
ProcfsInitialization bool // Determine whether to scan procfs data for process tree initialization
ProcfsQuerying bool // Determine whether to query procfs for missing information during runtime
}

// ProcessTree is a tree of processes and threads.
type ProcessTree struct {
processes *lru.Cache[uint32, *Process] // hash -> process
threads *lru.Cache[uint32, *Thread] // hash -> threads
procfsChan chan int // channel of pids to read from procfs
procfsOnce *sync.Once // busy loop debug message throttling
ctx context.Context // context for the process tree
mutex *sync.RWMutex // mutex for the process tree
processes *expirable.LRU[uint32, *Process] // hash -> process
threads *expirable.LRU[uint32, *Thread] // hash -> threads
procfsChan chan int // channel of pids to read from procfs
procfsOnce *sync.Once // busy loop debug message throttling
ctx context.Context // context for the process tree
procfsQuery bool
timeNormalizer traceetime.TimeNormalizer
}
Expand All @@ -89,22 +91,22 @@ func NewProcessTree(ctx context.Context, config ProcTreeConfig, timeNormalizer t
thrEvicted := 0

// Create caches for processes.
processes, err := lru.NewWithEvict[uint32, *Process](
processes := expirable.NewLRU[uint32, *Process](
config.ProcessCacheSize,
func(uint32, *Process) { procEvited++ },
func(k uint32, v *Process) {
procEvited++
},
config.ProcessCacheTTL,
)
if err != nil {
return nil, errfmt.WrapError(err)
}

// Create caches for threads.
threads, err := lru.NewWithEvict[uint32, *Thread](
threads := expirable.NewLRU[uint32, *Thread](
config.ThreadCacheSize,
func(uint32, *Thread) { thrEvicted++ },
func(k uint32, v *Thread) {
thrEvicted++
},
config.ThreadCacheTTL,
)
if err != nil {
return nil, errfmt.WrapError(err)
}

// Report cache stats if debug is enabled.
go func() {
Expand Down Expand Up @@ -141,7 +143,6 @@ func NewProcessTree(ctx context.Context, config ProcTreeConfig, timeNormalizer t
processes: processes,
threads: threads,
ctx: ctx,
mutex: &sync.RWMutex{},
procfsQuery: config.ProcfsQuerying,
timeNormalizer: timeNormalizer,
}
Expand All @@ -160,17 +161,16 @@ func NewProcessTree(ctx context.Context, config ProcTreeConfig, timeNormalizer t

// GetProcessByHash returns a process by its hash.
func (pt *ProcessTree) GetProcessByHash(hash uint32) (*Process, bool) {
pt.mutex.RLock()
defer pt.mutex.RUnlock()
process, ok := pt.processes.Get(hash)
if !ok {
return nil, false
}

return process, ok
}

// GetOrCreateProcessByHash returns a process by its hash, or creates a new one if it doesn't exist.
func (pt *ProcessTree) GetOrCreateProcessByHash(hash uint32) *Process {
pt.mutex.RLock()
defer pt.mutex.RUnlock()

process, ok := pt.processes.Get(hash)
if !ok {
// Each process must have a thread with thread ID matching its process ID.
Expand Down Expand Up @@ -199,17 +199,12 @@ func (pt *ProcessTree) GetOrCreateProcessByHash(hash uint32) *Process {

// GetThreadByHash returns a thread by its hash.
func (pt *ProcessTree) GetThreadByHash(hash uint32) (*Thread, bool) {
pt.mutex.RLock()
defer pt.mutex.RUnlock()
thread, ok := pt.threads.Get(hash)
return thread, ok
}

// GetOrCreateThreadByHash returns a thread by its hash, or creates a new one if it doesn't exist.
func (pt *ProcessTree) GetOrCreateThreadByHash(hash uint32) *Thread {
pt.mutex.RLock()
defer pt.mutex.RUnlock()

thread, ok := pt.threads.Get(hash)
if !ok {
// Create a new thread
Expand Down
Loading
Loading