goserver_sync/core/utils/panic.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())
}
}
}