117 lines
2.5 KiB
Go
117 lines
2.5 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"runtime"
|
|
|
|
"encoding/json"
|
|
"fmt"
|
|
"mongo.games.com/goserver/core/logger"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var _panicStackMgr = &PanicStackMgr{
|
|
items: make(map[string]*PanicStackInfo),
|
|
}
|
|
|
|
type PanicStackMgr struct {
|
|
sync.RWMutex
|
|
items map[string]*PanicStackInfo
|
|
}
|
|
|
|
type PanicStackInfo struct {
|
|
FirstTime time.Time
|
|
LastTime time.Time
|
|
Times int64
|
|
ErrorMsg string
|
|
StackBuf string
|
|
}
|
|
|
|
func DumpStackIfPanic(f string) {
|
|
if err := recover(); err != nil {
|
|
defer func() { //防止二次panic
|
|
if err := recover(); err != nil {
|
|
logger.Logger.Error(f, " panic.panic,error=", err)
|
|
}
|
|
}()
|
|
logger.Logger.Error(f, " panic,error=", err)
|
|
errMsg := fmt.Sprintf("%v", err)
|
|
var buf [4096]byte
|
|
n := runtime.Stack(buf[:], false)
|
|
logger.Logger.Error("stack--->", string(buf[:n]))
|
|
stk := make([]uintptr, 32)
|
|
m := runtime.Callers(0, stk[:])
|
|
stk = stk[:m]
|
|
if len(stk) > 0 {
|
|
d, err := json.Marshal(stk)
|
|
if err == nil && len(d) > 0 {
|
|
key := string(d)
|
|
_panicStackMgr.Lock()
|
|
defer _panicStackMgr.Unlock()
|
|
tNow := time.Now()
|
|
if ps, exist := _panicStackMgr.items[key]; exist {
|
|
atomic.AddInt64(&ps.Times, 1)
|
|
ps.LastTime = tNow
|
|
} else {
|
|
ps = &PanicStackInfo{
|
|
ErrorMsg: errMsg,
|
|
Times: 1,
|
|
StackBuf: string(buf[:n]),
|
|
FirstTime: tNow,
|
|
LastTime: tNow,
|
|
}
|
|
_panicStackMgr.items[key] = ps
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func DumpStack(f string) {
|
|
logger.Logger.Error(f)
|
|
var buf [4096]byte
|
|
len := runtime.Stack(buf[:], false)
|
|
logger.Logger.Error("stack--->", string(buf[:len]))
|
|
}
|
|
|
|
func GetPanicStats() map[string]PanicStackInfo {
|
|
stats := make(map[string]PanicStackInfo)
|
|
_panicStackMgr.RLock()
|
|
defer _panicStackMgr.RUnlock()
|
|
for k, v := range _panicStackMgr.items {
|
|
stats[k] = *v
|
|
}
|
|
return stats
|
|
}
|
|
|
|
var RecoverPanicFunc func(args ...interface{})
|
|
|
|
func init() {
|
|
bufPool := &sync.Pool{
|
|
New: func() interface{} {
|
|
return &bytes.Buffer{}
|
|
},
|
|
}
|
|
RecoverPanicFunc = func(args ...interface{}) {
|
|
if r := recover(); r != nil {
|
|
buf := bufPool.Get().(*bytes.Buffer)
|
|
defer bufPool.Put(buf)
|
|
buf.Reset()
|
|
buf.WriteString(fmt.Sprintf("panic: %v\n", r))
|
|
for _, v := range args {
|
|
buf.WriteString(fmt.Sprintf("%v\n", v))
|
|
}
|
|
pcs := make([]uintptr, 10)
|
|
n := runtime.Callers(3, pcs)
|
|
frames := runtime.CallersFrames(pcs[:n])
|
|
for f, again := frames.Next(); again; f, again = frames.Next() {
|
|
buf.WriteString(fmt.Sprintf("%v:%v %v\n", f.File, f.Line, f.Function))
|
|
}
|
|
fmt.Fprint(os.Stderr, buf.String())
|
|
}
|
|
}
|
|
}
|