inspektor-gadget

Форк
0
734 строки · 20.9 Кб
1
// Copyright 2019-2023 The Inspektor Gadget authors
2
//
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
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
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.
14

15
//go:build !withoutebpf
16

17
package tracer
18

19
import (
20
	"context"
21
	"errors"
22
	"fmt"
23
	"os"
24
	"sort"
25
	"strconv"
26
	"sync"
27
	"syscall"
28
	"time"
29
	"unsafe"
30

31
	"github.com/cilium/ebpf"
32
	"github.com/cilium/ebpf/link"
33
	"github.com/cilium/ebpf/perf"
34
	log "github.com/sirupsen/logrus"
35

36
	containercollection "github.com/inspektor-gadget/inspektor-gadget/pkg/container-collection"
37
	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
38
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
39
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/traceloop/types"
40
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
41
	"github.com/inspektor-gadget/inspektor-gadget/pkg/utils/syscalls"
42
)
43

44
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -type syscall_event_t -type syscall_event_cont_t -target ${TARGET} -cc clang -cflags ${CFLAGS} traceloop ./bpf/traceloop.bpf.c -- -I./bpf/
45

46
// These consts must match the content of traceloop.h.
47
const (
48
	useNullByteLength        uint64 = 0x0fffffffffffffff
49
	useRetAsParamLength      uint64 = 0x0ffffffffffffffe
50
	useArgIndexAsParamLength uint64 = 0x0ffffffffffffff0
51
	paramProbeAtExitMask     uint64 = 0xf000000000000000
52

53
	syscallEventTypeEnter uint8 = 0
54
	syscallEventTypeExit  uint8 = 1
55

56
	syscallArgs uint8 = 6
57
)
58

59
var (
60
	syscallsOnce         sync.Once
61
	syscallsDeclarations map[string]syscallDeclaration
62
)
63

64
type containerRingReader struct {
65
	perfReader *perf.Reader
66
	mntnsID    uint64
67
}
68

69
type Tracer struct {
70
	enricher gadgets.DataEnricherByMntNs
71

72
	innerMapSpec *ebpf.MapSpec
73

74
	objs      traceloopObjects
75
	enterLink link.Link
76
	exitLink  link.Link
77

78
	// Same comment than above, this map is designed to handle parallel access.
79
	// The keys of this map are containerID.
80
	readers sync.Map
81

82
	gadgetCtx     gadgets.GadgetContext
83
	ctx           context.Context
84
	cancel        context.CancelFunc
85
	eventCallback func(event *types.Event)
86
	waitGroup     sync.WaitGroup
87

88
	syscallFilters []string
89
}
90

91
type syscallEvent struct {
92
	bootTimestamp      uint64
93
	monotonicTimestamp uint64
94
	typ                uint8
95
	contNr             uint8
96
	cpu                uint16
97
	id                 uint16
98
	pid                uint32
99
	comm               string
100
	args               []uint64
101
	mountNsID          uint64
102
	retval             int
103
}
104

105
type syscallEventContinued struct {
106
	monotonicTimestamp uint64
107
	index              uint8
108
	param              string
109
}
110

111
func NewTracer(enricher gadgets.DataEnricherByMntNs, filters []string) (*Tracer, error) {
112
	t := &Tracer{
113
		enricher:       enricher,
114
		syscallFilters: filters,
115
	}
116
	if err := t.install(); err != nil {
117
		t.close()
118
		return nil, err
119
	}
120
	return t, nil
121
}
122

