stack.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package errors
  2. import (
  3. "fmt"
  4. "io"
  5. "path"
  6. "runtime"
  7. "strconv"
  8. "strings"
  9. )
  10. // tip: 计算机中关于程序计数器,不要理解成是程序的执行次数,而是指向某个位置的下标,或者说是一个地址。
  11. // Frame 调用栈程序计数器,用来表示栈指针。
  12. type Frame uintptr
  13. // pc 返回程序计数器。我们会是用栈指针来获取栈帧对应函数的文件名 行号等信息
  14. // runtime.Caller 可以返回函数栈某一层的程序计数器,0表示函数本身,1表示调用者
  15. // runtime.Callers 可以返回某个调用链的所有程序计数器,由于历史原因,1表示函数本身,和上面的0相同。所以在这里做-1操作
  16. func (f Frame) pc() uintptr {
  17. return uintptr(f) - 1
  18. }
  19. // file 获取当前栈帧对应函数所在的文件名
  20. func (f Frame) file() string {
  21. // runtime.FuncForPC 函数可以把程序计数器地址对应的函数信息获取出来
  22. fn := runtime.FuncForPC(f.pc())
  23. if fn == nil {
  24. return "unknown"
  25. }
  26. file, _ := fn.FileLine(f.pc())
  27. return file
  28. }
  29. // line 获取当前栈帧对应函数所在的行号
  30. func (f Frame) line() int {
  31. fn := runtime.FuncForPC(f.pc())
  32. if fn == nil {
  33. return 0
  34. }
  35. _, line := fn.FileLine(f.pc())
  36. return line
  37. }
  38. // name 获取当前栈帧对应函数的函数名称
  39. func (f Frame) name() string {
  40. fn := runtime.FuncForPC(f.pc())
  41. if fn == nil {
  42. return "unknown"
  43. }
  44. return fn.Name()
  45. }
  46. // Format 栈帧对应的函数进行格式化输出。
  47. // 实现 fmt.Formatter 接口用来支持fmt包格式化输出
  48. // %s 只打印文件名
  49. // %+s 打印文件完整路径和名称
  50. // %d 只打印行号
  51. // %n 只打印函数名称
  52. // %v %s:%d
  53. // %+v %+s:%d
  54. func (f Frame) Format(s fmt.State, verb rune) {
  55. switch verb {
  56. case 's':
  57. switch {
  58. case s.Flag('+'):
  59. _, _ = io.WriteString(s, f.name())
  60. _, _ = io.WriteString(s, "\n\t")
  61. _, _ = io.WriteString(s, f.file())
  62. default:
  63. _, _ = io.WriteString(s, path.Base(f.file()))
  64. }
  65. case 'd':
  66. _, _ = io.WriteString(s, strconv.Itoa(f.line()))
  67. case 'n':
  68. _, _ = io.WriteString(s, f.name())
  69. case 'v':
  70. f.Format(s, 's')
  71. _, _ = io.WriteString(s, ":")
  72. f.Format(s, 'd')
  73. }
  74. }
  75. // MarshalText 序列化为字符串输出
  76. // 实现 encoding.TextMarshaler 接口
  77. func (f Frame) MarshalText() ([]byte, error) {
  78. name := f.name()
  79. if name == "unknown" {
  80. return []byte(name), nil
  81. }
  82. return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
  83. }
  84. // StackTrace 堆栈结构
  85. type StackTrace []Frame
  86. // Format 整个调用栈的格式化输出
  87. // 实现 fmt.Formatter 接口用来支持fmt包格式化输出
  88. // %s 堆栈输出格式为数组字符串
  89. // %v 堆栈输出格式为数组字符串
  90. // %+v 输出堆栈中每个栈帧的为文件名 函数名 行号,使用换行分隔。
  91. func (st StackTrace) Format(s fmt.State, verb rune) {
  92. switch verb {
  93. case 'v':
  94. switch {
  95. case s.Flag('+'):
  96. for _, f := range st {
  97. _, _ = io.WriteString(s, "\n")
  98. f.Format(s, verb)
  99. }
  100. case s.Flag('#'):
  101. fmt.Fprintf(s, "%#v", []Frame(st))
  102. default:
  103. st.formatSlice(s, verb)
  104. }
  105. case 's':
  106. st.formatSlice(s, verb)
  107. }
  108. }
  109. // formatSlice 将堆栈转换为数组字符串格式输出
  110. func (st StackTrace) formatSlice(s fmt.State, verb rune) {
  111. _, _ = io.WriteString(s, "[")
  112. for i, f := range st {
  113. if i > 0 {
  114. _, _ = io.WriteString(s, " ")
  115. }
  116. f.Format(s, verb)
  117. }
  118. _, _ = io.WriteString(s, "]")
  119. }
  120. // stack 程序计数器的堆栈
  121. type stack []uintptr
  122. // Format 直接格式化输出
  123. func (s *stack) Format(st fmt.State, verb rune) {
  124. switch verb {
  125. case 'v':
  126. switch {
  127. case st.Flag('+'):
  128. for _, pc := range *s {
  129. f := Frame(pc)
  130. fmt.Fprintf(st, "\n%+v", f)
  131. }
  132. }
  133. }
  134. }
  135. // StackTrace 转换为封装好的堆栈信息 StackTrace
  136. func (s *stack) StackTrace() StackTrace {
  137. f := make([]Frame, len(*s))
  138. for i := 0; i < len(f); i++ {
  139. f[i] = Frame((*s)[i])
  140. }
  141. return f
  142. }
  143. // 生成堆栈的程序计算器
  144. func callers() *stack {
  145. const depth = 32
  146. var pcs [depth]uintptr
  147. n := runtime.Callers(3, pcs[:])
  148. var st stack = pcs[0:n]
  149. return &st
  150. }
  151. // funcname 获取函数名。去除路径和包前缀
  152. func funcname(name string) string {
  153. i := strings.LastIndex(name, "/")
  154. name = name[i+1:]
  155. i = strings.Index(name, ".")
  156. return name[i+1:]
  157. }