// Copyright Earl Warren // Copyright Loïc Dachary // 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 }