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 }