github.com/haraldrudell/parl@v0.4.176/pruntime/debug-stack.go (about) 1 /* 2 © 2022–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package pruntime 7 8 import ( 9 "bytes" 10 "runtime" 11 "runtime/debug" 12 ) 13 14 const ( 15 // the lead-in line contains “goroutine 18 [running]:” 16 prLeadInLines = 1 17 // stack frame of pruntime.DebugStack function 18 prDebugStackStdFrame = 1 19 // stack frame of debuig.Stack function 20 prDebugStackFnFrame = 1 21 // - first line of frame describes source file 22 // - second line of frame describes package and function 23 prLinesPerFrame = 2 24 // creator lines are two lines that describe how a goroutine was launched: 25 // - “created by…” 26 prCreatorLines = 2 27 // newline as a byte 28 byteNewline = byte('\n') 29 // tab as a byte 30 byteTab = byte('\t') 31 // space as a byte 32 byteSpace = byte('\x20') 33 ) 34 35 // byte slice newline separator 36 var byteSliceNewline = []byte{byteNewline} 37 38 // byte slice tab 39 var byteSliceTab = []byte{byteTab} 40 41 // byte slice two spaces 42 var byteSliceTwoSpaces = []byte{byteSpace, byteSpace} 43 44 var _ = runtime.Stack 45 46 // DebugStack returns a string stack trace intended to be printed or when a full printable trace is desired 47 // - top returned stack frame is caller of [pruntime.DebugStack] 48 // - skipFrames allows for removing additional frames. 49 // - differences from debug.Stack: 50 // - tabs are replaced with two spaces 51 // - Stack frames other than the callers of pruntime.DebugStack are removed 52 func DebugStack(skipFrames int) (stack string) { 53 if skipFrames < 0 { 54 skipFrames = 0 55 } 56 57 /* 58 goroutine 18 [running]: 59 runtime/debug.Stack() 60 /opt/homebrew/Cellar/go/1.18/libexec/src/runtime/debug/stack.go:24 +0x68 61 github.com/haraldrudell/parl/pruntime.NewStack() 62 … 63 created by testing.(*T).Run 64 /opt/homebrew/Cellar/go/1.18/libexec/src/testing/testing.go:1486 +0x300 65 */ 66 67 // remove final newline, split into lines 68 // - hold off on converting to string to reduce interning memory leak 69 // - [][]byte 70 var stackTraceByteLines = bytes.Split(bytes.TrimSuffix(debug.Stack(), byteSliceNewline), byteSliceNewline) 71 72 // number of stack frames that are not header data or creator line 73 var frameCount = (len(stackTraceByteLines)-prLeadInLines-prCreatorLines)/prLinesPerFrame - 74 prDebugStackStdFrame - prDebugStackFnFrame 75 76 // check skipFrames maximum value 77 if skipFrames > frameCount { 78 skipFrames = frameCount 79 } 80 81 // undesirable stack frames: debug.Stack, pruntime.DebugStack and skipFrames 82 var skipLines = prLinesPerFrame * (prDebugStackStdFrame + prDebugStackFnFrame + skipFrames) 83 84 // remove lines 85 copy(stackTraceByteLines[prLeadInLines:], stackTraceByteLines[prLeadInLines+skipLines:]) 86 stackTraceByteLines = stackTraceByteLines[:len(stackTraceByteLines)-skipLines] 87 88 // merge back together, replace tab with two spaces, make string 89 stack = string(bytes.ReplaceAll( 90 bytes.Join(stackTraceByteLines, byteSliceNewline), 91 byteSliceTab, 92 byteSliceTwoSpaces, 93 )) 94 95 return 96 }