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