github.com/haraldrudell/parl@v0.4.176/perrors/panicdetector/indices.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package panicdetector 7 8 import ( 9 "strings" 10 11 "github.com/haraldrudell/parl/pruntime" 12 ) 13 14 const ( 15 runtimePrefix = "runtime." 16 ) 17 18 // Indices examines a stack to see if it includes a panic. Thread-safe 19 // - isPanic is true if the stack includes a panic 20 // - stack[recoveryIndex] is the code line of the deferred function containing recovery() invocation 21 // - stack[panicIndex] is the code line causing the panic located in 22 // the function deferring or any code that function invokes 23 // - for panic detection to work, the code line of defer() invocation 24 // must be included in stack 25 // - note: the location in the deferring function is returned as the last 26 // line of the deferring function 27 // - [pruntime.Stack] is the parsed output of [runtime.Stack] 28 func Indices(stack pruntime.Stack) (isPanic bool, recoveryIndex, panicIndex int) { 29 30 // scan for newest frame for the Go runtime’s defer invoker 31 // - [runtime.Stack], unlike [runtime.Callers], hides runtime frames handling the panic, 32 // so only look for panic() 33 var found = 0 34 var frames = stack.Frames() 35 var stackLength = len(frames) 36 var pd = panicDetectorOne 37 for i := 0; i < stackLength; i++ { 38 var funcName = frames[i].Loc().FuncName 39 if i > 0 && funcName == pd.runtimeDeferInvokerLocation { 40 recoveryIndex = i - 1 41 found++ 42 if found == 2 { 43 break 44 } 45 } 46 if i+1 < stackLength && funcName == pd.runtimePanicFunctionLocation { 47 48 // scan for end of runtime functions 49 for panicIndex = i + 1; panicIndex+1 < stackLength; panicIndex++ { 50 if !strings.HasPrefix(frames[panicIndex].Loc().FuncLine(), runtimePrefix) { 51 break // this frame not part of the runtime 52 } 53 } 54 55 found++ 56 if found == 2 { 57 break 58 } 59 } 60 } 61 isPanic = found == 2 62 63 return 64 }