github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/internal/sys/signals.go (about)

     1  package sys
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"unsafe"
     7  
     8  	"github.com/cilium/ebpf/internal/unix"
     9  )
    10  
    11  // A sigset containing only SIGPROF.
    12  var profSet unix.Sigset_t
    13  
    14  func init() {
    15  	// See sigsetAdd for details on the implementation. Open coded here so
    16  	// that the compiler will check the constant calculations for us.
    17  	profSet.Val[sigprofBit/wordBits] |= 1 << (sigprofBit % wordBits)
    18  }
    19  
    20  // maskProfilerSignal locks the calling goroutine to its underlying OS thread
    21  // and adds SIGPROF to the thread's signal mask. This prevents pprof from
    22  // interrupting expensive syscalls like e.g. BPF_PROG_LOAD.
    23  //
    24  // The caller must defer unmaskProfilerSignal() to reverse the operation.
    25  func maskProfilerSignal() {
    26  	runtime.LockOSThread()
    27  
    28  	if err := unix.PthreadSigmask(unix.SIG_BLOCK, &profSet, nil); err != nil {
    29  		runtime.UnlockOSThread()
    30  		panic(fmt.Errorf("masking profiler signal: %w", err))
    31  	}
    32  }
    33  
    34  // unmaskProfilerSignal removes SIGPROF from the underlying thread's signal
    35  // mask, allowing it to be interrupted for profiling once again.
    36  //
    37  // It also unlocks the current goroutine from its underlying OS thread.
    38  func unmaskProfilerSignal() {
    39  	defer runtime.UnlockOSThread()
    40  
    41  	if err := unix.PthreadSigmask(unix.SIG_UNBLOCK, &profSet, nil); err != nil {
    42  		panic(fmt.Errorf("unmasking profiler signal: %w", err))
    43  	}
    44  }
    45  
    46  const (
    47  	// Signal is the nth bit in the bitfield.
    48  	sigprofBit = int(unix.SIGPROF - 1)
    49  	// The number of bits in one Sigset_t word.
    50  	wordBits = int(unsafe.Sizeof(unix.Sigset_t{}.Val[0])) * 8
    51  )
    52  
    53  // sigsetAdd adds signal to set.
    54  //
    55  // Note: Sigset_t.Val's value type is uint32 or uint64 depending on the arch.
    56  // This function must be able to deal with both and so must avoid any direct
    57  // references to u32 or u64 types.
    58  func sigsetAdd(set *unix.Sigset_t, signal unix.Signal) error {
    59  	if signal < 1 {
    60  		return fmt.Errorf("signal %d must be larger than 0", signal)
    61  	}
    62  
    63  	// For amd64, runtime.sigaddset() performs the following operation:
    64  	// set[(signal-1)/32] |= 1 << ((uint32(signal) - 1) & 31)
    65  	//
    66  	// This trick depends on sigset being two u32's, causing a signal in the
    67  	// bottom 31 bits to be written to the low word if bit 32 is low, or the high
    68  	// word if bit 32 is high.
    69  
    70  	// Signal is the nth bit in the bitfield.
    71  	bit := int(signal - 1)
    72  	// Word within the sigset the bit needs to be written to.
    73  	word := bit / wordBits
    74  
    75  	if word >= len(set.Val) {
    76  		return fmt.Errorf("signal %d does not fit within unix.Sigset_t", signal)
    77  	}
    78  
    79  	// Write the signal bit into its corresponding word at the corrected offset.
    80  	set.Val[word] |= 1 << (bit % wordBits)
    81  
    82  	return nil
    83  }