gdebug_caller.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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.Replace(goRootForFilter, "\\", "/", -1)
  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. return true
  116. }
  117. return false
  118. }
  119. // CallerPackage returns the package name of the caller.
  120. func CallerPackage() string {
  121. function, _, _ := Caller()
  122. indexSplit := strings.LastIndexByte(function, '/')
  123. if indexSplit == -1 {
  124. return function[:strings.IndexByte(function, '.')]
  125. } else {
  126. leftPart := function[:indexSplit+1]
  127. rightPart := function[indexSplit+1:]
  128. indexDot := strings.IndexByte(function, '.')
  129. rightPart = rightPart[:indexDot-1]
  130. return leftPart + rightPart
  131. }
  132. }
  133. // CallerFunction returns the function name of the caller.
  134. func CallerFunction() string {
  135. function, _, _ := Caller()
  136. function = function[strings.LastIndexByte(function, '/')+1:]
  137. function = function[strings.IndexByte(function, '.')+1:]
  138. return function
  139. }
  140. // CallerFilePath returns the file path of the caller.
  141. func CallerFilePath() string {
  142. _, path, _ := Caller()
  143. return path
  144. }
  145. // CallerDirectory returns the directory of the caller.
  146. func CallerDirectory() string {
  147. _, path, _ := Caller()
  148. return filepath.Dir(path)
  149. }
  150. // CallerFileLine returns the file path along with the line number of the caller.
  151. func CallerFileLine() string {
  152. _, path, line := Caller()
  153. return fmt.Sprintf(`%s:%d`, path, line)
  154. }
  155. // CallerFileLineShort returns the file name along with the line number of the caller.
  156. func CallerFileLineShort() string {
  157. _, path, line := Caller()
  158. return fmt.Sprintf(`%s:%d`, filepath.Base(path), line)
  159. }
  160. // FuncPath returns the complete function path of given `f`.
  161. func FuncPath(f interface{}) string {
  162. return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
  163. }
  164. // FuncName returns the function name of given `f`.
  165. func FuncName(f interface{}) string {
  166. path := FuncPath(f)
  167. if path == "" {
  168. return ""
  169. }
  170. index := strings.LastIndexByte(path, '/')
  171. if index < 0 {
  172. index = strings.LastIndexByte(path, '\\')
  173. }
  174. return path[index+1:]
  175. }