wide/util/panic.go

105 lines
2.8 KiB
Go
Raw Normal View History

2017-03-14 17:40:45 +03:00
// Copyright (c) 2014-2017, b3log.org & hacpai.com
2014-11-20 09:00:11 +03:00
//
2014-11-12 18:13:14 +03:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-11-20 09:00:11 +03:00
//
2014-11-12 18:13:14 +03:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-11-20 09:00:11 +03:00
//
2014-11-12 18:13:14 +03:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-09-26 13:36:12 +04:00
package util
import (
2014-12-21 06:59:12 +03:00
"bytes"
"fmt"
"io/ioutil"
2014-12-13 13:47:41 +03:00
"os"
2014-12-21 06:59:12 +03:00
"runtime"
2014-09-26 13:36:12 +04:00
2014-12-13 13:47:41 +03:00
"github.com/b3log/wide/log"
2014-09-26 13:36:12 +04:00
)
2014-12-13 13:47:41 +03:00
// Logger.
var logger = log.NewLogger(os.Stdout)
2014-12-21 06:59:12 +03:00
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
slash = []byte("/")
)
2014-10-29 13:15:18 +03:00
// Recover recovers a panic.
2014-09-26 13:36:12 +04:00
func Recover() {
2014-11-20 09:00:11 +03:00
if re := recover(); nil != re {
2014-12-21 06:59:12 +03:00
stack := stack()
logger.Errorf("PANIC RECOVERED: %v\n\t%s\n", re, stack)
}
}
2014-12-21 07:07:33 +03:00
// stack implements Stack, skipping 2 frames.
2014-12-21 06:59:12 +03:00
func stack() []byte {
2015-03-18 16:43:29 +03:00
buf := &bytes.Buffer{} // the returned data
2014-12-21 06:59:12 +03:00
// As we loop, we open files and read them. These variables record the currently
// loaded file.
var lines [][]byte
var lastFile string
for i := 2; ; i++ { // Caller we care about is the user, 2 frames up
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
if file != lastFile {
data, err := ioutil.ReadFile(file)
if err != nil {
continue
}
lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
}
return buf.Bytes()
}
// source returns a space-trimmed slice of the n'th line.
func source(lines [][]byte, n int) []byte {
if n < 0 || n >= len(lines) {
return dunno
}
return bytes.Trim(lines[n], " \t")
}
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
// Since the package path might contains dots (e.g. code.google.com/...),
// we first remove the path prefix if there is one.
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
2014-09-26 13:36:12 +04:00
}
2014-12-21 06:59:12 +03:00
name = bytes.Replace(name, centerDot, dot, -1)
return name
2014-09-26 13:36:12 +04:00
}