80 lines
1.4 KiB
Go
80 lines
1.4 KiB
Go
//go:build js && wasm
|
|
|
|
// Package stackerr adds stack traces to verbose error messages.
|
|
package stackerr
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
type stackError struct {
|
|
err error
|
|
stack *stacktrace
|
|
}
|
|
|
|
// WithStack returns 'err' with a stack trace in its verbose formatter output.
|
|
// Returns nil if err is nil.
|
|
func WithStack(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
return &stackError{
|
|
err: err,
|
|
stack: collectStacktrace(3),
|
|
}
|
|
}
|
|
|
|
func (s *stackError) Error() string {
|
|
return s.err.Error()
|
|
}
|
|
|
|
func (s *stackError) Unwrap() error {
|
|
return s.err
|
|
}
|
|
|
|
func (s *stackError) Format(f fmt.State, verb rune) {
|
|
switch verb {
|
|
case 'v':
|
|
if f.Flag('+') {
|
|
fmt.Fprintf(f, "%+v\n%s", s.err, s.stack)
|
|
return
|
|
}
|
|
fmt.Fprint(f, s.Error())
|
|
case 's':
|
|
fmt.Fprint(f, s.Error())
|
|
case 'q':
|
|
fmt.Fprintf(f, "%q", s.Error())
|
|
}
|
|
}
|
|
|
|
type stacktrace struct {
|
|
callers []uintptr
|
|
}
|
|
|
|
func collectStacktrace(skip int) *stacktrace {
|
|
const (
|
|
maxFrames = 32
|
|
)
|
|
pc := make([]uintptr, maxFrames)
|
|
n := runtime.Callers(1+skip, pc)
|
|
return &stacktrace{
|
|
callers: pc[:n],
|
|
}
|
|
}
|
|
|
|
func (s *stacktrace) String() string {
|
|
var sb strings.Builder
|
|
frames := runtime.CallersFrames(s.callers)
|
|
for frame, next := frames.Next(); next; frame, next = frames.Next() {
|
|
funcName := "unknown"
|
|
if frame.Func != nil {
|
|
funcName = frame.Func.Name()
|
|
}
|
|
sb.WriteString(fmt.Sprintf("%s\n\t%s:%d\n", funcName, frame.File, frame.Line))
|
|
}
|
|
return sb.String()
|
|
}
|