Initial QSfera import
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
# GoInfo
|
||||
|
||||
`goutil/x/goinfo` provide some useful info for golang.
|
||||
|
||||
> Github: https://github.com/gookit/goutil
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/gookit/goutil/x/goinfo
|
||||
```
|
||||
|
||||
## Go docs
|
||||
|
||||
- [Go docs](https://pkg.go.dev/github.com/gookit/goutil)
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
gover := goinfo.GoVersion() // eg: "1.15.6"
|
||||
|
||||
```
|
||||
|
||||
## Testings
|
||||
|
||||
```shell
|
||||
go test -v ./goinfo/...
|
||||
```
|
||||
|
||||
Test limit by regexp:
|
||||
|
||||
```shell
|
||||
go test -v -run ^TestSetByKeys ./goinfo/...
|
||||
```
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package goinfo
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/gookit/goutil/strutil"
|
||||
)
|
||||
|
||||
// FullFcName struct.
|
||||
type FullFcName struct {
|
||||
// FullName eg: "github.com/gookit/goutil/x/goinfo.PanicIf"
|
||||
FullName string
|
||||
pkgPath string // "github.com/gookit/goutil/x/goinfo"
|
||||
pkgName string // "goinfo"
|
||||
funcName string // "PanicIf"
|
||||
}
|
||||
|
||||
// Parse the full func name.
|
||||
func (ffn *FullFcName) Parse() {
|
||||
if ffn.funcName != "" {
|
||||
return
|
||||
}
|
||||
|
||||
i := strings.LastIndex(ffn.FullName, "/")
|
||||
|
||||
ffn.pkgPath = ffn.FullName[:i+1]
|
||||
// spilt get pkg and func name
|
||||
ffn.pkgName, ffn.funcName = strutil.MustCut(ffn.FullName[i+1:], ".")
|
||||
ffn.pkgPath += ffn.pkgName
|
||||
}
|
||||
|
||||
// PkgPath string get. eg: github.com/gookit/goutil/x/goinfo
|
||||
func (ffn *FullFcName) PkgPath() string {
|
||||
ffn.Parse()
|
||||
return ffn.pkgPath
|
||||
}
|
||||
|
||||
// PkgName string get. eg: goinfo
|
||||
func (ffn *FullFcName) PkgName() string {
|
||||
ffn.Parse()
|
||||
return ffn.pkgName
|
||||
}
|
||||
|
||||
// FuncName get short func name. eg: PanicIf
|
||||
func (ffn *FullFcName) FuncName() string {
|
||||
ffn.Parse()
|
||||
return ffn.funcName
|
||||
}
|
||||
|
||||
// String get full func name string, pkg path and func name.
|
||||
func (ffn *FullFcName) String() string {
|
||||
return ffn.FullName
|
||||
}
|
||||
|
||||
// FuncName get full func name, contains pkg path.
|
||||
//
|
||||
// eg:
|
||||
//
|
||||
// // OUTPUT: github.com/gookit/goutil/x/goinfo.PkgName
|
||||
// goinfo.FuncName(goinfo.PkgName)
|
||||
func FuncName(fn any) string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
||||
}
|
||||
|
||||
// CutFuncName get pkg path and short func name
|
||||
// eg:
|
||||
//
|
||||
// "github.com/gookit/goutil/x/goinfo.FuncName" => [github.com/gookit/goutil/x/goinfo, FuncName]
|
||||
func CutFuncName(fullFcName string) (pkgPath, shortFnName string) {
|
||||
ffn := FullFcName{FullName: fullFcName}
|
||||
return ffn.PkgPath(), ffn.FuncName()
|
||||
}
|
||||
|
||||
// PkgName get current package name
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// fullFcName := goinfo.FuncName(fn)
|
||||
// pgkName := goinfo.PkgName(fullFcName)
|
||||
func PkgName(fullFcName string) string {
|
||||
for {
|
||||
lastPeriod := strings.LastIndex(fullFcName, ".")
|
||||
lastSlash := strings.LastIndex(fullFcName, "/")
|
||||
|
||||
if lastPeriod > lastSlash {
|
||||
fullFcName = fullFcName[:lastPeriod]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return fullFcName
|
||||
}
|
||||
|
||||
// GoodFuncName reports whether the function name is a valid identifier.
|
||||
func GoodFuncName(name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, r := range name {
|
||||
switch {
|
||||
case r == '_':
|
||||
case i == 0 && !unicode.IsLetter(r):
|
||||
return false
|
||||
case !unicode.IsLetter(r) && !unicode.IsDigit(r):
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Package goinfo provide some standard util functions for go.
|
||||
package goinfo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoVersion get go runtime version. eg: "1.18.2"
|
||||
func GoVersion() string {
|
||||
return runtime.Version()[2:]
|
||||
}
|
||||
|
||||
// GoInfo define
|
||||
//
|
||||
// On os by:
|
||||
//
|
||||
// go env GOVERSION GOOS GOARCH
|
||||
// go version // "go version go1.19 darwin/amd64"
|
||||
type GoInfo struct {
|
||||
Version string
|
||||
GoOS string
|
||||
Arch string
|
||||
}
|
||||
|
||||
// match "go version go1.19 darwin/amd64"
|
||||
var goVerRegex = regexp.MustCompile(`\sgo([\d.]+)\s(\w+)/(\w+)`)
|
||||
|
||||
// ParseGoVersion get info by parse `go version` results.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// line, err := sysutil.ExecLine("go version")
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// info, err := goinfo.ParseGoVersion()
|
||||
// dump.P(info)
|
||||
func ParseGoVersion(line string) (*GoInfo, error) {
|
||||
// eg: [" go1.19 darwin/amd64", "1.19", "darwin", "amd64"]
|
||||
lines := goVerRegex.FindStringSubmatch(line)
|
||||
if len(lines) != 4 {
|
||||
return nil, errors.New("input go version info is invalid")
|
||||
}
|
||||
|
||||
info := &GoInfo{
|
||||
Version: strings.TrimPrefix(lines[1], "go"),
|
||||
}
|
||||
info.GoOS = lines[2]
|
||||
info.Arch = lines[3]
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// OsGoInfo fetch and parse
|
||||
func OsGoInfo() (*GoInfo, error) {
|
||||
cmdArgs := []string{"env", "GOVERSION", "GOOS", "GOARCH"}
|
||||
bs, err := exec.Command("go", cmdArgs...).Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(bs)), "\n")
|
||||
if len(lines) != len(cmdArgs)-1 {
|
||||
return nil, errors.New("returns go info is not full")
|
||||
}
|
||||
|
||||
info := &GoInfo{}
|
||||
info.Version = strings.TrimPrefix(lines[0], "go")
|
||||
info.GoOS = lines[1]
|
||||
info.Arch = lines[2]
|
||||
|
||||
return info, nil
|
||||
}
|
||||
+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