1 // Copyright 2023 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package stackdump provides wrappers for runtime.Stack and runtime.Callers 16 // with uniform support for skipping caller frames. 17 // 18 // ⚠ Unlike the functions in the runtime package, these may allocate a 19 // non-trivial quantity of memory: use them with care. ⚠ 20 package stackdump 21 22 import ( 23 "bytes" 24 "runtime" 25 ) 26 27 // runtimeStackSelfFrames is 1 if runtime.Stack includes the call to 28 // runtime.Stack itself or 0 if it does not. 29 // 30 // As of 2016-04-27, the gccgo compiler includes runtime.Stack but the gc 31 // compiler does not. 32 var runtimeStackSelfFrames = func() int { 33 for n := 1 << 10; n < 1<<20; n *= 2 { 34 buf := make([]byte, n) 35 n := runtime.Stack(buf, false) 36 if bytes.Contains(buf[:n], []byte("runtime.Stack")) { 37 return 1 38 } else if n < len(buf) || bytes.Count(buf, []byte("\n")) >= 3 { 39 return 0 40 } 41 } 42 return 0 43 }() 44 45 // Stack is a stack dump for a single goroutine. 46 type Stack struct { 47 // Text is a representation of the stack dump in a human-readable format. 48 Text []byte 49 50 // PC is a representation of the stack dump using raw program counter values. 51 PC []uintptr 52 } 53 54 func (s Stack) String() string { return string(s.Text) } 55 56 // Caller returns the Stack dump for the calling goroutine, starting skipDepth 57 // frames before the caller of Caller. (Caller(0) provides a dump starting at 58 // the caller of this function.) 59 func Caller(skipDepth int) Stack { 60 return Stack{ 61 Text: CallerText(skipDepth + 1), 62 PC: CallerPC(skipDepth + 1), 63 } 64 } 65 66 // CallerText returns a textual dump of the stack starting skipDepth frames before 67 // the caller. (CallerText(0) provides a dump starting at the caller of this 68 // function.) 69 func CallerText(skipDepth int) []byte { 70 for n := 1 << 10; ; n *= 2 { 71 buf := make([]byte, n) 72 n := runtime.Stack(buf, false) 73 if n < len(buf) { 74 return pruneFrames(skipDepth+1+runtimeStackSelfFrames, buf[:n]) 75 } 76 } 77 } 78 79 // CallerPC returns a dump of the program counters of the stack starting 80 // skipDepth frames before the caller. (CallerPC(0) provides a dump starting at 81 // the caller of this function.) 82 func CallerPC(skipDepth int) []uintptr { 83 for n := 1 << 8; ; n *= 2 { 84 buf := make([]uintptr, n) 85 n := runtime.Callers(skipDepth+2, buf) 86 if n < len(buf) { 87 return buf[:n] 88 } 89 } 90 } 91 92 // pruneFrames removes the topmost skipDepth frames of the first goroutine in a 93 // textual stack dump. It overwrites the passed-in slice. 94 // 95 // If there are fewer than skipDepth frames in the first goroutine's stack, 96 // pruneFrames prunes it to an empty stack and leaves the remaining contents 97 // intact. 98 func pruneFrames(skipDepth int, stack []byte) []byte { 99 headerLen := 0 100 for i, c := range stack { 101 if c == '\n' { 102 headerLen = i + 1 103 break 104 } 105 } 106 if headerLen == 0 { 107 return stack // No header line - not a well-formed stack trace. 108 } 109 110 skipLen := headerLen 111 skipNewlines := skipDepth * 2 112 for ; skipLen < len(stack) && skipNewlines > 0; skipLen++ { 113 c := stack[skipLen] 114 if c != '\n' { 115 continue 116 } 117 skipNewlines-- 118 skipLen++ 119 if skipNewlines == 0 || skipLen == len(stack) || stack[skipLen] == '\n' { 120 break 121 } 122 } 123 124 pruned := stack[skipLen-headerLen:] 125 copy(pruned, stack[:headerLen]) 126 return pruned 127 } 128