| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- //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()
- }
|