Skip to content

Commit

Permalink
Make Stacker easier for other types to implement.
Browse files Browse the repository at this point in the history
  • Loading branch information
bobg committed Feb 3, 2024
1 parent 8912860 commit d023c1a
Showing 1 changed file with 30 additions and 28 deletions.
58 changes: 30 additions & 28 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,9 @@ func (w *wrapped) Error() string {
return w.err.Error()
}

// Stack returns a slice of [Frame]
// representing the call stack at the point [Wrap] was called.
func (w *wrapped) Stack() Frames {
var (
rframes = runtime.CallersFrames(w.stack)
result []Frame
)

for {
rframe, more := rframes.Next()
if rframe.PC == 0 {
break
}
result = append(result, Frame{
Function: rframe.Function,
File: rframe.File,
Line: rframe.Line,
})
if !more {
break
}
}

return result
// Stack implements the [Stacker] interface.
func (w *wrapped) Stack() []uintptr {
return w.stack
}

func dowrap(err error) error {
Expand All @@ -64,19 +43,20 @@ func dowrap(err error) error {

const maxdepth = 32
var pcs [maxdepth]uintptr
n := runtime.Callers(3, pcs[:]) // skip runtime.Callers, dowrap, and the Wrap/Wrapf/New/Newf call that got us here.
n := runtime.Callers(3, pcs[:]) // 3 skips runtime.Callers, dowrap (this function), and the Wrap/Wrapf/New/Newf call that got us here.
return &wrapped{
err: err,
stack: pcs[:n],
}
}

// Stacker is the interface implemented by errors with a stack trace.
// Types wishing to implement Stacker can use [runtime.Callers] to get the call stack.
type Stacker interface {
Stack() Frames
Stack() []uintptr
}

// Stack returns the stack trace from an error.
// Stack returns the stack trace from an error as a [Frames].
// If the error is nil or does not contain a stack trace,
// Stack returns nil.
func Stack(e error) Frames {
Expand All @@ -88,7 +68,29 @@ func Stack(e error) Frames {
if !As(e, &s) {
return nil
}
return s.Stack()

var (
pcs = s.Stack()
rframes = runtime.CallersFrames(pcs)
result []Frame
)

for {
rframe, more := rframes.Next()
if rframe.PC == 0 {
break
}
result = append(result, Frame{
Function: rframe.Function,
File: rframe.File,
Line: rframe.Line,
})
if !more {
break
}
}

return result
}

// Frames is a slice of [Frame].
Expand Down

0 comments on commit d023c1a

Please sign in to comment.