func (pe *perfEvent) attach(prog *ebpf.Program) error {
	if prog == nil {
		return errors.New("cannot attach a nil program")
	}
	if pe.fd == nil {
		return errors.New("cannot attach to nil perf event")
	}
	if prog.FD() < 0 {
		return fmt.Errorf("invalid program: %w", internal.ErrClosedFd)
	}
	switch pe.typ {
	case kprobeEvent, kretprobeEvent, uprobeEvent, uretprobeEvent:
		if t := prog.Type(); t != ebpf.Kprobe {
			return fmt.Errorf("invalid program type (expected %s): %s", ebpf.Kprobe, t)
		}
	case tracepointEvent:
		if t := prog.Type(); t != ebpf.TracePoint {
			return fmt.Errorf("invalid program type (expected %s): %s", ebpf.TracePoint, t)
		}
	default:
		return fmt.Errorf("unknown perf event type: %d", pe.typ)
	}
	kfd, _ := pe.fd.Value()
	err := unix.IoctlSetInt(int(kfd), unix.PERF_EVENT_IOC_SET_BPF, prog.FD())
	if err != nil {
		return fmt.Errorf("setting perf event bpf program: %w", err)
	}
	if err := unix.IoctlSetInt(int(kfd), unix.PERF_EVENT_IOC_ENABLE, 0); err != nil {
		return fmt.Errorf("enable perf event: %s", err)
	}
	runtime.SetFinalizer(pe, (*perfEvent).Close)
	return nil
}
attach the given eBPF prog to the perf event stored in pe.
pe must contain a valid perf event fd.
prog's type must match the program type stored in pe.
package link
import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"unsafe"
	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/internal"
	"github.com/cilium/ebpf/internal/unix"
)
var (
	tracefsPath = "/sys/kernel/debug/tracing"
	rgxTraceEvent = regexp.MustCompile("^[a-zA-Z_][0-9a-zA-Z_]*$")
	errInvalidInput = errors.New("invalid input")
)
const (
	perfAllThreads = -1
)
type perfEventType uint8
const (
	tracepointEvent perfEventType = iota
	kprobeEvent
	kretprobeEvent
	uprobeEvent
	uretprobeEvent
)
type perfEvent struct {
	group string
	name  string
	pmuID uint64
	tracefsID uint64
	typ perfEventType
	fd *internal.FD
}
func (pe *perfEvent) isLink() {}
func (pe *perfEvent) Pin(string) error {
	return fmt.Errorf("pin perf event: %w", ErrNotSupported)
}
func (pe *perfEvent) Unpin() error {
	return fmt.Errorf("unpin perf event: %w", ErrNotSupported)
}
func (pe *perfEvent) Update(prog *ebpf.Program) error {
	return fmt.Errorf("can't replace eBPF program in perf event: %w", ErrNotSupported)
}
func (pe *perfEvent) Close() error {
	if pe.fd == nil {
		return nil
	}
	pfd, err := pe.fd.Value()
	if err != nil {
		return fmt.Errorf("getting perf event fd: %w", err)
	}
	err = unix.IoctlSetInt(int(pfd), unix.PERF_EVENT_IOC_DISABLE, 0)
	if err != nil {
		return fmt.Errorf("disabling perf event: %w", err)
	}
	err = pe.fd.Close()
	if err != nil {
		return fmt.Errorf("closing perf event fd: %w", err)
	}
	switch pe.typ {
	case kprobeEvent, kretprobeEvent:
		if pe.tracefsID != 0 {
			return closeTraceFSProbeEvent(kprobeType, pe.group, pe.name)
		}
	case uprobeEvent, uretprobeEvent:
		if pe.tracefsID != 0 {
			return closeTraceFSProbeEvent(uprobeType, pe.group, pe.name)
		}
	case tracepointEvent:
		return nil
	}
	return nil
}