gdebug_caller.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gdebug
  7. import (
  8. "fmt"
  9. "os"
  10. "os/exec"
  11. "path/filepath"
  12. "reflect"
  13. "runtime"
  14. "strings"
  15. )
  16. const (
  17. maxCallerDepth = 1000
  18. stackFilterKey = "/debug/gdebug/gdebug"
  19. )
  20. var (
  21. goRootForFilter = runtime.GOROOT() // goRootForFilter is used for stack filtering purpose.
  22. binaryVersion = "" // The version of current running binary(uint64 hex).
  23. binaryVersionMd5 = "" // The version of current running binary(MD5).
  24. selfPath = "" // Current running binary absolute path.
  25. )
  26. func init() {
  27. if goRootForFilter != "" {
  28. goRootForFilter = strings.ReplaceAll(goRootForFilter, "\\", "/")
  29. }
  30. // Initialize internal package variable: selfPath.
  31. selfPath, _ = exec.LookPath(os.Args[0])
  32. if selfPath != "" {
  33. selfPath, _ = filepath.Abs(selfPath)
  34. }
  35. if selfPath == "" {
  36. selfPath, _ = filepath.Abs(os.Args[0])
  37. }
  38. }
  39. // Caller returns the function name and the absolute file path along with its line
  40. // number of the caller.
  41. func Caller(skip ...int) (function string, path string, line int) {
  42. return CallerWithFilter(nil, skip...)
  43. }
  44. // CallerWithFilter returns the function name and the absolute file path along with
  45. // its line number of the caller.
  46. //
  47. // The parameter `filters` is used to filter the path of the caller.
  48. func CallerWithFilter(filters []string, skip ...int) (function string, path string, line int) {
  49. var (
  50. number = 0
  51. ok = true
  52. )
  53. if len(skip) > 0 {
  54. number = skip[0]
  55. }
  56. pc, file, line, start := callerFromIndex(filters)
  57. if start != -1 {
  58. for i := start + number; i < maxCallerDepth; i++ {
  59. if i != start {
  60. pc, file, line, ok = runtime.Caller(i)
  61. }
  62. if ok {
  63. if filterFileByFilters(file, filters) {
  64. continue
  65. }
  66. function = ""
  67. if fn := runtime.FuncForPC(pc); fn == nil {
  68. function = "unknown"
  69. } else {
  70. function = fn.Name()
  71. }
  72. return function, file, line
  73. } else {
  74. break
  75. }
  76. }
  77. }
  78. return "", "", -1
  79. }
  80. // callerFromIndex returns the caller position and according information exclusive of the
  81. // debug package.
  82. //
  83. // VERY NOTE THAT, the returned index value should be `index - 1` as the caller's start point.
  84. func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) {
  85. var ok bool
  86. for index = 0; index < maxCallerDepth; index++ {
  87. if pc, file, line, ok = runtime.Caller(index); ok {
  88. if filterFileByFilters(file, filters) {
  89. continue
  90. }
  91. if index > 0 {
  92. index--
  93. }
  94. return
  95. }
  96. }
  97. return 0, "", -1, -1
  98. }
  99. func filterFileByFilters(file string, filters []string) (filtered bool) {
  100. // Filter empty file.
  101. if file == "" {
  102. return true
  103. }
  104. // Filter gdebug package callings.
  105. if strings.Contains(file, stackFilterKey) {
  106. return true
  107. }
  108. for _, filter := range filters {
  109. if filter != "" && strings.Contains(file, filter) {
  110. return true
  111. }
  112. }
  113. // GOROOT filter.
  114. if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
  115. // https://github.com/gogf/gf/issues/2047
  116. fileSeparator := file[len(goRootForFilter)]
  117. if fileSeparator == filepath.Separator || fileSeparator == '\\' || fileSeparator == '/' {
  118. return true
  119. }
  120. }
  121. return false
  122. }
  123. // CallerPackage returns the package name of the caller.
  124. func CallerPackage() string {
  125. function, _, _ := Caller()
  126. indexSplit := strings.LastIndexByte(function, '/')
  127. if indexSplit == -1 {
  128. return function[:strings.IndexByte(function, '.')]
  129. } else {
  130. leftPart := function[:indexSplit+1]
  131. rightPart := function[indexSplit+1:]
  132. indexDot := strings.IndexByte(function, '.')
  133. rightPart = rightPart[:indexDot-1]
  134. return leftPart + rightPart
  135. }
  136. }
  137. // CallerFunction returns the function name of the caller.
  138. func CallerFunction() string {
  139. function, _, _ := Caller()
  140. function = function[strings.LastIndexByte(function, '/')+1:]
  141. function = function[strings.IndexByte(function, '.')+1:]
  142. return function
  143. }
  144. // CallerFilePath returns the file path of the caller.
  145. func CallerFilePath() string {
  146. _, path, _ := Caller()
  147. return path
  148. }
  149. // CallerDirectory returns the directory of the caller.
  150. func CallerDirectory() string {
  151. _, path, _ := Caller()
  152. return filepath.Dir(path)
  153. }
  154. // CallerFileLine returns the file path along with the line number of the caller.
  155. func CallerFileLine() string {
  156. _, path, line := Caller()
  157. return fmt.Sprintf(`%s:%d`, path, line)
  158. }
  159. // CallerFileLineShort returns the file name along with the line number of the caller.
  160. func CallerFileLineShort() string {
  161. _, path, line := Caller()
  162. return fmt.Sprintf(`%s:%d`, filepath.Base(path), line)
  163. }
  164. // FuncPath returns the complete function path of given `f`.
  165. func FuncPath(f interface{}) string {
  166. return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
  167. }
  168. // FuncName returns the function name of given `f`.
  169. func FuncName(f interface{}) string {
  170. path := FuncPath(f)
  171. if path == "" {
  172. return ""
  173. }
  174. index := strings.LastIndexByte(path, '/')
  175. if index < 0 {
  176. index = strings.LastIndexByte(path, '\\')
  177. }
  178. return path[index+1:]
  179. }