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  }