go.undefinedlabs.com/scopeagent@v0.4.2/reflection/panic_handler.go (about)

     1  package reflection
     2  
     3  import (
     4  	"sync"
     5  	"unsafe"
     6  	_ "unsafe"
     7  
     8  	"github.com/undefinedlabs/go-mpatch"
     9  )
    10  
    11  var (
    12  	patchOnPanic    *mpatch.Patch
    13  	mOnPanic        sync.Mutex
    14  	onPanicHandlers []func(e interface{})
    15  
    16  	patchOnExit   *mpatch.Patch
    17  	mOnExit       sync.Mutex
    18  	onExitHandler []func(e interface{})
    19  )
    20  
    21  // Adds a global panic handler (this handler will be executed before any recover call)
    22  func AddPanicHandler(fn func(interface{})) {
    23  	mOnPanic.Lock()
    24  	defer mOnPanic.Unlock()
    25  	if patchOnPanic == nil {
    26  		np, err := mpatch.PatchMethod(lgopanic, gopanic)
    27  		if err == nil {
    28  			patchOnPanic = np
    29  		}
    30  	}
    31  	onPanicHandlers = append(onPanicHandlers, fn)
    32  }
    33  
    34  // Adds a global panic handler before process kill (this handler will be executed if not recover is set before the process exits)
    35  func AddOnPanicExitHandler(fn func(interface{})) {
    36  	mOnExit.Lock()
    37  	defer mOnExit.Unlock()
    38  	if patchOnExit == nil {
    39  		np, err := mpatch.PatchMethod(lpreprintpanics, preprintpanics)
    40  		if err == nil {
    41  			patchOnExit = np
    42  		}
    43  	}
    44  	onExitHandler = append(onExitHandler, fn)
    45  }
    46  
    47  func gopanic(e interface{}) {
    48  	mOnPanic.Lock()
    49  	defer mOnPanic.Unlock()
    50  	for _, fn := range onPanicHandlers {
    51  		fn(e)
    52  	}
    53  	patchOnPanic.Unpatch()
    54  	defer patchOnPanic.Patch()
    55  	lgopanic(e)
    56  }
    57  
    58  func preprintpanics(p *_panic) {
    59  	mOnExit.Lock()
    60  	defer mOnExit.Unlock()
    61  	for _, fn := range onExitHandler {
    62  		fn(p.arg)
    63  	}
    64  	patchOnExit.Unpatch()
    65  	defer patchOnExit.Patch()
    66  	lpreprintpanics(p)
    67  }
    68  
    69  //go:linkname lgopanic runtime.gopanic
    70  func lgopanic(e interface{})
    71  
    72  //go:linkname lpreprintpanics runtime.preprintpanics
    73  func lpreprintpanics(p *_panic)
    74  
    75  type _panic struct {
    76  	argp      unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
    77  	arg       interface{}    // argument to panic
    78  	link      *_panic        // link to earlier panic
    79  	recovered bool           // whether this panic is over
    80  	aborted   bool           // the panic was aborted
    81  }