// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. package gdebug import ( "fmt" "os" "os/exec" "path/filepath" "reflect" "runtime" "strings" ) const ( maxCallerDepth = 1000 stackFilterKey = "/debug/gdebug/gdebug" ) var ( goRootForFilter = runtime.GOROOT() // goRootForFilter is used for stack filtering purpose. binaryVersion = "" // The version of current running binary(uint64 hex). binaryVersionMd5 = "" // The version of current running binary(MD5). selfPath = "" // Current running binary absolute path. ) func init() { if goRootForFilter != "" { goRootForFilter = strings.ReplaceAll(goRootForFilter, "\\", "/") } // Initialize internal package variable: selfPath. selfPath, _ = exec.LookPath(os.Args[0]) if selfPath != "" { selfPath, _ = filepath.Abs(selfPath) } if selfPath == "" { selfPath, _ = filepath.Abs(os.Args[0]) } } // Caller returns the function name and the absolute file path along with its line // number of the caller. func Caller(skip ...int) (function string, path string, line int) { return CallerWithFilter(nil, skip...) } // CallerWithFilter returns the function name and the absolute file path along with // its line number of the caller. // // The parameter `filters` is used to filter the path of the caller. func CallerWithFilter(filters []string, skip ...int) (function string, path string, line int) { var ( number = 0 ok = true ) if len(skip) > 0 { number = skip[0] } pc, file, line, start := callerFromIndex(filters) if start != -1 { for i := start + number; i < maxCallerDepth; i++ { if i != start { pc, file, line, ok = runtime.Caller(i) } if ok { if filterFileByFilters(file, filters) { continue } function = "" if fn := runtime.FuncForPC(pc); fn == nil { function = "unknown" } else { function = fn.Name() } return function, file, line } else { break } } } return "", "", -1 } // callerFromIndex returns the caller position and according information exclusive of the // debug package. // // VERY NOTE THAT, the returned index value should be `index - 1` as the caller's start point. func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) { var ok bool for index = 0; index < maxCallerDepth; index++ { if pc, file, line, ok = runtime.Caller(index); ok { if filterFileByFilters(file, filters) { continue } if index > 0 { index-- } return } } return 0, "", -1, -1 } func filterFileByFilters(file string, filters []string) (filtered bool) { // Filter empty file. if file == "" { return true } // Filter gdebug package callings. if strings.Contains(file, stackFilterKey) { return true } for _, filter := range filters { if filter != "" && strings.Contains(file, filter) { return true } } // GOROOT filter. if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { // https://github.com/gogf/gf/issues/2047 fileSeparator := file[len(goRootForFilter)] if fileSeparator == filepath.Separator || fileSeparator == '\\' || fileSeparator == '/' { return true } } return false } // CallerPackage returns the package name of the caller. func CallerPackage() string { function, _, _ := Caller() indexSplit := strings.LastIndexByte(function, '/') if indexSplit == -1 { return function[:strings.IndexByte(function, '.')] } else { leftPart := function[:indexSplit+1] rightPart := function[indexSplit+1:] indexDot := strings.IndexByte(function, '.') rightPart = rightPart[:indexDot-1] return leftPart + rightPart } } // CallerFunction returns the function name of the caller. func CallerFunction() string { function, _, _ := Caller() function = function[strings.LastIndexByte(function, '/')+1:] function = function[strings.IndexByte(function, '.')+1:] return function } // CallerFilePath returns the file path of the caller. func CallerFilePath() string { _, path, _ := Caller() return path } // CallerDirectory returns the directory of the caller. func CallerDirectory() string { _, path, _ := Caller() return filepath.Dir(path) } // CallerFileLine returns the file path along with the line number of the caller. func CallerFileLine() string { _, path, line := Caller() return fmt.Sprintf(`%s:%d`, path, line) } // CallerFileLineShort returns the file name along with the line number of the caller. func CallerFileLineShort() string { _, path, line := Caller() return fmt.Sprintf(`%s:%d`, filepath.Base(path), line) } // FuncPath returns the complete function path of given `f`. func FuncPath(f interface{}) string { return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() } // FuncName returns the function name of given `f`. func FuncName(f interface{}) string { path := FuncPath(f) if path == "" { return "" } index := strings.LastIndexByte(path, '/') if index < 0 { index = strings.LastIndexByte(path, '\\') } return path[index+1:] }