github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/throw/panicwrap.go (about) 1 // Copyright 2020 Insolar Network Ltd. 2 // All rights reserved. 3 // This material is licensed under the Insolar License version 1.0, 4 // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md. 5 6 package throw 7 8 import ( 9 "errors" 10 "math" 11 ) 12 13 const NoStackTrace int = math.MaxInt32 14 15 // PanicHolder is a marker interface for a recovered panic 16 type PanicHolder interface { 17 StackTraceHolder 18 Recovered() interface{} 19 } 20 21 const recoverSkipFrames = 2 // defer() + runtime.panic() 22 23 // WrapPanic returns an error for the given recovered panic with a stack trace. 24 // The error is also marked as recovered panic. Will return nil for a nil value 25 // NB! Should be used inside a defer. 26 func WrapPanic(recovered interface{}) error { 27 return WrapPanicExt(recovered, recoverSkipFrames+1) // WrapPanic() + defer 28 } 29 30 // WrapPanicNoStack returns an error for the given recovered panic without a stack trace. 31 // The error is also marked as recovered panic. Will return nil for a nil value 32 // NB! Should be used inside a defer. 33 func WrapPanicNoStack(recovered interface{}) error { 34 return WrapPanicExt(recovered, NoStackTrace) 35 } 36 37 // WrapPanicNoStack returns an error for the given recovered panic with or without a stack trace. 38 // When (skipFrames) = NoStackTrace then no stack trace is included. 39 // The error is also marked as recovered panic. Will return nil for a nil value 40 func WrapPanicExt(recovered interface{}, skipFrames int) error { 41 if recovered == nil { 42 return nil 43 } 44 45 var st StackTrace 46 if skipFrames < NoStackTrace { 47 st = CaptureStack(skipFrames + 1) 48 } 49 50 var stDeepest StackTrace 51 var stDeepMod DeepestStackMode 52 53 switch vv := recovered.(type) { 54 case panicWrap: 55 if st == nil { 56 return vv 57 } 58 switch CompareStackTrace(vv.st, st) { 59 case EqualStack, SupersetStack: 60 return vv 61 } 62 case stackWrap: 63 stDeepest, stDeepMod = reuseSupersetTrace(st, vv.stDeepest) 64 case fmtWrap: 65 return panicWrap{st: st, stDeepest: stDeepest, fmtWrap: vv} 66 case error: 67 sth := OutermostStack(vv) 68 if sth != nil { 69 stDeep, _ := sth.DeepestStackTrace() 70 stDeepest, stDeepMod = reuseSupersetTrace(st, stDeep) 71 } 72 } 73 return panicWrap{st, recovered, stDeepest, wrapInternal(recovered), stDeepMod} 74 } 75 76 // InnermostPanicWithStack returns the most distant panic holder from the chain. 77 // The value can be either error or PanicHolder. Otherwise will return nil. 78 func InnermostPanicWithStack(errorOrHolder interface{}) PanicHolder { 79 switch vv := errorOrHolder.(type) { 80 case PanicHolder: 81 st := vv.ShallowStackTrace() 82 if st != nil { 83 return vv 84 } 85 return innermostWithStack(vv.Cause()) 86 case error: 87 return innermostWithStack(vv) 88 default: 89 return nil 90 } 91 } 92 93 func innermostWithStack(errChain error) PanicHolder { 94 for errChain != nil { 95 if sw, ok := errChain.(panicWrap); ok { 96 nextErr := sw.Unwrap() 97 if sw.ShallowStackTrace() != nil && nextErr != nil { 98 return sw 99 } 100 errChain = nextErr 101 continue 102 } 103 errChain = errors.Unwrap(errChain) 104 } 105 return nil 106 }