123
func (t *Tracer) install() error {
124
	spec, err := loadTraceloop()
125
	if err != nil {
126
		return fmt.Errorf("loading ebpf program: %w", err)
127
	}
128

129
	gadgets.FixBpfKtimeGetBootNs(spec.Programs)
130

131
	syscallsOnce.Do(func() {
132
		syscallsDeclarations, err = gatherSyscallsDeclarations()
133
	})
134
	if err != nil {
135
		return fmt.Errorf("gathering syscall definitions: %w", err)
136
	}
137

138
	// Fill the syscall map with specific syscall signatures.
139
	syscallsMapSpec := spec.Maps["syscalls"]
140
	for name, def := range syscallDefs {
141
		number, ok := syscalls.GetSyscallNumberByName(name)
142
		if !ok {
143
			// It's possible that the syscall doesn't exist for this architecture, skip it
144
			continue
145
		}
146

147
		// We need to do so to avoid taking each time the same address.
148
		def := def
149
		syscallsMapSpec.Contents = append(syscallsMapSpec.Contents, ebpf.MapKV{
150
			Key:   uint64(number),
151
			Value: def,
152
		})
153
	}
154

155
	// Fill the syscall filter map with the corresponding syscall numbers.
156
	syscallFiltersMapSpec := spec.Maps["syscall_filters"]
157
	for _, name := range t.syscallFilters {
158
		if name == "" {
159
			continue
160
		}
161

162
		number, ok := syscalls.GetSyscallNumberByName(name)
163
		if !ok {
164
			return fmt.Errorf("syscall %q does not exist", name)
165
		}
166

167
		syscallFiltersMapSpec.Contents = append(syscallFiltersMapSpec.Contents, ebpf.MapKV{
168
			Key: uint64(number),
169
			// We do not care about the value itself but we need to provide one.
170
			Value: true,
171
		})
172
	}
173

174
	consts := make(map[string]interface{})
175
	consts["filter_syscall"] = len(syscallFiltersMapSpec.Contents) > 0
176
	if err := gadgets.LoadeBPFSpec(nil, spec, consts, &t.objs); err != nil {
177
		return fmt.Errorf("loading ebpf program: %w", err)
178
	}
179

180
	t.enterLink, err = link.AttachRawTracepoint(link.RawTracepointOptions{
181
		Name:    "sys_enter",
182
		Program: t.objs.IgTraceloopE,
183
	})
184
	if err != nil {
185
		return fmt.Errorf("opening enter tracepoint: %w", err)
186
	}
187

188
	t.exitLink, err = link.AttachRawTracepoint(link.RawTracepointOptions{
189
		Name:    "sys_exit",
190
		Program: t.objs.IgTraceloopX,
191
	})
192
	if err != nil {
193
		return fmt.Errorf("opening exit tracepoint: %w", err)
194
	}
195

196
	t.innerMapSpec = spec.Maps["map_of_perf_buffers"].InnerMap
197

198
	return nil
199
}
200

201
// Stop stops the tracer
202
// TODO: Remove after refactoring
203
func (t *Tracer) Stop() {
204
	t.close()
205
}
206

207
func (t *Tracer) close() {
208
	t.enterLink = gadgets.CloseLink(t.enterLink)
209
	t.exitLink = gadgets.CloseLink(t.exitLink)
210

211
	t.readers.Range(func(key, _ any) bool {
212
		t.Delete(key.(string))
213

214
		return true
215
	})
216

217
	t.objs.Close()
218
}
219

220
func (t *Tracer) Attach(containerID string, mntnsID uint64) error {
221
	innerBufferSpec := t.innerMapSpec.Copy()
222
	innerBufferSpec.Name = fmt.Sprintf("perf_buffer_%d", mntnsID)
223

224
	// 1. Create inner Map as perf buffer.
225
	innerBuffer, err := ebpf.NewMap(innerBufferSpec)
226
	if err != nil {
227
		return fmt.Errorf("creating inner map: %w", err)
228
	}
229

230
	// 2. Use this inner Map to create the perf reader.
231
	perfReader, err := perf.NewReaderWithOptions(innerBuffer, gadgets.PerfBufferPages*os.Getpagesize(), perf.ReaderOptions{Overwritable: true})
232
	if err != nil {
233
		innerBuffer.Close()
234

235
		return fmt.Errorf("creating perf ring buffer: %w", err)
236
	}
237

238
	// 3. Add the inner map's file descriptor to outer map.
239
	err = t.objs.MapOfPerfBuffers.Put(mntnsID, innerBuffer)
240
	if err != nil {
241
		innerBuffer.Close()
242
		perfReader.Close()
243

244
		return fmt.Errorf("adding perf buffer to map with mntnsID %d: %w", mntnsID, err)
245
	}
246

247
	t.readers.Store(containerID, &containerRingReader{
248
		perfReader: perfReader,
249
		mntnsID:    mntnsID,
250
	})
251

252
	return nil
253
}
254

