github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadget-collection/gadgets/trace/fsslower/gadget.go (about) 1 // Copyright 2022 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 package fsslower 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "strconv" 21 "strings" 22 23 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-collection/gadgets" 24 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-collection/gadgets/trace" 25 26 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/fsslower/tracer" 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/trace/fsslower/types" 28 29 gadgetv1alpha1 "github.com/inspektor-gadget/inspektor-gadget/pkg/apis/gadget/v1alpha1" 30 ) 31 32 var validFilesystems = []string{"btrfs", "ext4", "nfs", "xfs"} 33 34 type Trace struct { 35 helpers gadgets.GadgetHelpers 36 37 started bool 38 tracer trace.Tracer 39 } 40 41 type TraceFactory struct { 42 gadgets.BaseFactory 43 } 44 45 func NewFactory() gadgets.TraceFactory { 46 return &TraceFactory{ 47 BaseFactory: gadgets.BaseFactory{DeleteTrace: deleteTrace}, 48 } 49 } 50 51 func (f *TraceFactory) Description() string { 52 t := `fsslower shows open, read, write and fsync operations slower than a threshold 53 54 The following parameters are supported: 55 - filesystem: Which filesystem to trace [%s] 56 - minlatency: Min latency to trace, in ms. (default %d)` 57 58 return fmt.Sprintf(t, strings.Join(validFilesystems, ", "), types.MinLatencyDefault) 59 } 60 61 func (f *TraceFactory) OutputModesSupported() map[gadgetv1alpha1.TraceOutputMode]struct{} { 62 return map[gadgetv1alpha1.TraceOutputMode]struct{}{ 63 gadgetv1alpha1.TraceOutputModeStream: {}, 64 } 65 } 66 67 func deleteTrace(name string, t interface{}) { 68 trace := t.(*Trace) 69 if trace.tracer != nil { 70 trace.tracer.Stop() 71 } 72 } 73 74 func (f *TraceFactory) Operations() map[gadgetv1alpha1.Operation]gadgets.TraceOperation { 75 n := func() interface{} { 76 return &Trace{ 77 helpers: f.Helpers, 78 } 79 } 80 81 return map[gadgetv1alpha1.Operation]gadgets.TraceOperation{ 82 gadgetv1alpha1.OperationStart: { 83 Doc: "Start fsslower gadget", 84 Operation: func(name string, trace *gadgetv1alpha1.Trace) { 85 f.LookupOrCreate(name, n).(*Trace).Start(trace) 86 }, 87 }, 88 gadgetv1alpha1.OperationStop: { 89 Doc: "Stop fsslower gadget", 90 Operation: func(name string, trace *gadgetv1alpha1.Trace) { 91 f.LookupOrCreate(name, n).(*Trace).Stop(trace) 92 }, 93 }, 94 } 95 } 96 97 func (t *Trace) Start(trace *gadgetv1alpha1.Trace) { 98 if t.started { 99 trace.Status.State = gadgetv1alpha1.TraceStateStarted 100 return 101 } 102 103 traceName := gadgets.TraceName(trace.ObjectMeta.Namespace, trace.ObjectMeta.Name) 104 105 eventCallback := func(event *types.Event) { 106 r, err := json.Marshal(event) 107 if err != nil { 108 fmt.Printf("error marshaling event: %s\n", err) 109 return 110 } 111 t.helpers.PublishEvent(traceName, string(r)) 112 } 113 114 var err error 115 116 if trace.Spec.Parameters == nil { 117 trace.Status.OperationError = "missing parameters" 118 return 119 } 120 121 params := trace.Spec.Parameters 122 123 filesystem, ok := params["filesystem"] 124 if !ok { 125 trace.Status.OperationError = "missing filesystem" 126 return 127 } 128 129 minLatency := types.MinLatencyDefault 130 131 val, ok := params["minlatency"] 132 if ok { 133 minLatencyParsed, err := strconv.ParseUint(val, 10, 32) 134 if err != nil { 135 trace.Status.OperationError = fmt.Sprintf("%q is not valid for minlatency", val) 136 return 137 } 138 minLatency = uint(minLatencyParsed) 139 } 140 141 mountNsMap, err := t.helpers.TracerMountNsMap(traceName) 142 if err != nil { 143 trace.Status.OperationError = fmt.Sprintf("failed to find tracer's mount ns map: %s", err) 144 return 145 } 146 147 config := &tracer.Config{ 148 MountnsMap: mountNsMap, 149 Filesystem: filesystem, 150 MinLatency: minLatency, 151 } 152 t.tracer, err = tracer.NewTracer(config, t.helpers, eventCallback) 153 if err != nil { 154 trace.Status.OperationError = fmt.Sprintf("failed to create tracer: %s", err) 155 return 156 } 157 158 t.started = true 159 160 trace.Status.State = gadgetv1alpha1.TraceStateStarted 161 } 162 163 func (t *Trace) Stop(trace *gadgetv1alpha1.Trace) { 164 if !t.started { 165 trace.Status.OperationError = "Not started" 166 return 167 } 168 169 t.tracer.Stop() 170 t.tracer = nil 171 t.started = false 172 173 trace.Status.State = gadgetv1alpha1.TraceStateStopped 174 }