|
|
@@ -1,7 +1,6 @@
|
|
|
package errors
|
|
|
|
|
|
import (
|
|
|
- "bytes"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"path"
|
|
|
@@ -11,7 +10,42 @@ import (
|
|
|
)
|
|
|
|
|
|
// Frame represents a program counter inside a stack frame.
|
|
|
-type Frame runtime.Frame
|
|
|
+type Frame uintptr
|
|
|
+
|
|
|
+// pc returns the program counter for this frame;
|
|
|
+// multiple frames may have the same PC value.
|
|
|
+func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
|
|
+
|
|
|
+// file returns the full path to the file that contains the
|
|
|
+// function for this Frame's pc.
|
|
|
+func (f Frame) file() string {
|
|
|
+ fn := runtime.FuncForPC(f.pc())
|
|
|
+ if fn == nil {
|
|
|
+ return "unknown"
|
|
|
+ }
|
|
|
+ file, _ := fn.FileLine(f.pc())
|
|
|
+ return file
|
|
|
+}
|
|
|
+
|
|
|
+// line returns the line number of source code of the
|
|
|
+// function for this Frame's pc.
|
|
|
+func (f Frame) line() int {
|
|
|
+ fn := runtime.FuncForPC(f.pc())
|
|
|
+ if fn == nil {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+ _, line := fn.FileLine(f.pc())
|
|
|
+ return line
|
|
|
+}
|
|
|
+
|
|
|
+// name returns the name of this function, if known.
|
|
|
+func (f Frame) name() string {
|
|
|
+ fn := runtime.FuncForPC(f.pc())
|
|
|
+ if fn == nil {
|
|
|
+ return "unknown"
|
|
|
+ }
|
|
|
+ return fn.Name()
|
|
|
+}
|
|
|
|
|
|
// Format formats the frame according to the fmt.Formatter interface.
|
|
|
//
|
|
|
@@ -35,25 +69,16 @@ func (f Frame) format(w io.Writer, s fmt.State, verb rune) {
|
|
|
case 's':
|
|
|
switch {
|
|
|
case s.Flag('+'):
|
|
|
- if f.Function == "" {
|
|
|
- io.WriteString(w, "unknown")
|
|
|
- } else {
|
|
|
- io.WriteString(w, f.Function)
|
|
|
- io.WriteString(w, "\n\t")
|
|
|
- io.WriteString(w, f.File)
|
|
|
- }
|
|
|
+ io.WriteString(w, f.name())
|
|
|
+ io.WriteString(w, "\n\t")
|
|
|
+ io.WriteString(w, f.file())
|
|
|
default:
|
|
|
- file := f.File
|
|
|
- if file == "" {
|
|
|
- file = "unknown"
|
|
|
- }
|
|
|
- io.WriteString(w, path.Base(file))
|
|
|
+ io.WriteString(w, path.Base(f.file()))
|
|
|
}
|
|
|
case 'd':
|
|
|
- io.WriteString(w, strconv.Itoa(f.Line))
|
|
|
+ io.WriteString(w, strconv.Itoa(f.line()))
|
|
|
case 'n':
|
|
|
- name := f.Function
|
|
|
- io.WriteString(s, funcname(name))
|
|
|
+ io.WriteString(w, funcname(f.name()))
|
|
|
case 'v':
|
|
|
f.format(w, s, 's')
|
|
|
io.WriteString(w, ":")
|
|
|
@@ -73,50 +98,23 @@ type StackTrace []Frame
|
|
|
//
|
|
|
// %+v Prints filename, function, and line number for each Frame in the stack.
|
|
|
func (st StackTrace) Format(s fmt.State, verb rune) {
|
|
|
- var b bytes.Buffer
|
|
|
switch verb {
|
|
|
case 'v':
|
|
|
switch {
|
|
|
case s.Flag('+'):
|
|
|
- b.Grow(len(st) * stackMinLen)
|
|
|
- for _, fr := range st {
|
|
|
- b.WriteByte('\n')
|
|
|
- fr.format(&b, s, verb)
|
|
|
+ for _, f := range st {
|
|
|
+ fmt.Fprintf(s, "\n%+v", f)
|
|
|
}
|
|
|
case s.Flag('#'):
|
|
|
- fmt.Fprintf(&b, "%#v", []Frame(st))
|
|
|
+ fmt.Fprintf(s, "%#v", []Frame(st))
|
|
|
default:
|
|
|
- st.formatSlice(&b, s, verb)
|
|
|
+ fmt.Fprintf(s, "%v", []Frame(st))
|
|
|
}
|
|
|
case 's':
|
|
|
- st.formatSlice(&b, s, verb)
|
|
|
+ fmt.Fprintf(s, "%s", []Frame(st))
|
|
|
}
|
|
|
- io.Copy(s, &b)
|
|
|
}
|
|
|
|
|
|
-// formatSlice will format this StackTrace into the given buffer as a slice of
|
|
|
-// Frame, only valid when called with '%s' or '%v'.
|
|
|
-func (st StackTrace) formatSlice(b *bytes.Buffer, s fmt.State, verb rune) {
|
|
|
- b.WriteByte('[')
|
|
|
- if len(st) == 0 {
|
|
|
- b.WriteByte(']')
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- b.Grow(len(st) * (stackMinLen / 4))
|
|
|
- st[0].format(b, s, verb)
|
|
|
- for _, fr := range st[1:] {
|
|
|
- b.WriteByte(' ')
|
|
|
- fr.format(b, s, verb)
|
|
|
- }
|
|
|
- b.WriteByte(']')
|
|
|
-}
|
|
|
-
|
|
|
-// stackMinLen is a best-guess at the minimum length of a stack trace. It
|
|
|
-// doesn't need to be exact, just give a good enough head start for the buffer
|
|
|
-// to avoid the expensive early growth.
|
|
|
-const stackMinLen = 96
|
|
|
-
|
|
|
// stack represents a stack of program counters.
|
|
|
type stack []uintptr
|
|
|
|
|
|
@@ -125,29 +123,20 @@ func (s *stack) Format(st fmt.State, verb rune) {
|
|
|
case 'v':
|
|
|
switch {
|
|
|
case st.Flag('+'):
|
|
|
- frames := runtime.CallersFrames(*s)
|
|
|
- for {
|
|
|
- frame, more := frames.Next()
|
|
|
- fmt.Fprintf(st, "\n%+v", Frame(frame))
|
|
|
- if !more {
|
|
|
- break
|
|
|
- }
|
|
|
+ for _, pc := range *s {
|
|
|
+ f := Frame(pc)
|
|
|
+ fmt.Fprintf(st, "\n%+v", f)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (s *stack) StackTrace() StackTrace {
|
|
|
- var st []Frame
|
|
|
- frames := runtime.CallersFrames(*s)
|
|
|
- for {
|
|
|
- frame, more := frames.Next()
|
|
|
- st = append(st, Frame(frame))
|
|
|
- if !more {
|
|
|
- break
|
|
|
- }
|
|
|
+ f := make([]Frame, len(*s))
|
|
|
+ for i := 0; i < len(f); i++ {
|
|
|
+ f[i] = Frame((*s)[i])
|
|
|
}
|
|
|
- return st
|
|
|
+ return f
|
|
|
}
|
|
|
|
|
|
func callers() *stack {
|