255
func timestampFromEvent(event *syscallEvent) eventtypes.Time {
256
	if !gadgets.HasBpfKtimeGetBootNs() {
257
		// Traceloop works differently than other gadgets: if the
258
		// kernel does not support bpf_ktime_get_boot_ns, don't
259
		// generate a timestamp from userspace because traceloop reads
260
		// events from the ring buffer an arbitrary long time after
261
		// they are generated, so the timestamp would be meaningless.
262

263
		// However we need some kind of timestamp for sorting events
264
		return gadgets.WallTimeFromBootTime(event.monotonicTimestamp)
265
	}
266
	return gadgets.WallTimeFromBootTime(event.bootTimestamp)
267
}
268

269
// Copied/pasted/adapted from kernel macro round_up:
270
// https://elixir.bootlin.com/linux/v6.0/source/include/linux/math.h#L25
271
func roundUp(x, y uintptr) uintptr {
272
	return ((x - 1) | (y - 1)) + 1
273
}
274

275
// The kernel aligns size of perf event with the following snippet:
276
// void perf_prepare_sample(...)
277
//
278
//	{
279
//		//...
280
//		size = round_up(sum + sizeof(u32), sizeof(u64));
281
//		raw->size = size - sizeof(u32);
282
//		frag->pad = raw->size - sum;
283
//		// ...
284
//	}
285
//
286
// (https://elixir.bootlin.com/linux/v6.0/source/kernel/events/core.c#L7353)
287
// In the case of our structure of interest (i.e. struct_syscall_event_t and
288
// struct_syscall_event_cont_t), their size will be increased by 4, here is
289
// an example for struct_syscall_event_t which size is 88:
290
// size = round_up(sum + sizeof(u32), sizeof(u64))
291
//
292
//	= round_up(88 + 4, 8)
293
//	= round_up(92, 8)
294
//	= 96
295
//
296
// raw->size = size - sizeof(u32)
297
//
298
//	= 96 - 4
299
//	= 92
300
//
301
// So, 4 bytes will be added as padding at the end of the event and the size we
302
// will read getting perfEventSample will be 92 instead of 88.
303
func alignSize(structSize uintptr) uintptr {
304
	var ret uintptr
305
	var foo uint64
306
	var bar uint32
307

308
	ret = roundUp(structSize+unsafe.Sizeof(bar), unsafe.Sizeof(foo))
309
	ret = ret - unsafe.Sizeof(bar)
310

311
	return ret
312
}
313

314
// Convert a return value to corresponding error number if meaningful.
315
// See man syscalls:
316
// Note:
317
// system calls indicate a failure by returning a negative error
318
// number to the caller on architectures without a separate error
319
// register/flag, as noted in syscall(2); when this happens, the
320
// wrapper function negates the returned error number (to make it
321
// positive), copies it to errno, and returns -1 to the caller of
322
// the wrapper.
323
func retToStr(ret int) string {
324
	errNo := int64(ret)
325
	if errNo >= -4095 && errNo <= -1 {
326
		return fmt.Sprintf("-1 (%s)", syscall.Errno(-errNo).Error())
327
	}
328
	return fmt.Sprintf("%d", ret)
329
}
330

