github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/perf/debugfs.go (about) 1 package perf 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "os" 8 "path" 9 "strconv" 10 "strings" 11 ) 12 13 // This file contains debugfs (/sys/kernel/debug) related code. 14 15 const ( 16 debugfs = "/sys/kernel/debug" 17 eventsPath = "tracing/events" 18 ) 19 20 // getTracepointID returns the ID of a tracepoint. 21 // If the function returns permission errors the program is not being run a user with the correct permissions. 22 // If the function returns os.ErrNotExist the given tracepoint doesn't exist 23 func getTracepointID(category, name string) (int, error) { 24 file, err := os.OpenFile(path.Join(debugfs, eventsPath, category, name, "id"), os.O_RDONLY, 0) 25 if err != nil { 26 return -1, fmt.Errorf("open file: %w", err) 27 } 28 29 contents, err := io.ReadAll(file) 30 if err != nil { 31 return -1, fmt.Errorf("read file: %w", err) 32 } 33 34 return strconv.Atoi(strings.TrimSpace(string(contents))) 35 } 36 37 type ProbeType int 38 39 const ( 40 // TypeUnknown is the default value for the KProbeType 41 TypeUnknown ProbeType = iota 42 // TypeKProbe means a kprobe triggers on the beginning of a function/symbol 43 TypeKProbe 44 // TypeKRetprobe means a krprobe trigger on the end/return of a function/symbol 45 TypeKRetprobe 46 // TypeUProbe means a uprobe triggers on the beginning of a function/symbol 47 TypeUProbe 48 // TypeURetProbe means a uprobe trigger on the end/return of a function/symbol 49 TypeURetProbe 50 ) 51 52 type KProbeOpts struct { 53 Type ProbeType 54 // Group name. If omitted, use "kprobes" for it. 55 Group string 56 // Event name. If omitted, the event name is generated 57 // based on SYM+offs or MEMADDR. 58 Event string 59 60 // Module name which has given Symbol. 61 Module string 62 // Symbol+Offset where the probe is inserted. 63 Symbol string 64 } 65 66 func (opts KProbeOpts) kprobeConfig() string { 67 probeConfig := "p" 68 if opts.Type == TypeKRetprobe { 69 probeConfig = "r" 70 } 71 72 if opts.Event != "" { 73 probeConfig = probeConfig + ":" 74 if opts.Group != "" { 75 probeConfig = opts.Group + "/" 76 } 77 probeConfig = probeConfig + opts.Event + " " 78 } 79 80 if opts.Module != "" { 81 probeConfig += opts.Module + ":" 82 } 83 probeConfig += opts.Symbol 84 85 return probeConfig 86 } 87 88 type KProbe struct { 89 Name string 90 ID int 91 } 92 93 func (kp KProbe) Clear() error { 94 probeEvents, err := openKProbeEvents() 95 if err != nil { 96 return fmt.Errorf("open kprobe_events: %w", err) 97 } 98 defer probeEvents.Close() 99 100 _, err = probeEvents.Write([]byte("-:" + kp.Name)) 101 if err != nil { 102 return fmt.Errorf("write kprobe_events: %w", err) 103 } 104 105 return nil 106 } 107 108 func openKProbeEvents() (*os.File, error) { 109 return os.OpenFile(path.Join(debugfs, "tracing/kprobe_events"), os.O_WRONLY|os.O_APPEND, 0) 110 } 111 112 // newKProbe creates a new kprobe event, if successful the ID of the new kprobe is returned 113 func newKProbe(opts KProbeOpts) (*KProbe, error) { 114 if opts.Event == "" || opts.Symbol == "" { 115 return nil, errors.New("'Event' and 'Symbol' options are required") 116 } 117 118 probeEvents, err := openKProbeEvents() 119 if err != nil { 120 return nil, fmt.Errorf("open kprobe_events: %w", err) 121 } 122 defer probeEvents.Close() 123 124 _, err = probeEvents.Write([]byte(opts.kprobeConfig())) 125 if err != nil { 126 // TODO handle event naming conflicts 127 return nil, fmt.Errorf("write kprobe_events: %w", err) 128 } 129 130 idBytes, err := os.ReadFile(path.Join(debugfs, eventsPath, "kprobes", opts.Event, "id")) 131 if err != nil { 132 return nil, fmt.Errorf("unable to find created kprobe: %w", err) 133 } 134 135 id, err := strconv.Atoi(strings.TrimSpace(string(idBytes))) 136 if err != nil { 137 return nil, fmt.Errorf("atoi: %w", err) 138 } 139 140 return &KProbe{ 141 Name: opts.Event, 142 ID: id, 143 }, nil 144 } 145 146 type UProbeOpts struct { 147 Type ProbeType 148 // Group name. If omitted, use "kprobes" for it. 149 Group string 150 // Event name. If omitted, the event name is generated 151 // based on SYM+offs or MEMADDR. 152 Event string 153 154 // Path is the path to the executable to be probed. 155 Path string 156 // Offset of the address to be be probed. 157 Offset int 158 } 159 160 func (opts UProbeOpts) uprobeConfig() string { 161 probeConfig := "p" 162 if opts.Type == TypeURetProbe { 163 probeConfig = "r" 164 } 165 166 if opts.Event != "" { 167 probeConfig = probeConfig + ":" 168 if opts.Group != "" { 169 probeConfig = opts.Group + "/" 170 } 171 probeConfig = probeConfig + opts.Event + " " 172 } 173 174 probeConfig += fmt.Sprintf("%s:0x%x", opts.Path, opts.Offset) 175 176 return probeConfig 177 } 178 179 type UProbe struct { 180 Name string 181 ID int 182 } 183 184 func (kp UProbe) Clear() error { 185 probeEvents, err := openUProbeEvents() 186 if err != nil { 187 return fmt.Errorf("open uprobe_events: %w", err) 188 } 189 defer probeEvents.Close() 190 191 _, err = probeEvents.Write([]byte("-:" + kp.Name)) 192 if err != nil { 193 return fmt.Errorf("write uprobe_events: %w", err) 194 } 195 196 return nil 197 } 198 199 func openUProbeEvents() (*os.File, error) { 200 return os.OpenFile(path.Join(debugfs, "tracing/uprobe_events"), os.O_WRONLY|os.O_APPEND, 0) 201 } 202 203 // newUProbe creates a new uprobe event, if successful the ID of the new uprobe is returned 204 func newUProbe(opts UProbeOpts) (*UProbe, error) { 205 if opts.Event == "" || opts.Path == "" { 206 return nil, errors.New("'Event' and 'Path' options are required") 207 } 208 209 probeEvents, err := openUProbeEvents() 210 if err != nil { 211 return nil, fmt.Errorf("open uprobe_events: %w", err) 212 } 213 defer probeEvents.Close() 214 215 _, err = probeEvents.Write([]byte(opts.uprobeConfig())) 216 if err != nil { 217 // TODO handle event naming conflicts 218 return nil, fmt.Errorf("write uprobe_events: %w", err) 219 } 220 221 idBytes, err := os.ReadFile(path.Join(debugfs, eventsPath, "uprobes", opts.Event, "id")) 222 if err != nil { 223 return nil, fmt.Errorf("unable to find created uprobe: %w", err) 224 } 225 226 id, err := strconv.Atoi(strings.TrimSpace(string(idBytes))) 227 if err != nil { 228 return nil, fmt.Errorf("atoi: %w", err) 229 } 230 231 return &UProbe{ 232 Name: opts.Event, 233 ID: id, 234 }, nil 235 }