99 lines
2 KiB
Go
99 lines
2 KiB
Go
|
// Copyright Earl Warren <contact@earl-warren.org>
|
||
|
// Copyright Loïc Dachary <loic@dachary.org>
|
||
|
// SPDX-License-Identifier: MIT
|
||
|
|
||
|
package util
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
var packagePrefix string
|
||
|
|
||
|
func init() {
|
||
|
_, filename, _, _ := runtime.Caller(0)
|
||
|
packagePrefix = strings.TrimSuffix(filename, "util/panic.go")
|
||
|
if packagePrefix == filename {
|
||
|
// in case the source code file is moved, we can not trim the suffix, the code above should also be updated.
|
||
|
panic("unable to detect correct package prefix, please update file: " + filename)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getStack() string {
|
||
|
callersLength := 5
|
||
|
var callers []uintptr
|
||
|
var callersCount int
|
||
|
for {
|
||
|
callers = make([]uintptr, callersLength)
|
||
|
callersCount = runtime.Callers(4, callers)
|
||
|
if callersCount <= 0 {
|
||
|
panic("runtime.Callers <= 0")
|
||
|
}
|
||
|
if callersCount >= callersLength {
|
||
|
callersLength *= 2
|
||
|
} else {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
callers = callers[:callersCount]
|
||
|
frames := runtime.CallersFrames(callers)
|
||
|
stack := make([]string, 0, 10)
|
||
|
for {
|
||
|
frame, more := frames.Next()
|
||
|
if strings.HasPrefix(frame.File, packagePrefix) {
|
||
|
file := strings.TrimPrefix(frame.File, packagePrefix)
|
||
|
if file == "util/panic.go" || file == "util/terminate.go" && more {
|
||
|
continue
|
||
|
}
|
||
|
var function string
|
||
|
dot := strings.LastIndex(frame.Function, ".")
|
||
|
if dot >= 0 {
|
||
|
function = frame.Function[dot+1:]
|
||
|
}
|
||
|
stack = append(stack, fmt.Sprintf("%s:%d:%s", file, frame.Line, function))
|
||
|
}
|
||
|
if !more {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
return strings.Join(stack, "\n") + "\n"
|
||
|
}
|
||
|
|
||
|
type PanicError interface {
|
||
|
error
|
||
|
Stack() string
|
||
|
}
|
||
|
|
||
|
type panicError struct {
|
||
|
message string
|
||
|
stack string
|
||
|
}
|
||
|
|
||
|
func (o panicError) Error() string { return o.message }
|
||
|
func (o panicError) Stack() string { return o.stack }
|
||
|
|
||
|
func PanicToError(fun func()) (err PanicError) {
|
||
|
defer func() {
|
||
|
if r := recover(); r != nil {
|
||
|
recoveredErr, ok := r.(error)
|
||
|
var message string
|
||
|
if ok {
|
||
|
message = recoveredErr.Error()
|
||
|
}
|
||
|
err = panicError{
|
||
|
message: message,
|
||
|
stack: getStack(),
|
||
|
}
|
||
|
if !ok {
|
||
|
panic(r)
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
fun()
|
||
|
|
||
|
return err
|
||
|
}
|