You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
4.3 KiB
Go
168 lines
4.3 KiB
Go
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()
|
|
}
|