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  }