signals.go 2.5 KB

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