package runtimehelper import ( "fmt" "path/filepath" "runtime" ) const ( callerSkipOffset = 2 maximumFrames = 32 ) // Frame 调用相关信息 type Frame struct { Function string `json:"func"` File string `json:"file"` Line int `json:"line"` } // IsValid 表示是否有效 func (frame Frame) IsValid() bool { return frame.Line > 0 } // GetFilename 返回文件名 func (frame Frame) GetFilename() string { _, file := filepath.Split(frame.File) return file } // SplitFilePath 返回 File 对应的路径和文件名 func (frame Frame) SplitFilePath() (string, string) { return filepath.Split(frame.File) } // SplitFunction 返回 Function 对应的包路径和函数名 func (frame Frame) SplitFunction() (string, string) { return PackagePathQualifiedFunctionName(frame.Function).Split() } // Caller 返回当前调用对应的 Frame, // skip=0 表示调用 Caller 处. func Caller(skip int) Frame { pc := make([]uintptr, 1) n := runtime.Callers(skip+callerSkipOffset, pc) frame, _ := runtime.CallersFrames(pc[:n]).Next() if frame.PC == 0 { return Frame{} } return Frame{ Function: frame.Function, File: frame.File, Line: frame.Line, } } // Stack 返回调用栈, // skip=0 表示调用 Stack 处. func Stack(skip, maximumFrames int) []Frame { pc := make([]uintptr, maximumFrames) n := runtime.Callers(skip+callerSkipOffset, pc) stack := make([]Frame, 0, n) frames := runtime.CallersFrames(pc[:n]) for { frame, more := frames.Next() if frame.PC != 0 { stack = append(stack, Frame{ Function: frame.Function, File: frame.File, Line: frame.Line, }) } if !more { break } } return stack } // CallerFrame 函数调用对应的 [runtime.Frame], // 从 [runtime.Frame] 中可以获取调用路径上的函数的名字、文件、行号等信息; // skip 值对应的 [runtime.Frame]: // - -1: CallerFrame // - 0: 调用 CallerFrame 处 // // 可以使用 [runtime.Frame.PC] != 0 判断 runtime.Frame 有效 func CallerFrame(skip int) runtime.Frame { pc := make([]uintptr, 1) n := runtime.Callers(skip+callerSkipOffset, pc) frame, _ := runtime.CallersFrames(pc[:n]).Next() return frame } // CallersFrames 返回函数调用对应的 [runtime.Frames], // 从 Frames 中可以获取调用路径上的函数的名字、文件、行号等信息; // skip 值对应的第1个 [runtime.Frame]: // - -2: [runtime.Callers] // - -1: CallersFrames // - 0: 调用 CallersFrames 处 func CallersFrames(skip, maximumFrames int) *runtime.Frames { pc := make([]uintptr, maximumFrames) n := runtime.Callers(skip+callerSkipOffset, pc) return runtime.CallersFrames(pc[:n]) } // PrintCallersFrames 打印函数调用栈, // 从调用 PrintCallersFrames 的地方开始打印 func PrintCallersFrames() { frames := CallersFrames(callerSkipOffset+1, maximumFrames) for { frame, more := frames.Next() if frame.PC != 0 { fmt.Printf("%s:%d in %s\n", frame.File, frame.Line, frame.Function) } if !more { break } } } // PackagePathQualifiedFunctionName 是 [runtime.Frame.Function] (package path-qualified function name) type PackagePathQualifiedFunctionName string // Split 把完整的函数路径切割成包路径和函数名 func (f PackagePathQualifiedFunctionName) Split() (string, string) { var period int loop: for i := len(f) - 1; i >= 0; i-- { switch f[i] { case '/': break loop case '.': period = i } } if period == 0 { return "", "" } return string(f[:period]), string(f[period+1:]) } // PackageName 把完整的函数路径切割成包路径和函数名 func (f PackagePathQualifiedFunctionName) PackageName() string { var period int for i := len(f) - 1; i >= 0; i-- { switch f[i] { case '/': return string(f[:period]) case '.': period = i } } return string(f[:period]) } // SplitPackageFunctionName 把完整的函数路径切割成包路径和函数名 // 参数 f 是 [runtime.Frame.Function] (package path-qualified function name), func SplitPackageFunctionName(f string) (string, string) { return PackagePathQualifiedFunctionName(f).Split() } // GetPackageName 把完整的函数路径切割成包路径和函数名 // 参数 f 是 [runtime.Frame.Function] (package path-qualified function name), func GetPackageName(f string) string { return PackagePathQualifiedFunctionName(f).PackageName() }