331
func (t *Tracer) Read(containerID string) ([]*types.Event, error) {
332
	syscallContinuedEventsMap := make(map[uint64][]*syscallEventContinued)
333
	syscallEnterEventsMap := make(map[uint64][]*syscallEvent)
334
	syscallExitEventsMap := make(map[uint64][]*syscallEvent)
335
	events := make([]*types.Event, 0)
336

337
	r, ok := t.readers.Load(containerID)
338
	if !ok {
339
		return nil, fmt.Errorf("no perf reader for %q", containerID)
340
	}
341

342
	reader, ok := r.(*containerRingReader)
343
	if !ok {
344
		return nil, errors.New("the map should only contain *containerRingReader")
345
	}
346

347
	if reader.perfReader == nil {
348
		log.Infof("reader for %v is nil, it was surely detached", containerID)
349

350
		return nil, nil
351
	}
352

353
	err := reader.perfReader.Pause()
354
	if err != nil {
355
		return nil, err
356
	}
357

358
	reader.perfReader.SetDeadline(time.Now())
359

360
	records := make([]*perf.Record, 0)
361
	for {
362
		record, err := reader.perfReader.Read()
363
		if err != nil {
364
			if errors.Is(err, os.ErrDeadlineExceeded) {
365
				break
366
			} else {
367
				return nil, err
368
			}
369
		}
370
		records = append(records, &record)
371
	}
372

373
	err = reader.perfReader.Resume()
374
	if err != nil {
375
		return nil, err
376
	}
377

378
	for _, record := range records {
379
		size := len(record.RawSample)
380

381
		var sysEvent *traceloopSyscallEventT
382
		var sysEventCont *traceloopSyscallEventContT
383

384
		switch uintptr(size) {
385
		case alignSize(unsafe.Sizeof(*sysEvent)):
386
			sysEvent = (*traceloopSyscallEventT)(unsafe.Pointer(&record.RawSample[0]))
387

388
			event := &syscallEvent{
389
				bootTimestamp:      sysEvent.BootTimestamp,
390
				monotonicTimestamp: sysEvent.MonotonicTimestamp,
391
				typ:                sysEvent.Typ,
392
				contNr:             sysEvent.ContNr,
393
				cpu:                sysEvent.Cpu,
394
				id:                 sysEvent.Id,
395
				pid:                sysEvent.Pid,
396
				comm:               gadgets.FromCString(sysEvent.Comm[:]),
397
				mountNsID:          reader.mntnsID,
398
			}
399

400
			var typeMap *map[uint64][]*syscallEvent
401
			switch event.typ {
402
			case syscallEventTypeEnter:
403
				event.args = make([]uint64, syscallArgs)
404
				for i := uint8(0); i < syscallArgs; i++ {
405
					event.args[i] = sysEvent.Args[i]
406
				}
407

408
				typeMap = &syscallEnterEventsMap
409
			case syscallEventTypeExit:
410
				// In the C structure, args is an array of uint64.
411
				// But in this particular case, we used it to store a C long, i.e. the
412
				// syscall return value, so it is safe to cast it to golang int.
413
				event.retval = int(sysEvent.Args[0])
414

415
				typeMap = &syscallExitEventsMap
416
			default:
417
				// Rather than returning an error, we skip this event.
418
				log.Debugf("type %d is not a valid type for syscallEvent, received data are: %v", event.typ, record.RawSample)
419

420
				continue
421
			}
422

423
			if _, ok := (*typeMap)[event.monotonicTimestamp]; !ok {
424
				(*typeMap)[event.monotonicTimestamp] = make([]*syscallEvent, 0)
425
			}
426

427
			(*typeMap)[event.monotonicTimestamp] = append((*typeMap)[event.monotonicTimestamp], event)
428
		case alignSize(unsafe.Sizeof(*sysEventCont)):
429
			sysEventCont = (*traceloopSyscallEventContT)(unsafe.Pointer(&record.RawSample[0]))
430

431
			event := &syscallEventContinued{
432
				monotonicTimestamp: sysEventCont.MonotonicTimestamp,
433
				index:              sysEventCont.Index,
434
			}
435

436
			if sysEventCont.Failed != 0 {
437
				event.param = "(Failed to dereference pointer)"
438
			} else if sysEventCont.Length == useNullByteLength {
439
				// 0 byte at [C.PARAM_LENGTH - 1] is enforced in BPF code
440
				event.param = gadgets.FromCString(sysEventCont.Param[:])
441
			} else {
442
				event.param = gadgets.FromCStringN(sysEventCont.Param[:], int(sysEventCont.Length))
443
			}
444

445
			// Remove all non unicode character from the string.
446
			event.param = strconv.Quote(event.param)
447

448
			_, ok := syscallContinuedEventsMap[event.monotonicTimestamp]
449
			if !ok {
450
				// Just create a 0 elements slice for the moment, the ContNr will be
451
				// checked later.
452
				syscallContinuedEventsMap[event.monotonicTimestamp] = make([]*syscallEventContinued, 0)
453
			}
454

455
			syscallContinuedEventsMap[event.monotonicTimestamp] = append(syscallContinuedEventsMap[event.monotonicTimestamp], event)
456
		default:
457
			log.Debugf("size %d does not correspond to any expected element, which are %d and %d; received data are: %v", size, unsafe.Sizeof(sysEvent), unsafe.Sizeof(sysEventCont), record.RawSample)
458
		}
459
	}
460

461
	// Let's try to publish the events we gathered.
462
	for enterTimestamp, enterTimestampEvents := range syscallEnterEventsMap {
463
		for _, enterEvent := range enterTimestampEvents {
464
			event := &types.Event{
465
				Event: eventtypes.Event{
466
					Type:      eventtypes.NORMAL,
467
					Timestamp: timestampFromEvent(enterEvent),
468
				},
469
				CPU:           enterEvent.cpu,
470
				Pid:           enterEvent.pid,
471
				Comm:          enterEvent.comm,
472
				WithMountNsID: eventtypes.WithMountNsID{MountNsID: enterEvent.mountNsID},
473
				Syscall:       syscallGetName(enterEvent.id),
474
			}
475

476
			syscallDeclaration, err := getSyscallDeclaration(syscallsDeclarations, event.Syscall)
477
			if err != nil {
478
				return nil, fmt.Errorf("getting syscall definition")
479
			}
480

481
			parametersNumber := syscallDeclaration.getParameterCount()
482
			event.Parameters = make([]types.SyscallParam, parametersNumber)
483
			log.Debugf("\tevent parametersNumber: %d", parametersNumber)
484

485
			for i := uint8(0); i < parametersNumber; i++ {
486
				paramName, err := syscallDeclaration.getParameterName(i)
487
				if err != nil {
488
					return nil, fmt.Errorf("getting syscall parameter name: %w", err)
489
				}
490
				log.Debugf("\t\tevent paramName: %q", paramName)
491

492
				isPointer, err := syscallDeclaration.paramIsPointer(i)
493
				if err != nil {
494
					return nil, fmt.Errorf("checking syscall parameter is a pointer: %w", err)
495
				}
496

497
				format := "%d"
498
				if isPointer {
499
					format = "0x%x"
500
				}
501
				paramValue := fmt.Sprintf(format, enterEvent.args[i])
502
				log.Debugf("\t\tevent paramValue: %q", paramValue)
503

504
				var paramContent *string
505

506
				for _, syscallContEvent := range syscallContinuedEventsMap[enterTimestamp] {
507
					if syscallContEvent.index == i {
508
						paramContent = &syscallContEvent.param
509
						log.Debugf("\t\t\tevent paramContent: %q", *paramContent)
510

511
						break
512
					}
513
				}
514

515
				event.Parameters[i] = types.SyscallParam{
516
					Name:    paramName,
517
					Value:   paramValue,
518
					Content: paramContent,
519
				}
520
			}
521

522
			delete(syscallContinuedEventsMap, enterTimestamp)
523

524
			// There is no exit event for exit(), exit_group() and rt_sigreturn().
525
			if event.Syscall == "exit" || event.Syscall == "exit_group" || event.Syscall == "rt_sigreturn" {
526
				delete(syscallEnterEventsMap, enterTimestamp)
527

528
				if t.enricher != nil {
529
					t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
530
				}
531

532
				// As there is no exit events for these syscalls,
533
				// then there is no return value.
534
				event.Retval = "X"
535

536
				log.Debugf("%v", event)
537
				events = append(events, event)
538

539
				continue
540
			}
541

542
			exitTimestampEvents, ok := syscallExitEventsMap[enterTimestamp]
543
			if !ok {
544
				log.Debugf("no exit event for timestamp %d", enterTimestamp)
545

546
				continue
547
			}
548

549
			for _, exitEvent := range exitTimestampEvents {
550
				if enterEvent.id != exitEvent.id || enterEvent.pid != exitEvent.pid {
551
					continue
552
				}
553

554
				event.Retval = retToStr(exitEvent.retval)
555

556
				delete(syscallEnterEventsMap, enterTimestamp)
557
				delete(syscallExitEventsMap, enterTimestamp)
558

559
				if t.enricher != nil {
560
					t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
561
				}
562
				log.Debugf("%v", event)
563
				events = append(events, event)
564

565
				break
566
			}
567
		}
568
	}
569

570
	log.Debugf("len(events): %d; len(syscallEnterEventsMap): %d; len(syscallExitEventsMap): %d; len(syscallContinuedEventsMap): %d\n", len(events), len(syscallEnterEventsMap), len(syscallExitEventsMap), len(syscallContinuedEventsMap))
571

572
	// It is possible there are some incomplete events for two mains reasons:
573
	// 1. Traceloop was started in the middle of a syscall, then we will only get
574
	//    the exit but not the enter.
575
	// 2. The buffer is full and so it only remains some exit events and not the
576
	//    corresponding enter.
577
	// Rather than dropping these incomplete events, we just add them to the
578
	// events to be published.
579
	for _, enterTimestampEvents := range syscallEnterEventsMap {
580
		for _, enterEvent := range enterTimestampEvents {
581
			syscallName := syscallGetName(enterEvent.id)
582

583
			incompleteEnterEvent := &types.Event{
584
				Event: eventtypes.Event{
585
					Type:      eventtypes.NORMAL,
586
					Timestamp: timestampFromEvent(enterEvent),
587
				},
588
				CPU:           enterEvent.cpu,
589
				Pid:           enterEvent.pid,
590
				Comm:          enterEvent.comm,
591
				WithMountNsID: eventtypes.WithMountNsID{MountNsID: enterEvent.mountNsID},
592
				Syscall:       syscallName,
593
				Retval:        "unfinished",
594
			}
595

596
			if t.enricher != nil {
597
				t.enricher.EnrichByMntNs(&incompleteEnterEvent.CommonData, incompleteEnterEvent.MountNsID)
598
			}
599

600
			events = append(events, incompleteEnterEvent)
601

602
			log.Debugf("enterEvent(%q): %v\n", syscallName, enterEvent)
603
		}
604
	}
605

606
	for _, exitTimestampEvents := range syscallExitEventsMap {
607
		for _, exitEvent := range exitTimestampEvents {
608
			syscallName := syscallGetName(exitEvent.id)
609

610
			incompleteExitEvent := &types.Event{
611
				Event: eventtypes.Event{
612
					Type:      eventtypes.NORMAL,
613
					Timestamp: timestampFromEvent(exitEvent),
614
				},
615
				CPU:           exitEvent.cpu,
616
				Pid:           exitEvent.pid,
617
				Comm:          exitEvent.comm,
618
				WithMountNsID: eventtypes.WithMountNsID{MountNsID: exitEvent.mountNsID},
619
				Syscall:       syscallName,
620
				Retval:        retToStr(exitEvent.retval),
621
			}
622

623
			if t.enricher != nil {
624
				t.enricher.EnrichByMntNs(&incompleteExitEvent.CommonData, incompleteExitEvent.MountNsID)
625
			}
626

627
			events = append(events, incompleteExitEvent)
628

629
			log.Debugf("exitEvent(%q): %v\n", syscallName, exitEvent)
630
		}
631
	}
632

633
	// Sort all events by ascending timestamp.
634
	sort.Slice(events, func(i, j int) bool {
635
		return events[i].Timestamp < events[j].Timestamp
636
	})
637

638
	// Remove timestamps if we couldn't get reliable ones
639
	if !gadgets.HasBpfKtimeGetBootNs() {
640
		for i := range events {
641
			events[i].Timestamp = 0
642
		}
643
	}
644

645
	return events, nil
646
}
647

