1
// Copyright 2022-2023 The Inspektor Gadget authors
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
7
// http://www.apache.org/licenses/LICENSE-2.0
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
27
utilstest "github.com/inspektor-gadget/inspektor-gadget/internal/test"
28
containerutils "github.com/inspektor-gadget/inspektor-gadget/pkg/container-utils"
29
"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
30
snapshotProcessTypes "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/snapshot/process/types"
31
eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
34
type collectorFunc func(config *Config, enricher gadgets.DataEnricherByMntNs) ([]*snapshotProcessTypes.Event, error)
36
func BenchmarkSnapshotProcessEBPFTracer(b *testing.B) {
37
benchmarkTracer(b, runeBPFCollector)
40
func BenchmarkSnapshotProcessProcfsTracer(b *testing.B) {
41
benchmarkTracer(b, runProcfsCollector)
44
func benchmarkTracer(b *testing.B, runCollector collectorFunc) {
45
utilstest.RequireRoot(b)
47
for n := 0; n < b.N; n++ {
48
_, err := runCollector(&Config{}, nil)
50
b.Fatalf("benchmarking collector: %s", err)
55
func TestSnapshotProcessEBPFTracer(t *testing.T) {
56
testTracer(t, runeBPFCollector)
59
func TestSnapshotProcessProcfsTracer(t *testing.T) {
60
testTracer(t, runProcfsCollector)
63
func testTracer(t *testing.T, runCollector collectorFunc) {
66
utilstest.RequireRoot(t)
69
type testDefinition struct {
70
getTracerConfig func(info *utilstest.RunnerInfo) *Config
71
runnerConfig *utilstest.RunnerConfig
72
generateEvent func() (int, error)
73
validateEvent func(t *testing.T, info *utilstest.RunnerInfo, sleepPid int, events []snapshotProcessTypes.Event)
76
for name, test := range map[string]testDefinition{
77
"captures_all_events_with_no_filters_configured": {
78
getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
81
generateEvent: generateEvent,
82
validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, sleepPid int) *snapshotProcessTypes.Event {
83
return &snapshotProcessTypes.Event{
84
Event: eventtypes.Event{
85
Type: eventtypes.NORMAL,
91
WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
95
"captures_no_events_with_no_matching_filter": {
96
getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
97
// We can't use 0 as the mntnsid because the tracer will collect
98
// some defunct processes that don't have any mntnsid defined
99
// anymore. Then, we set the network namespace inode id to be sure
100
// there is not any mount namespace using that same inode.
101
mntns, _ := containerutils.GetNetNs(os.Getpid())
103
MountnsMap: utilstest.CreateMntNsFilterMap(t, mntns),
106
generateEvent: generateEvent,
107
validateEvent: utilstest.ExpectNoEvent[snapshotProcessTypes.Event, int],
109
"captures_events_with_matching_filter": {
110
getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
112
MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
115
generateEvent: generateEvent,
116
// We have to use ExpectAtLeastOneEvent because it's possible that the
117
// golang thread that executes this test is also captured
118
validateEvent: utilstest.ExpectAtLeastOneEvent(func(info *utilstest.RunnerInfo, sleepPid int) *snapshotProcessTypes.Event {
119
return &snapshotProcessTypes.Event{
120
Event: eventtypes.Event{
121
Type: eventtypes.NORMAL,
126
WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
130
// This is a hacky way to test this: one of the threads of the goroutine is moved to
131
// the mount namespace created for testing, also the sleep process we execute is
132
// there. That's why 2 events are expected. A better way would be to execute a
133
// command that creates multiple threads and check if we capture all of them, but so
134
// far I haven't found an easy way to do so. One idea is to use python but it seems
135
// too complicated and will introduce another dependency for testing.
136
"captures_events_with_matching_filter_threads": {
137
getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
139
MountnsMap: utilstest.CreateMntNsFilterMap(t, info.MountNsID),
143
generateEvent: generateEvent,
144
validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, sleepPid int, events []snapshotProcessTypes.Event) {
145
if len(events) != 2 {
146
t.Fatalf("%d events expected, found: %d", 2, len(events))
149
expectedEvent := &snapshotProcessTypes.Event{
150
Event: eventtypes.Event{
151
Type: eventtypes.NORMAL,
157
WithMountNsID: eventtypes.WithMountNsID{MountNsID: info.MountNsID},
160
for _, event := range events {
161
if reflect.DeepEqual(expectedEvent, &event) {
166
t.Fatalf("Event wasn't captured")
169
"no_threads_are_captured": {
170
getTracerConfig: func(info *utilstest.RunnerInfo) *Config {
173
generateEvent: generateEvent,
174
validateEvent: func(t *testing.T, info *utilstest.RunnerInfo, sleepPid int, events []snapshotProcessTypes.Event) {
175
if len(events) == 0 {
176
t.Fatalf("no events were captured")
178
for _, event := range events {
179
if event.Pid != event.Tid {
180
t.Fatalf("thread %d was captured", event.Tid)
188
t.Run(name, func(t *testing.T) {
191
runner := utilstest.NewRunnerWithTest(t, test.runnerConfig)
195
utilstest.RunWithRunner(t, runner, func() error {
197
sleepPid, err = test.generateEvent()
201
events, err := runCollector(test.getTracerConfig(runner.Info), nil)
203
t.Fatalf("running collector: %s", err)
206
// TODO: This won't be required once we pass pointers everywhere
207
validateEvents := []snapshotProcessTypes.Event{}
208
for _, event := range events {
209
// Normalize parent PID to avoid failing tests as this is not trivial to
210
// guess the parent PID.
213
validateEvents = append(validateEvents, *event)
216
test.validateEvent(t, runner.Info, sleepPid, validateEvents)
221
// Function that runs a "sleep" process.
222
func generateEvent() (int, error) {
223
cmd := exec.Command("/bin/sleep", "5")
224
if err := cmd.Start(); err != nil {
225
return 0, fmt.Errorf("running command: %w", err)
228
return cmd.Process.Pid, nil