github.com/jayanthvn/pure-gobpf@v0.0.0-20230623131354-8d1d959d9e0b/pkg/ebpf_kprobe/kprobe.go (about)

     1  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
     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_kprobe
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"strconv"
    21  	"strings"
    22  	"syscall"
    23  	"unsafe"
    24  
    25  	"github.com/jayanthvn/pure-gobpf/pkg/logger"
    26  	"golang.org/x/sys/unix"
    27  )
    28  
    29  const (
    30  	kprobeSysEventsFile = "/sys/kernel/debug/tracing/kprobe_events"
    31  )
    32  
    33  var log = logger.Get()
    34  
    35  /*
    36   p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]  : Set a probe
    37   r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]  : Set a return probe
    38   -:[GRP/]EVENT
    39  */
    40  // if event is nil, we pick funcName
    41  func KprobeAttach(progFD int, eventName string, funcName string) error {
    42  
    43  	//var log = logger.Get()
    44  
    45  	if progFD <= 0 {
    46  		log.Infof("Invalid BPF prog FD %d", progFD)
    47  		return fmt.Errorf("Invalid BPF prog FD %d", progFD)
    48  
    49  	}
    50  
    51  	if len(eventName) == 0 {
    52  		eventName = funcName
    53  	}
    54  
    55  	// Register the Kprobe event
    56  	file, err := os.OpenFile(kprobeSysEventsFile, os.O_WRONLY|os.O_APPEND, 0)
    57  	if err != nil {
    58  		log.Infof("error opening kprobe_events file: %v", err)
    59  		return fmt.Errorf("error opening kprobe_events file: %v", err)
    60  	}
    61  	//defer file.Close()
    62  
    63  	eventString := fmt.Sprintf("p:kprobes/%s %s", eventName, funcName)
    64  	_, err = file.WriteString(eventString)
    65  	if err != nil {
    66  		log.Infof("error writing to kprobe_events file: %v", err)
    67  		return fmt.Errorf("error writing to kprobe_events file: %v", err)
    68  	}
    69  
    70  	//Get the Kprobe ID
    71  	kprobeIDpath := fmt.Sprintf("/sys/kernel/debug/tracing/events/kprobes/%s/id", eventName)
    72  	data, err := os.ReadFile(kprobeIDpath)
    73  	if err != nil {
    74  		log.Infof("Unable to read the kprobeID: %v", err)
    75  		return fmt.Errorf("Unable to read the kprobeID: %v", err)
    76  	}
    77  	id := strings.TrimSpace(string(data))
    78  	eventID, err := strconv.Atoi(id)
    79  	if err != nil {
    80  		log.Infof("Invalid ID during parsing: %s - %v", id, err)
    81  		return fmt.Errorf("Invalid ID during parsing: %s - %w", id, err)
    82  	}
    83  
    84  	log.Infof("Got eventID %d", eventID)
    85  
    86  	attr := unix.PerfEventAttr{
    87  		Type:   unix.PERF_TYPE_TRACEPOINT,
    88  		Sample: 1,
    89  		Wakeup: 1,
    90  		Config: uint64(eventID),
    91  	}
    92  	attr.Size = uint32(unsafe.Sizeof(attr))
    93  
    94  	fd, err := unix.PerfEventOpen(&attr, -1, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
    95  	if err != nil {
    96  		log.Infof("Failed to open perf event %v", err)
    97  		return fmt.Errorf("Failed to open perf event %v", err)
    98  	}
    99  	//defer unix.Close(fd)
   100  
   101  	log.Infof("Attach bpf program to perf event Prog FD %d Event FD %d", progFD, fd)
   102  
   103  	if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_SET_BPF)), uintptr(progFD)); err != 0 {
   104  		log.Infof("error attaching bpf program to perf event: %v", err)
   105  		return fmt.Errorf("error attaching bpf program to perf event: %v", err)
   106  	}
   107  
   108  	if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_ENABLE)), 0); err != 0 {
   109  		log.Infof("error enabling perf event: %v", err)
   110  		return fmt.Errorf("error enabling perf event: %v", err)
   111  	}
   112  
   113  	log.Infof("Attach done!!! %d", fd)
   114  	return nil
   115  
   116  }
   117  
   118  /*
   119   p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]  : Set a probe
   120   r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]  : Set a return probe
   121   -:[GRP/]EVENT
   122  
   123  MAXACTIVE      : Maximum number of instances of the specified function that
   124                   can be probed simultaneously, or 0 for the default value
   125                   as defined in Documentation/kprobes.txt section 1.3.1.
   126  
   127  */
   128  
   129  // if event is nil, we pick funcName
   130  func KretprobeAttach(progFD int, eventName string, funcName string) error {
   131  
   132  	//var log = logger.Get()
   133  
   134  	if progFD <= 0 {
   135  		log.Infof("Invalid BPF prog FD %d", progFD)
   136  		return fmt.Errorf("Invalid BPF prog FD %d", progFD)
   137  
   138  	}
   139  
   140  	if len(eventName) == 0 {
   141  		eventName = funcName
   142  	}
   143  
   144  	// Register the Kprobe event
   145  	file, err := os.OpenFile(kprobeSysEventsFile, os.O_WRONLY|os.O_APPEND, 0)
   146  	if err != nil {
   147  		log.Infof("error opening kprobe_events file: %v", err)
   148  		return fmt.Errorf("error opening kprobe_events file: %v", err)
   149  	}
   150  	//defer file.Close()
   151  
   152  	eventString := fmt.Sprintf("r4096:kretprobes/%s %s", eventName, funcName)
   153  	_, err = file.WriteString(eventString)
   154  	if err != nil {
   155  		log.Infof("error writing to kprobe_events file: %v", err)
   156  		return fmt.Errorf("error writing to kprobe_events file: %v", err)
   157  	}
   158  
   159  	//Get the Kprobe ID
   160  	kprobeIDpath := fmt.Sprintf("/sys/kernel/debug/tracing/events/kretprobes/%s/id", eventName)
   161  	data, err := os.ReadFile(kprobeIDpath)
   162  	if err != nil {
   163  		log.Infof("Unable to read the kretprobeID: %v", err)
   164  		return fmt.Errorf("Unable to read the kretprobeID: %v", err)
   165  	}
   166  	id := strings.TrimSpace(string(data))
   167  	eventID, err := strconv.Atoi(id)
   168  	if err != nil {
   169  		log.Infof("Invalid ID during parsing: %s - %v", id, err)
   170  		return fmt.Errorf("Invalid ID during parsing: %s - %w", id, err)
   171  	}
   172  
   173  	log.Infof("Got eventID %d", eventID)
   174  
   175  	attr := unix.PerfEventAttr{
   176  		Type:   unix.PERF_TYPE_TRACEPOINT,
   177  		Sample: 1,
   178  		Wakeup: 1,
   179  		Config: uint64(eventID),
   180  	}
   181  	attr.Size = uint32(unsafe.Sizeof(attr))
   182  
   183  	fd, err := unix.PerfEventOpen(&attr, -1, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
   184  	if err != nil {
   185  		log.Infof("Failed to open perf event %v", err)
   186  		return fmt.Errorf("Failed to open perf event %v", err)
   187  	}
   188  	//defer unix.Close(fd)
   189  
   190  	log.Infof("Attach bpf program to perf event Prog FD %d Event FD %d", progFD, fd)
   191  
   192  	if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_SET_BPF)), uintptr(progFD)); err != 0 {
   193  		log.Infof("error attaching bpf program to perf event: %v", err)
   194  		return fmt.Errorf("error attaching bpf program to perf event: %v", err)
   195  	}
   196  
   197  	if _, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(int(fd)), uintptr(uint(unix.PERF_EVENT_IOC_ENABLE)), 0); err != 0 {
   198  		log.Infof("error enabling perf event: %v", err)
   199  		return fmt.Errorf("error enabling perf event: %v", err)
   200  	}
   201  
   202  	log.Infof("Attach done!!! %d", fd)
   203  	return nil
   204  
   205  }
   206  
   207  func KprobeDetach(eventName string) error {
   208  	//var log = logger.Get()
   209  	log.Infof("Calling Detach on %s", eventName)
   210  	file, err := os.OpenFile(kprobeSysEventsFile, os.O_APPEND|os.O_WRONLY, 0)
   211  	if err != nil {
   212  		log.Infof("Cannot open file to detach")
   213  		return fmt.Errorf("cannot open kprobe events: %v", err)
   214  	}
   215  	defer file.Close()
   216  
   217  	eventString := fmt.Sprintf("-:%s\n", eventName)
   218  	if _, err = file.WriteString(eventString); err != nil {
   219  		pathErr, ok := err.(*os.PathError)
   220  		if ok && pathErr.Err == syscall.ENOENT {
   221  			log.Infof("File is already cleanedup, maybe some other process?")
   222  			return nil
   223  		}
   224  		log.Infof("Cannot update the kprobe events %v", err)
   225  		return fmt.Errorf("cannot update the kprobe_events: %v", err)
   226  	}
   227  	log.Infof("Detach done!!!")
   228  	return nil
   229  }