648
func (t *Tracer) Detach(mntnsID uint64) error {
649
	err := t.objs.MapOfPerfBuffers.Delete(mntnsID)
650
	if err != nil {
651
		return fmt.Errorf("removing perf buffer from map with mntnsID %d", mntnsID)
652
	}
653

654
	return nil
655
}
656

657
func (t *Tracer) Delete(containerID string) error {
658
	r, ok := t.readers.LoadAndDelete(containerID)
659
	if !ok {
660
		return fmt.Errorf("no reader for containerID %s", containerID)
661
	}
662

663
	reader := r.(*containerRingReader)
664
	err := reader.perfReader.Close()
665
	reader.perfReader = nil
666

667
	return err
668
}
669

670
// --- Registry changes
671

672
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
673
	return &Tracer{}, nil
674
}
675

676
func (t *Tracer) Init(gadgetCtx gadgets.GadgetContext) error {
677
	t.syscallFilters = gadgetCtx.GadgetParams().Get(ParamSyscallFilters).AsStringSlice()
678

679
	if err := t.install(); err != nil {
680
		t.close()
681
		return fmt.Errorf("installing tracer: %w", err)
682
	}
683

684
	// Context must be created before the first call to AttachContainer
685
	t.gadgetCtx = gadgetCtx
686
	t.ctx, t.cancel = gadgetcontext.WithTimeoutOrCancel(gadgetCtx.Context(), gadgetCtx.Timeout())
687
	return nil
688
}
689

