github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/gadget-collection/gadgets/top/ebpf/gadget.go (about) 1 // Copyright 2019-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 ebpf 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/bpfstats" 28 "github.com/inspektor-gadget/inspektor-gadget/pkg/columns/sort" 29 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-collection/gadgets" 30 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top" 31 ebpftoptracer "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/ebpf/tracer" 32 "github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets/top/ebpf/types" 33 ) 34 35 type Trace struct { 36 helpers gadgets.GadgetHelpers 37 38 traceName string 39 node string 40 started bool 41 42 tracer *ebpftoptracer.Tracer 43 } 44 45 type TraceFactory struct { 46 gadgets.BaseFactory 47 } 48 49 func NewFactory() gadgets.TraceFactory { 50 return &TraceFactory{ 51 BaseFactory: gadgets.BaseFactory{DeleteTrace: deleteTrace}, 52 } 53 } 54 55 func (f *TraceFactory) Description() string { 56 cols := types.GetColumns() 57 validCols, _ := sort.FilterSortableColumns(cols.ColumnMap, cols.GetColumnNames()) 58 59 t := `ebpftop shows cpu time used by ebpf programs. 60 61 The following parameters are supported: 62 - %s: Output interval, in seconds. (default %d) 63 - %s: Maximum rows to print. (default %d) 64 - %s: The field to sort the results by (%s). (default %s)` 65 return fmt.Sprintf(t, top.IntervalParam, top.IntervalDefault, 66 top.MaxRowsParam, top.MaxRowsDefault, 67 top.SortByParam, strings.Join(validCols, ","), strings.Join(types.SortByDefault, ",")) 68 } 69 70 func (f *TraceFactory) OutputModesSupported() map[gadgetv1alpha1.TraceOutputMode]struct{} { 71 return map[gadgetv1alpha1.TraceOutputMode]struct{}{ 72 gadgetv1alpha1.TraceOutputModeStream: {}, 73 } 74 } 75 76 func deleteTrace(name string, t interface{}) { 77 trace := t.(*Trace) 78 if trace.started { 79 trace.stop() 80 } 81 } 82 83 func (f *TraceFactory) Operations() map[gadgetv1alpha1.Operation]gadgets.TraceOperation { 84 n := func() interface{} { 85 return &Trace{ 86 helpers: f.Helpers, 87 } 88 } 89 90 return map[gadgetv1alpha1.Operation]gadgets.TraceOperation{ 91 gadgetv1alpha1.OperationStart: { 92 Doc: "Start ebpftop gadget", 93 Operation: func(name string, trace *gadgetv1alpha1.Trace) { 94 f.LookupOrCreate(name, n).(*Trace).Start(trace) 95 }, 96 }, 97 gadgetv1alpha1.OperationStop: { 98 Doc: "Stop ebpftop gadget", 99 Operation: func(name string, trace *gadgetv1alpha1.Trace) { 100 f.LookupOrCreate(name, n).(*Trace).Stop(trace) 101 }, 102 }, 103 } 104 } 105 106 func (t *Trace) Start(trace *gadgetv1alpha1.Trace) { 107 if t.started { 108 trace.Status.State = gadgetv1alpha1.TraceStateStarted 109 return 110 } 111 112 t.traceName = gadgets.TraceName(trace.ObjectMeta.Namespace, trace.ObjectMeta.Name) 113 t.node = trace.Spec.Node 114 115 maxRows := top.MaxRowsDefault 116 intervalSeconds := top.IntervalDefault 117 sortBy := types.SortByDefault 118 119 if trace.Spec.Parameters != nil { 120 params := trace.Spec.Parameters 121 var err error 122 123 if val, ok := params[top.MaxRowsParam]; ok { 124 maxRows, err = strconv.Atoi(val) 125 if err != nil { 126 trace.Status.OperationError = fmt.Sprintf("%q is not valid for %s: %v", val, top.MaxRowsParam, err) 127 return 128 } 129 } 130 131 if val, ok := params[top.IntervalParam]; ok { 132 intervalSeconds, err = strconv.Atoi(val) 133 if err != nil { 134 trace.Status.OperationError = fmt.Sprintf("%q is not valid for %s: %v", val, top.IntervalParam, err) 135 return 136 } 137 } 138 139 if val, ok := params[top.SortByParam]; ok { 140 sortByColumns := strings.Split(val, ",") 141 142 _, invalidCols := sort.FilterSortableColumns(types.GetColumns().ColumnMap, sortByColumns) 143 if len(invalidCols) > 0 { 144 trace.Status.OperationError = fmt.Sprintf("%q are not valid for %q", strings.Join(invalidCols, ","), top.SortByParam) 145 return 146 } 147 148 sortBy = sortByColumns 149 } 150 } 151 152 config := &ebpftoptracer.Config{ 153 MaxRows: maxRows, 154 Interval: time.Second * time.Duration(intervalSeconds), 155 SortBy: sortBy, 156 } 157 158 eventCallback := func(ev *top.Event[types.Stats]) { 159 r, err := json.Marshal(ev) 160 if err != nil { 161 log.Warnf("Gadget %s: Failed to marshal event: %s", trace.Spec.Gadget, err) 162 return 163 } 164 t.helpers.PublishEvent(t.traceName, string(r)) 165 } 166 167 tracer, err := ebpftoptracer.NewTracer(config, t.helpers, eventCallback) 168 if err != nil { 169 trace.Status.OperationError = fmt.Sprintf("failed to create tracer: %s", err) 170 return 171 } 172 173 t.tracer = tracer 174 t.started = true 175 176 trace.Status.State = gadgetv1alpha1.TraceStateStarted 177 } 178 179 func (t *Trace) Stop(trace *gadgetv1alpha1.Trace) { 180 if !t.started { 181 trace.Status.OperationError = "Not started" 182 return 183 } 184 185 err := t.stop() 186 if err != nil { 187 trace.Status.OperationWarning = err.Error() 188 } 189 190 trace.Status.State = gadgetv1alpha1.TraceStateStopped 191 } 192 193 func (t *Trace) stop() error { 194 t.tracer.Stop() 195 t.tracer = nil 196 t.started = false 197 198 return bpfstats.DisableBPFStats() 199 }