github.com/cilium/ebpf@v0.10.0/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  var profSet unix.Sigset_t
    12  
    13  func init() {
    14  	if err := sigsetAdd(&profSet, unix.SIGPROF); err != nil {
    15  		panic(fmt.Errorf("creating signal set: %w", err))
    16  	}
    17  }
    18  
    19  // maskProfilerSignal locks the calling goroutine to its underlying OS thread
    20  // and adds SIGPROF to the thread's signal mask. This prevents pprof from
    21  // interrupting expensive syscalls like e.g. BPF_PROG_LOAD.
    22  //
    23  // The caller must defer sys.UnmaskProfilerSignal() to reverse the operation.
    24  func maskProfilerSignal() {
    25  	runtime.LockOSThread()
    26  
    27  	if err := unix.PthreadSigmask(unix.SIG_BLOCK, &profSet, nil); err != nil {
    28  		runtime.UnlockOSThread()
    29  		panic(fmt.Errorf("masking profiler signal: %w", err))
    30  	}
    31  }
    32  
    33  // unmaskProfilerSignal removes SIGPROF from the underlying thread's signal
    34  // mask, allowing it to be interrupted for profiling once again.
    35  //
    36  // It also unlocks the current goroutine from its underlying OS thread.
    37  func unmaskProfilerSignal() {
    38  	defer runtime.UnlockOSThread()
    39  
    40  	if err := unix.PthreadSigmask(unix.SIG_UNBLOCK, &profSet, nil); err != nil {
    41  		panic(fmt.Errorf("unmasking profiler signal: %w", err))
    42  	}
    43  }
    44  
    45  const (
    46  	wordBytes = int(unsafe.Sizeof(unix.Sigset_t{}.Val[0]))
    47  	wordBits  = wordBytes * 8
    48  
    49  	setBytes = int(unsafe.Sizeof(unix.Sigset_t{}))
    50  	setBits  = setBytes * 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  	if int(signal) > setBits {
    63  		return fmt.Errorf("signal %d does not fit within unix.Sigset_t", signal)
    64  	}
    65  
    66  	// For amd64, runtime.sigaddset() performs the following operation:
    67  	// set[(signal-1)/32] |= 1 << ((uint32(signal) - 1) & 31)
    68  	//
    69  	// This trick depends on sigset being two u32's, causing a signal in the the
    70  	// bottom 31 bits to be written to the low word if bit 32 is low, or the high
    71  	// word if bit 32 is high.
    72  
    73  	// Signal is the nth bit in the bitfield.
    74  	bit := int(signal - 1)
    75  	// Word within the sigset the bit needs to be written to.
    76  	word := bit / wordBits
    77  
    78  	// Write the signal bit into its corresponding word at the corrected offset.
    79  	set.Val[word] |= 1 << (bit % wordBits)
    80  
    81  	return nil
    82  }