github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadget-collection/gadgets/top/file/gadget.go (about) 1 // Copyright 2019-2021 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 filetop 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "strconv" 21 "strings" 22 "time" 23 24 log "github.com/sirupsen/logrus" 25 26 gadgetv1alpha1 "github.com/inspektor-gadget/inspektor-gadget/pkg/apis/gadget/v1alpha1" 27 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns/sort" 28 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-collection/gadgets" 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top" 30 filetoptracer "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/file/tracer" 31 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/file/types" 32 ) 33 34 type Trace struct { 35 helpers gadgets.GadgetHelpers 36 37 started bool 38 tracer *filetoptracer.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 cols := types.GetColumns() 53 validCols, _ := sort.FilterSortableColumns(cols.ColumnMap, cols.GetColumnNames()) 54 55 t := `filetop shows reads and writes by file, with container details. 56 57 The following parameters are supported: 58 - %s: Output interval, in seconds. (default %d) 59 - %s: Maximum rows to print. (default %d) 60 - %s: The field to sort the results by (%s). (default %s) 61 - %s: Show all files. (default %v, i.e. show regular files only)` 62 return fmt.Sprintf(t, top.IntervalParam, top.IntervalDefault, 63 top.MaxRowsParam, top.MaxRowsDefault, 64 top.SortByParam, strings.Join(validCols, ","), strings.Join(types.SortByDefault, ","), 65 types.AllFilesParam, types.AllFilesDefault) 66 } 67 68 func (f *TraceFactory) OutputModesSupported() map[gadgetv1alpha1.TraceOutputMode]struct{} { 69 return map[gadgetv1alpha1.TraceOutputMode]struct{}{ 70 gadgetv1alpha1.TraceOutputModeStream: {}, 71 } 72 } 73 74 func deleteTrace(name string, t interface{}) { 75 trace := t.(*Trace) 76 if trace.tracer != nil { 77 trace.tracer.Stop() 78 } 79 } 80 81 func (f *TraceFactory) Operations() map[gadgetv1alpha1.Operation]gadgets.TraceOperation { 82 n := func() interface{} { 83 return &Trace{ 84 helpers: f.Helpers, 85 } 86 } 87 88 return map[gadgetv1alpha1.Operation]gadgets.TraceOperation{ 89 gadgetv1alpha1.OperationStart: { 90 Doc: "Start filetop gadget", 91 Operation: func(name string, trace *gadgetv1alpha1.Trace) { 92 f.LookupOrCreate(name, n).(*Trace).Start(trace) 93 }, 94 }, 95 gadgetv1alpha1.OperationStop: { 96 Doc: "Stop filetop gadget", 97 Operation: func(name string, trace *gadgetv1alpha1.Trace) { 98 f.LookupOrCreate(name, n).(*Trace).Stop(trace) 99 }, 100 }, 101 } 102 } 103 104 func (t *Trace) Start(trace *gadgetv1alpha1.Trace) { 105 if t.started { 106 trace.Status.State = gadgetv1alpha1.TraceStateStarted 107 return 108 } 109 110 traceName := gadgets.TraceName(trace.ObjectMeta.Namespace, trace.ObjectMeta.Name) 111 112 maxRows := top.MaxRowsDefault 113 intervalSeconds := top.IntervalDefault 114 sortBy := types.SortByDefault 115 allFiles := types.AllFilesDefault 116 117 if trace.Spec.Parameters != nil { 118 params := trace.Spec.Parameters 119 var err error 120 121 if val, ok := params[top.MaxRowsParam]; ok { 122 maxRows, err = strconv.Atoi(val) 123 if err != nil { 124 trace.Status.OperationError = fmt.Sprintf("%q is not valid for %s: %v", val, top.MaxRowsParam, err) 125 return 126 } 127 } 128 129 if val, ok := params[top.IntervalParam]; ok { 130 intervalSeconds, err = strconv.Atoi(val) 131 if err != nil { 132 trace.Status.OperationError = fmt.Sprintf("%q is not valid for %s: %v", val, top.IntervalParam, err) 133 return 134 } 135 } 136 137 if val, ok := params[top.SortByParam]; ok { 138 sortByColumns := strings.Split(val, ",") 139 140 _, invalidCols := sort.FilterSortableColumns(types.GetColumns().ColumnMap, sortByColumns) 141 if len(invalidCols) > 0 { 142 trace.Status.OperationError = fmt.Sprintf("%q are not valid for %q", strings.Join(invalidCols, ","), top.SortByParam) 143 return 144 } 145 146 sortBy = sortByColumns 147 } 148 149 if val, ok := params[types.AllFilesParam]; ok { 150 allFiles, err = strconv.ParseBool(val) 151 if err != nil { 152 trace.Status.OperationError = fmt.Sprintf("%q is not valid for %s: %v", val, types.AllFilesParam, err) 153 return 154 } 155 } 156 } 157 158 mountNsMap, err := t.helpers.TracerMountNsMap(traceName) 159 if err != nil { 160 trace.Status.OperationError = fmt.Sprintf("failed to find tracer's mount ns map: %s", err) 161 return 162 } 163 164 config := &filetoptracer.Config{ 165 AllFiles: allFiles, 166 MaxRows: maxRows, 167 Interval: time.Second * time.Duration(intervalSeconds), 168 SortBy: sortBy, 169 MountnsMap: mountNsMap, 170 } 171 172 eventCallback := func(ev *top.Event[types.Stats]) { 173 r, err := json.Marshal(ev) 174 if err != nil { 175 log.Warnf("Gadget %s: Failed to marshall event: %s", trace.Spec.Gadget, err) 176 return 177 } 178 t.helpers.PublishEvent(traceName, string(r)) 179 } 180 181 tracer, err := filetoptracer.NewTracer(config, t.helpers, eventCallback) 182 if err != nil { 183 trace.Status.OperationError = fmt.Sprintf("failed to create tracer: %s", err) 184 return 185 } 186 187 t.tracer = tracer 188 t.started = true 189 190 trace.Status.State = gadgetv1alpha1.TraceStateStarted 191 } 192 193 func (t *Trace) Stop(trace *gadgetv1alpha1.Trace) { 194 if !t.started { 195 trace.Status.OperationError = "Not started" 196 return 197 } 198 199 t.tracer.Stop() 200 t.tracer = nil 201 t.started = false 202 203 trace.Status.State = gadgetv1alpha1.TraceStateStopped 204 }