Initial QSfera import
This commit is contained in:
+166
@@ -0,0 +1,166 @@
|
||||
package goinfo
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gookit/goutil/x/basefn"
|
||||
)
|
||||
|
||||
// some commonly consts
|
||||
var (
|
||||
DefStackLen = 10000
|
||||
MaxStackLen = 100000
|
||||
)
|
||||
|
||||
// GetCallStacks stacks is a wrapper for runtime.
|
||||
// If all is true, Stack that attempts to recover the data for all goroutines.
|
||||
//
|
||||
// from glog package
|
||||
func GetCallStacks(all bool) []byte {
|
||||
// We don't know how big the traces are, so grow a few times if they don't fit.
|
||||
// Start large, though.
|
||||
n := DefStackLen
|
||||
if all {
|
||||
n = MaxStackLen
|
||||
}
|
||||
|
||||
// 4<<10 // 4 KB should be enough
|
||||
var trace []byte
|
||||
for i := 0; i < 10; i++ {
|
||||
trace = make([]byte, n)
|
||||
bts := runtime.Stack(trace, all)
|
||||
if bts < len(trace) {
|
||||
return trace[:bts]
|
||||
}
|
||||
n *= 2
|
||||
}
|
||||
return trace
|
||||
}
|
||||
|
||||
// GetCallerInfo get caller func name and with base filename and line.
|
||||
//
|
||||
// returns:
|
||||
//
|
||||
// github.com/gookit/goutil/x/goinfo_test.someFunc2(),stack_test.go:26
|
||||
func GetCallerInfo(skip int) string {
|
||||
skip++ // ignore current func
|
||||
cs := GetCallersInfo(skip, skip+1)
|
||||
return basefn.FirstOr(cs, "")
|
||||
}
|
||||
|
||||
// SimpleCallersInfo returns an array of strings containing
|
||||
// the func name, file and line number of each stack frame leading.
|
||||
func SimpleCallersInfo(skip, num int) []string {
|
||||
skip++ // ignore current func
|
||||
return GetCallersInfo(skip, skip+num)
|
||||
}
|
||||
|
||||
// GetCallersInfo returns an array of strings containing
|
||||
// the func name, file and line number of each stack frame leading.
|
||||
//
|
||||
// NOTICE: max should > skip
|
||||
func GetCallersInfo(skip, max int) []string {
|
||||
var (
|
||||
pc uintptr
|
||||
ok bool
|
||||
line int
|
||||
file, name string
|
||||
)
|
||||
|
||||
callers := make([]string, 0, max-skip)
|
||||
for i := skip; i < max; i++ {
|
||||
pc, file, line, ok = runtime.Caller(i)
|
||||
if !ok {
|
||||
// The breaks below failed to terminate the loop, and we ran off the
|
||||
// end of the call stack.
|
||||
break
|
||||
}
|
||||
|
||||
// This is a huge edge case, but it will panic if this is the case
|
||||
if file == "<autogenerated>" {
|
||||
break
|
||||
}
|
||||
|
||||
fc := runtime.FuncForPC(pc)
|
||||
if fc == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.ContainsRune(file, '/') {
|
||||
name = fc.Name()
|
||||
file = filepath.Base(file)
|
||||
// eg: github.com/gookit/goutil/x/goinfo_test.someFunc2(),stack_test.go:26
|
||||
callers = append(callers, name+"(),"+file+":"+strconv.Itoa(line))
|
||||
}
|
||||
|
||||
// Drop the package
|
||||
// segments := strings.Split(name, ".")
|
||||
// name = segments[len(segments)-1]
|
||||
}
|
||||
|
||||
return callers
|
||||
}
|
||||
|
||||
// CallerInfo struct
|
||||
type CallerInfo struct {
|
||||
PC uintptr
|
||||
Fc *runtime.Func
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
// String convert
|
||||
func (ci *CallerInfo) String() string {
|
||||
return ci.File + ":" + strconv.Itoa(ci.Line)
|
||||
}
|
||||
|
||||
// CallerFilterFunc type
|
||||
type CallerFilterFunc func(file string, fc *runtime.Func) bool
|
||||
|
||||
// CallersInfos returns an array of the CallerInfo, can with filters
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// cs := sysutil.CallersInfos(3, 2)
|
||||
// for _, ci := range cs {
|
||||
// fc := runtime.FuncForPC(pc)
|
||||
// // maybe need check fc = nil
|
||||
// fnName = fc.Name()
|
||||
// }
|
||||
func CallersInfos(skip, num int, filters ...CallerFilterFunc) []*CallerInfo {
|
||||
filterLn := len(filters)
|
||||
callers := make([]*CallerInfo, 0, num)
|
||||
for i := skip; i < skip+num; i++ {
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
// The breaks below failed to terminate the loop, and we ran off the
|
||||
// end of the call stack.
|
||||
break
|
||||
}
|
||||
|
||||
fc := runtime.FuncForPC(pc)
|
||||
if fc == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if filterLn > 0 && filters[0] != nil {
|
||||
// filter - return false for skip
|
||||
if !filters[0](file, fc) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// collecting
|
||||
callers = append(callers, &CallerInfo{
|
||||
PC: pc,
|
||||
Fc: fc,
|
||||
File: file,
|
||||
Line: line,
|
||||
})
|
||||
}
|
||||
|
||||
return callers
|
||||
}
|
||||
Reference in New Issue
Block a user