inspektor-gadget

Форк
0
315 строк · 7.8 Кб
1
// Copyright 2022-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
	"errors"
21
	"fmt"
22
	"os"
23
	"unsafe"
24

25
	"github.com/cilium/ebpf"
26
	"github.com/cilium/ebpf/link"
27
	"github.com/cilium/ebpf/perf"
28

29
	gadgetcontext "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-context"
30
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
31
	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/fsslower/types"
32
	eventtypes "github.com/inspektor-gadget/inspektor-gadget/pkg/types"
33
)
34

35
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -no-global-types -target $TARGET -cc clang -cflags ${CFLAGS} -type event fsslower ./bpf/fsslower.bpf.c -- -I./bpf/
36
type Config struct {
37
	MountnsMap *ebpf.Map
38

39
	Filesystem string
40
	MinLatency uint
41
}
42

43
type Tracer struct {
44
	config        *Config
45
	enricher      gadgets.DataEnricherByMntNs
46
	eventCallback func(*types.Event)
47

48
	objs            fsslowerObjects
49
	readEnterLink   link.Link
50
	readExitLink    link.Link
51
	writeEnterLink  link.Link
52
	writeExitLink   link.Link
53
	openEnterLink   link.Link
54
	openExitLink    link.Link
55
	syncEnterLink   link.Link
56
	syncExitLink    link.Link
57
	statfsEnterLink link.Link
58
	statfsExitLink  link.Link
59
	reader          *perf.Reader
60
}
61

62
type fsConf struct {
63
	read   string
64
	write  string
65
	open   string
66
	fsync  string
67
	statfs string
68
}
69

70
var fsConfMap = map[string]fsConf{
71
	"btrfs": {
72
		read:   "btrfs_file_read_iter",
73
		write:  "btrfs_file_write_iter",
74
		open:   "btrfs_file_open",
75
		fsync:  "btrfs_sync_file",
76
		statfs: "btrfs_statfs",
77
	},
78
	"ext4": {
79
		read:   "ext4_file_read_iter",
80
		write:  "ext4_file_write_iter",
81
		open:   "ext4_file_open",
82
		fsync:  "ext4_sync_file",
83
		statfs: "ext4_statfs",
84
	},
85
	"nfs": {
86
		read:   "nfs_file_read",
87
		write:  "nfs_file_write",
88
		open:   "nfs_file_open",
89
		fsync:  "nfs_file_fsync",
90
		statfs: "nfs_statfs",
91
	},
92
	"xfs": {
93
		read:   "xfs_file_read_iter",
94
		write:  "xfs_file_write_iter",
95
		open:   "xfs_file_open",
96
		fsync:  "xfs_file_fsync",
97
		statfs: "xfs_fs_statfs",
98
	},
99
}
100

101
func NewTracer(config *Config, enricher gadgets.DataEnricherByMntNs,
102
	eventCallback func(*types.Event),
103
) (*Tracer, error) {
104
	t := &Tracer{
105
		config:        config,
106
		enricher:      enricher,
107
		eventCallback: eventCallback,
108
	}
109

110
	if err := t.install(); err != nil {
111
		t.close()
112
		return nil, err
113
	}
114

115
	go t.run()
116

117
	return t, nil
118
}
119

120
// Stop stops the tracer
121
// TODO: Remove after refactoring
122
func (t *Tracer) Stop() {
123
	t.close()
124
}
125

126
func (t *Tracer) close() {
127
	// read
128
	t.readEnterLink = gadgets.CloseLink(t.readEnterLink)
129
	t.readExitLink = gadgets.CloseLink(t.readExitLink)
130

131
	// write
132
	t.writeEnterLink = gadgets.CloseLink(t.writeEnterLink)
133
	t.writeExitLink = gadgets.CloseLink(t.writeExitLink)
134

135
	// open
136
	t.openEnterLink = gadgets.CloseLink(t.openEnterLink)
137
	t.openExitLink = gadgets.CloseLink(t.openExitLink)
138

139
	// sync
140
	t.syncEnterLink = gadgets.CloseLink(t.syncEnterLink)
141
	t.syncExitLink = gadgets.CloseLink(t.syncExitLink)
142

143
	// statfs
144
	t.statfsEnterLink = gadgets.CloseLink(t.statfsEnterLink)
145
	t.statfsExitLink = gadgets.CloseLink(t.statfsExitLink)
146

147
	if t.reader != nil {
148
		t.reader.Close()
149
	}
150

151
	t.objs.Close()
152
}
153

