parent
							
								
									521fee9817
								
							
						
					
					
						commit
						ff69d51e50
					
				@ -0,0 +1,3 @@
 | 
			
		||||
module git.blauwelle.com/go/crate/runtimehelper
 | 
			
		||||
 | 
			
		||||
go 1.20
 | 
			
		||||
@ -0,0 +1,162 @@
 | 
			
		||||
package runtimehelper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// 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+2, 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+2, 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+2, 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+2, pc)
 | 
			
		||||
	return runtime.CallersFrames(pc[:n])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrintCallersFrames 打印函数调用栈,
 | 
			
		||||
// 从调用 PrintCallersFrames 的地方开始打印
 | 
			
		||||
func PrintCallersFrames() {
 | 
			
		||||
	frames := CallersFrames(3, 32)
 | 
			
		||||
	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()
 | 
			
		||||
}
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue