stackdump.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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. // Package stackdump provides wrappers for runtime.Stack and runtime.Callers
  15. // with uniform support for skipping caller frames.
  16. //
  17. // ⚠ Unlike the functions in the runtime package, these may allocate a
  18. // non-trivial quantity of memory: use them with care. ⚠
  19. package stackdump
  20. import (
  21. "bytes"
  22. "runtime"
  23. )
  24. // runtimeStackSelfFrames is 1 if runtime.Stack includes the call to
  25. // runtime.Stack itself or 0 if it does not.
  26. //
  27. // As of 2016-04-27, the gccgo compiler includes runtime.Stack but the gc
  28. // compiler does not.
  29. var runtimeStackSelfFrames = func() int {
  30. for n := 1 << 10; n < 1<<20; n *= 2 {
  31. buf := make([]byte, n)
  32. n := runtime.Stack(buf, false)
  33. if bytes.Contains(buf[:n], []byte("runtime.Stack")) {
  34. return 1
  35. } else if n < len(buf) || bytes.Count(buf, []byte("\n")) >= 3 {
  36. return 0
  37. }
  38. }
  39. return 0
  40. }()
  41. // Stack is a stack dump for a single goroutine.
  42. type Stack struct {
  43. // Text is a representation of the stack dump in a human-readable format.
  44. Text []byte
  45. // PC is a representation of the stack dump using raw program counter values.
  46. PC []uintptr
  47. }
  48. func (s Stack) String() string { return string(s.Text) }
  49. // Caller returns the Stack dump for the calling goroutine, starting skipDepth
  50. // frames before the caller of Caller. (Caller(0) provides a dump starting at
  51. // the caller of this function.)
  52. func Caller(skipDepth int) Stack {
  53. return Stack{
  54. Text: CallerText(skipDepth + 1),
  55. PC: CallerPC(skipDepth + 1),
  56. }
  57. }
  58. // CallerText returns a textual dump of the stack starting skipDepth frames before
  59. // the caller. (CallerText(0) provides a dump starting at the caller of this
  60. // function.)
  61. func CallerText(skipDepth int) []byte {
  62. for n := 1 << 10; ; n *= 2 {
  63. buf := make([]byte, n)
  64. n := runtime.Stack(buf, false)
  65. if n < len(buf) {
  66. return pruneFrames(skipDepth+1+runtimeStackSelfFrames, buf[:n])
  67. }
  68. }
  69. }
  70. // CallerPC returns a dump of the program counters of the stack starting
  71. // skipDepth frames before the caller. (CallerPC(0) provides a dump starting at
  72. // the caller of this function.)
  73. func CallerPC(skipDepth int) []uintptr {
  74. for n := 1 << 8; ; n *= 2 {
  75. buf := make([]uintptr, n)
  76. n := runtime.Callers(skipDepth+2, buf)
  77. if n < len(buf) {
  78. return buf[:n]
  79. }
  80. }
  81. }
  82. // pruneFrames removes the topmost skipDepth frames of the first goroutine in a
  83. // textual stack dump. It overwrites the passed-in slice.
  84. //
  85. // If there are fewer than skipDepth frames in the first goroutine's stack,
  86. // pruneFrames prunes it to an empty stack and leaves the remaining contents
  87. // intact.
  88. func pruneFrames(skipDepth int, stack []byte) []byte {
  89. headerLen := 0
  90. for i, c := range stack {
  91. if c == '\n' {
  92. headerLen = i + 1
  93. break
  94. }
  95. }
  96. if headerLen == 0 {
  97. return stack // No header line - not a well-formed stack trace.
  98. }
  99. skipLen := headerLen
  100. skipNewlines := skipDepth * 2
  101. for ; skipLen < len(stack) && skipNewlines > 0; skipLen++ {
  102. c := stack[skipLen]
  103. if c != '\n' {
  104. continue
  105. }
  106. skipNewlines--
  107. skipLen++
  108. if skipNewlines == 0 || skipLen == len(stack) || stack[skipLen] == '\n' {
  109. break
  110. }
  111. }
  112. pruned := stack[skipLen-headerLen:]
  113. copy(pruned, stack[:headerLen])
  114. return pruned
  115. }