690
func (t *Tracer) SetEventHandler(handler any) {
691
	nh, ok := handler.(func(ev *types.Event))
692
	if !ok {
693
		panic("event handler invalid")
694
	}
695
	t.eventCallback = nh
696
}
697

698
func (t *Tracer) AttachContainer(container *containercollection.Container) error {
699
	t.waitGroup.Add(1)
700
	err := t.Attach(container.Runtime.ContainerID, container.Mntns)
701
	if err != nil {
702
		t.waitGroup.Done()
703
		return err
704
	}
705
	go func() {
706
		defer t.waitGroup.Done()
707
		<-t.ctx.Done()
708
		evs, err := t.Read(container.Runtime.ContainerID)
709
		if err != nil {
710
			t.gadgetCtx.Logger().Debugf("error reading from container %s: %v", container.Runtime.ContainerID, err)
711
			return
712
		}
713
		for _, ev := range evs {
714
			ev.SetContainerMetadata(&container.K8s.BasicK8sMetadata, &container.Runtime.BasicRuntimeMetadata)
715
			t.eventCallback(ev)
716
		}
717
	}()
718
	return nil
719
}
720

721
func (t *Tracer) DetachContainer(container *containercollection.Container) error {
722
	return t.Detach(container.Mntns)
723
}
724

725
func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
726
	<-t.ctx.Done()
727
	t.waitGroup.Wait()
728
	return nil
729
}
730

731
func (t *Tracer) Close() {
732
	t.cancel()
733
	t.close()
734
}
735

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.