154
func (t *Tracer) install() error {
155
	var err error
156

157
	spec, err := loadFsslower()
158
	if err != nil {
159
		return fmt.Errorf("loading ebpf program: %w", err)
160
	}
161

162
	consts := map[string]interface{}{
163
		"min_lat_ns": uint64(t.config.MinLatency * 1000 * 1000),
164
	}
165

166
	if err := gadgets.LoadeBPFSpec(t.config.MountnsMap, spec, consts, &t.objs); err != nil {
167
		return fmt.Errorf("loading ebpf spec: %w", err)
168
	}
169

170
	// choose a configuration based on the filesystem type passed
171
	fsConf, ok := fsConfMap[t.config.Filesystem]
172
	if !ok {
173
		return fmt.Errorf("%q is not a supported filesystem", t.config.Filesystem)
174
	}
175

176
	// read
177
	t.readEnterLink, err = link.Kprobe(fsConf.read, t.objs.IgFsslReadE, nil)
178
	if err != nil {
179
		return fmt.Errorf("attaching kprobe: %w", err)
180
	}
181
	t.readExitLink, err = link.Kretprobe(fsConf.read, t.objs.IgFsslReadX, nil)
182
	if err != nil {
183
		return fmt.Errorf("attaching kretprobe: %w", err)
184
	}
185

186
	// write
187
	t.writeEnterLink, err = link.Kprobe(fsConf.write, t.objs.IgFsslWrE, nil)
188
	if err != nil {
189
		return fmt.Errorf("attaching kprobe: %w", err)
190
	}
191
	t.writeExitLink, err = link.Kretprobe(fsConf.write, t.objs.IgFsslWrX, nil)
192
	if err != nil {
193
		return fmt.Errorf("attaching kretprobe: %w", err)
194
	}
195

196
	// open
197
	t.openEnterLink, err = link.Kprobe(fsConf.open, t.objs.IgFsslOpenE, nil)
198
	if err != nil {
199
		return fmt.Errorf("attaching kprobe: %w", err)
200
	}
201
	t.openExitLink, err = link.Kretprobe(fsConf.open, t.objs.IgFsslOpenX, nil)
202
	if err != nil {
203
		return fmt.Errorf("attaching kretprobe: %w", err)
204
	}
205

206
	// sync
207
	t.syncEnterLink, err = link.Kprobe(fsConf.fsync, t.objs.IgFsslSyncE, nil)
208
	if err != nil {
209
		return fmt.Errorf("attaching kprobe: %w", err)
210
	}
211
	t.syncExitLink, err = link.Kretprobe(fsConf.fsync, t.objs.IgFsslSyncX, nil)
212
	if err != nil {
213
		return fmt.Errorf("attaching kretprobe: %w", err)
214
	}
215

216
	// statfs
217
	t.statfsEnterLink, err = link.Kprobe(fsConf.statfs, t.objs.IgFsslStatfsE, nil)
218
	if err != nil {
219
		return fmt.Errorf("attaching kprobe: %w", err)
220
	}
221
	t.statfsExitLink, err = link.Kretprobe(fsConf.statfs, t.objs.IgFsslStatfsX, nil)
222
	if err != nil {
223
		return fmt.Errorf("attaching kretprobe: %w", err)
224
	}
225

226
	t.reader, err = perf.NewReader(t.objs.fsslowerMaps.Events, gadgets.PerfBufferPages*os.Getpagesize())
227
	if err != nil {
228
		return fmt.Errorf("creating perf ring buffer: %w", err)
229
	}
230
	return nil
231
}
232

233
var ops = []string{"R", "W", "O", "F", "S"}
234

235
func (t *Tracer) run() {
236
	for {
237
		record, err := t.reader.Read()
238
		if err != nil {
239
			if errors.Is(err, perf.ErrClosed) {
240
				// nothing to do, we're done
241
				return
242
			}
243

244
			msg := fmt.Sprintf("Error reading perf ring buffer: %s", err)
245
			t.eventCallback(types.Base(eventtypes.Err(msg)))
246
			return
247
		}
248

249
		if record.LostSamples > 0 {
250
			msg := fmt.Sprintf("lost %d samples", record.LostSamples)
251
			t.eventCallback(types.Base(eventtypes.Warn(msg)))
252
			continue
253
		}
254

255
		bpfEvent := (*fsslowerEvent)(unsafe.Pointer(&record.RawSample[0]))
256

257
		event := types.Event{
258
			Event: eventtypes.Event{
259
				Type:      eventtypes.NORMAL,
260
				Timestamp: gadgets.WallTimeFromBootTime(bpfEvent.Timestamp),
261
			},
262
			WithMountNsID: eventtypes.WithMountNsID{MountNsID: bpfEvent.MntnsId},
263
			Comm:          gadgets.FromCString(bpfEvent.Task[:]),
264
			Pid:           bpfEvent.Pid,
265
			Op:            ops[int(bpfEvent.Op)],
266
			Bytes:         bpfEvent.Size,
267
			Offset:        bpfEvent.Offset,
268
			Latency:       bpfEvent.DeltaUs,
269
			File:          gadgets.FromCString(bpfEvent.File[:]),
270
		}
271

272
		if t.enricher != nil {
273
			t.enricher.EnrichByMntNs(&event.CommonData, event.MountNsID)
274
		}
275

276
		t.eventCallback(&event)
277
	}
278
}
279

280
// --- Registry changes
281

282
func (t *Tracer) Run(gadgetCtx gadgets.GadgetContext) error {
283
	params := gadgetCtx.GadgetParams()
284
	t.config.Filesystem = params.Get(ParamFilesystem).AsString()
285
	t.config.MinLatency = params.Get(ParamMinLatency).AsUint()
286

287
	defer t.close()
288
	if err := t.install(); err != nil {
289
		return fmt.Errorf("installing tracer: %w", err)
290
	}
291

292
	go t.run()
293
	gadgetcontext.WaitForTimeoutOrDone(gadgetCtx)
294

295
	return nil
296
}
297

298
func (t *Tracer) SetMountNsMap(mountnsMap *ebpf.Map) {
299
	t.config.MountnsMap = mountnsMap
300
}
301

302
func (t *Tracer) SetEventHandler(handler any) {
303
	nh, ok := handler.(func(ev *types.Event))
304
	if !ok {
305
		panic("event handler invalid")
306
	}
307
	t.eventCallback = nh
308
}
309

310
func (g *GadgetDesc) NewInstance() (gadgets.Gadget, error) {
311
	tracer := &Tracer{
312
		config: &Config{},
313
	}
314
	return tracer, nil
315
}
316

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

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

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

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