gdebug_caller.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. // Copyright 2019-2020 gf Author(https://github.com/gogf/gf). 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. gMAX_DEPTH = 1000
  18. gFILTER_KEY = "/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. // CallerPath 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("", skip...)
  43. }
  44. // CallerPathWithFilter returns the function name and the absolute file path along with
  45. // its line number of the caller.
  46. //
  47. // The parameter <filter> is used to filter the path of the caller.
  48. func CallerWithFilter(filter string, skip ...int) (function string, path string, line int) {
  49. number := 0
  50. if len(skip) > 0 {
  51. number = skip[0]
  52. }
  53. ok := true
  54. pc, file, line, start := callerFromIndex([]string{filter})
  55. if start != -1 {
  56. for i := start + number; i < gMAX_DEPTH; i++ {
  57. if i != start {
  58. pc, file, line, ok = runtime.Caller(i)
  59. }
  60. if ok {
  61. if filter != "" && strings.Contains(file, filter) {
  62. continue
  63. }
  64. if strings.Contains(file, gFILTER_KEY) {
  65. continue
  66. }
  67. function := ""
  68. if fn := runtime.FuncForPC(pc); fn == nil {
  69. function = "unknown"
  70. } else {
  71. function = fn.Name()
  72. }
  73. return function, file, line
  74. } else {
  75. break
  76. }
  77. }
  78. }
  79. return "", "", -1
  80. }
  81. // callerFromIndex returns the caller position and according information exclusive of the
  82. // debug package.
  83. //
  84. // VERY NOTE THAT, the returned index value should be <index - 1> as the caller's start point.
  85. func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) {
  86. var filtered, ok bool
  87. for index = 0; index < gMAX_DEPTH; index++ {
  88. if pc, file, line, ok = runtime.Caller(index); ok {
  89. filtered = false
  90. for _, filter := range filters {
  91. if filter != "" && strings.Contains(file, filter) {
  92. filtered = true
  93. break
  94. }
  95. }
  96. if filtered {
  97. continue
  98. }
  99. if strings.Contains(file, gFILTER_KEY) {
  100. continue
  101. }
  102. if index > 0 {
  103. index--
  104. }
  105. return
  106. }
  107. }
  108. return 0, "", -1, -1
  109. }
  110. // CallerPackage returns the package name of the caller.
  111. func CallerPackage() string {
  112. function, _, _ := Caller()
  113. indexSplit := strings.LastIndexByte(function, '/')
  114. if indexSplit == -1 {
  115. return function[:strings.IndexByte(function, '.')]
  116. } else {
  117. leftPart := function[:indexSplit+1]
  118. rightPart := function[indexSplit+1:]
  119. indexDot := strings.IndexByte(function, '.')
  120. rightPart = rightPart[:indexDot-1]
  121. return leftPart + rightPart
  122. }
  123. }
  124. // CallerFunction returns the function name of the caller.
  125. func CallerFunction() string {
  126. function, _, _ := Caller()
  127. function = function[strings.LastIndexByte(function, '/')+1:]
  128. function = function[strings.IndexByte(function, '.')+1:]
  129. return function
  130. }
  131. // CallerFilePath returns the file path of the caller.
  132. func CallerFilePath() string {
  133. _, path, _ := Caller()
  134. return path
  135. }
  136. // CallerDirectory returns the directory of the caller.
  137. func CallerDirectory() string {
  138. _, path, _ := Caller()
  139. return filepath.Dir(path)
  140. }
  141. // CallerFileLine returns the file path along with the line number of the caller.
  142. func CallerFileLine() string {
  143. _, path, line := Caller()
  144. return fmt.Sprintf(`%s:%d`, path, line)
  145. }
  146. // CallerFileLineShort returns the file name along with the line number of the caller.
  147. func CallerFileLineShort() string {
  148. _, path, line := Caller()
  149. return fmt.Sprintf(`%s:%d`, filepath.Base(path), line)
  150. }
  151. // FuncPath returns the complete function path of given <f>.
  152. func FuncPath(f interface{}) string {
  153. return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
  154. }
  155. // FuncName returns the function name of given <f>.
  156. func FuncName(f interface{}) string {
  157. path := FuncPath(f)
  158. if path == "" {
  159. return ""
  160. }
  161. index := strings.LastIndexByte(path, '/')
  162. if index < 0 {
  163. index = strings.LastIndexByte(path, '\\')
  164. }
  165. return path[index+1:]
  166. }