pkg/kprobe/kprobe.go (199 lines of code) (raw):

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and //limitations under the License. package kprobe import ( "fmt" "io/ioutil" "os" "strconv" "strings" "syscall" "unsafe" constdef "github.com/aws/aws-ebpf-sdk-go/pkg/constants" "github.com/aws/aws-ebpf-sdk-go/pkg/logger" "golang.org/x/sys/unix" ) var log = logger.Get() type BpfKprobe interface { KprobeAttach() error KretprobeAttach() error KprobeDetach() error KretprobeDetach() error } var _ BpfKprobe = &bpfKprobe{} type bpfKprobe struct { progFD int eventName string funcName string perfFD int } func New(fd int, eName, fName string) BpfKprobe { return &bpfKprobe{ progFD: fd, eventName: eName, funcName: fName, } } func (k *bpfKprobe) SetPerfFD(perfFD int) { k.perfFD = perfFD } func (k *bpfKprobe) GetPerfFD() int { return k.perfFD } /* p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe -:[GRP/]EVENT */ func (k *bpfKprobe) KprobeAttach() error { if k.progFD <= 0 { log.Errorf("invalid BPF prog FD %d", k.progFD) return fmt.Errorf("Invalid BPF prog FD %d", k.progFD) } // if event is nil, we pick funcName if len(k.eventName) == 0 { k.eventName = k.funcName } // Register the Kprobe event file, err := os.OpenFile(constdef.KPROBE_SYS_EVENTS, os.O_WRONLY|os.O_APPEND, 0) if err != nil { log.Errorf("error opening kprobe_events file: %v", err) return err } defer file.Close() eventString := fmt.Sprintf("p:kprobes/%s %s", k.eventName, k.funcName) _, err = file.WriteString(eventString) if err != nil { log.Errorf("error writing to kprobe_events file: %v", err) return err } //Get the Kprobe ID kprobeIDpath := fmt.Sprintf("%s/%s/id", constdef.KPROBE_SYS_DEBUG, k.eventName) data, err := os.ReadFile(kprobeIDpath) if err != nil { log.Errorf("unable to read the kprobeID: %v", err) return err } id := strings.TrimSpace(string(data)) eventID, err := strconv.Atoi(id) if err != nil { log.Errorf("invalid ID during parsing: %s - %w", id, err) return err } log.Infof("Got eventID %d", eventID) attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_TRACEPOINT, Sample: 1, Wakeup: 1, Config: uint64(eventID), } attr.Size = uint32(unsafe.Sizeof(attr)) fd, err := unix.PerfEventOpen(&attr, -1, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { log.Errorf("failed to open perf event %v", err) return err } log.Infof("Attach bpf program to perf event Prog FD %d Event FD %d", k.progFD, fd) if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_SET_BPF)), uintptr(k.progFD)); err != 0 { log.Errorf("error attaching bpf program to perf event: %v", err) return err } if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_ENABLE)), 0); err != 0 { log.Errorf("error enabling perf event: %v", err) return err } k.SetPerfFD(fd) log.Infof("KPROBE Attach done!!! %d", fd) return nil } /* p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe -:[GRP/]EVENT MAXACTIVE : Maximum number of instances of the specified function that can be probed simultaneously, or 0 for the default value as defined in Documentation/kprobes.txt section 1.3.1. */ func (k *bpfKprobe) KretprobeAttach() error { if k.progFD <= 0 { log.Infof("invalid BPF prog FD %d", k.progFD) return fmt.Errorf("Invalid BPF prog FD %d", k.progFD) } // if event is nil, we pick funcName if len(k.eventName) == 0 { k.eventName = k.funcName } // Register the Kprobe event file, err := os.OpenFile(constdef.KPROBE_SYS_EVENTS, os.O_WRONLY|os.O_APPEND, 0) if err != nil { log.Errorf("error opening kprobe_events file: %v", err) return err } defer file.Close() eventString := fmt.Sprintf("r4096:kretprobes/%s %s", k.eventName, k.funcName) _, err = file.WriteString(eventString) if err != nil { log.Errorf("error writing to kprobe_events file: %v", err) return err } //Get the Kprobe ID kprobeIDpath := fmt.Sprintf("%s/%s/id", constdef.KRETPROBE_SYS_DEBUG, k.eventName) data, err := os.ReadFile(kprobeIDpath) if err != nil { log.Errorf("unable to read the kretprobeID: %v", err) return err } id := strings.TrimSpace(string(data)) eventID, err := strconv.Atoi(id) if err != nil { log.Errorf("invalid ID during parsing: %s - %w", id, err) return err } log.Infof("Got eventID %d", eventID) attr := unix.PerfEventAttr{ Type: unix.PERF_TYPE_TRACEPOINT, Sample: 1, Wakeup: 1, Config: uint64(eventID), } attr.Size = uint32(unsafe.Sizeof(attr)) fd, err := unix.PerfEventOpen(&attr, -1, 0, -1, unix.PERF_FLAG_FD_CLOEXEC) if err != nil { log.Errorf("failed to open perf event %v", err) return err } log.Infof("Attach bpf program to perf event Prog FD %d Event FD %d", k.progFD, fd) if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_SET_BPF)), uintptr(k.progFD)); err != 0 { log.Errorf("error attaching bpf program to perf event: %v", err) return err } if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_ENABLE)), 0); err != 0 { log.Errorf("error enabling perf event: %v", err) return err } k.SetPerfFD(fd) log.Infof("KRETPROBE Attach done!!! %d", fd) return nil } func probeDetach(eventName string, perfFD int, kretProbe bool) error { log.Infof("Calling Detach on %s", eventName) if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(perfFD)), uintptr(uint(unix.PERF_EVENT_IOC_DISABLE)), 0); err != 0 { log.Errorf("error enabling perf event: %v", err) return err } unix.Close(perfFD) eventEnable := constdef.KPROBE_SYS_DEBUG + "/" + eventName + "/enable" if kretProbe { eventEnable = constdef.KRETPROBE_SYS_DEBUG + "/" + eventName + "/enable" } setEnable := []byte("0") err := ioutil.WriteFile(eventEnable, setEnable, os.ModePerm) if err != nil { return err } file, err := os.OpenFile(constdef.KPROBE_SYS_EVENTS, os.O_APPEND|os.O_WRONLY, 0) if err != nil { log.Errorf("cannot open probe events: %v", err) return err } defer file.Close() eventString := fmt.Sprintf("-:%s\n", eventName) if _, err = file.WriteString(eventString); err != nil { pathErr, ok := err.(*os.PathError) if ok && pathErr.Err == syscall.ENOENT { log.Infof("File is already cleanedup, maybe some other process?") return nil } log.Errorf("cannot update the probe events %v", err) return err } log.Infof("probe Detach done!!!") return nil } func (k *bpfKprobe) KprobeDetach() error { log.Infof("Calling Kprobe Detach on %s", k.eventName) return probeDetach(k.eventName, k.perfFD, false) } func (k *bpfKprobe) KretprobeDetach() error { log.Infof("Calling Kretprobe Detach on %s", k.eventName) return probeDetach(k.eventName, k.perfFD, true) }