github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/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/wangyougui/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 indexSplit := strings.LastIndexByte(function, '/') 137 if indexSplit == -1 { 138 return function[:strings.IndexByte(function, '.')] 139 } else { 140 leftPart := function[:indexSplit+1] 141 rightPart := function[indexSplit+1:] 142 indexDot := strings.IndexByte(function, '.') 143 rightPart = rightPart[:indexDot-1] 144 return leftPart + rightPart 145 } 146 } 147 148 // CallerFunction returns the function name of the caller. 149 func CallerFunction() string { 150 function, _, _ := Caller() 151 function = function[strings.LastIndexByte(function, '/')+1:] 152 function = function[strings.IndexByte(function, '.')+1:] 153 return function 154 } 155 156 // CallerFilePath returns the file path of the caller. 157 func CallerFilePath() string { 158 _, path, _ := Caller() 159 return path 160 } 161 162 // CallerDirectory returns the directory of the caller. 163 func CallerDirectory() string { 164 _, path, _ := Caller() 165 return filepath.Dir(path) 166 } 167 168 // CallerFileLine returns the file path along with the line number of the caller. 169 func CallerFileLine() string { 170 _, path, line := Caller() 171 return fmt.Sprintf(`%s:%d`, path, line) 172 } 173 174 // CallerFileLineShort returns the file name along with the line number of the caller. 175 func CallerFileLineShort() string { 176 _, path, line := Caller() 177 return fmt.Sprintf(`%s:%d`, filepath.Base(path), line) 178 } 179 180 // FuncPath returns the complete function path of given `f`. 181 func FuncPath(f interface{}) string { 182 return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() 183 } 184 185 // FuncName returns the function name of given `f`. 186 func FuncName(f interface{}) string { 187 path := FuncPath(f) 188 if path == "" { 189 return "" 190 } 191 index := strings.LastIndexByte(path, '/') 192 if index < 0 { 193 index = strings.LastIndexByte(path, '\\') 194 } 195 return path[index+1:] 196 }