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