Merge pull request #193 from pkg/fixedbugs/188
Return errors.Frame to a uintptr
diff --git a/format_test.go b/format_test.go
index fd35ca3..cb1df82 100644
--- a/format_test.go
+++ b/format_test.go
@@ -385,6 +385,7 @@
}
func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
+ t.Helper()
got := fmt.Sprintf(format, arg)
gotLines := strings.SplitN(got, "\n", -1)
wantLines := strings.SplitN(want, "\n", -1)
diff --git a/stack.go b/stack.go
index 5be4627..ddc8c30 100644
--- a/stack.go
+++ b/stack.go
@@ -11,7 +11,42 @@
)
// 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 +70,16 @@
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, ":")
@@ -79,9 +105,9 @@
switch {
case s.Flag('+'):
b.Grow(len(st) * stackMinLen)
- for _, fr := range st {
+ for _, f := range st {
b.WriteByte('\n')
- fr.format(&b, s, verb)
+ f.format(&b, s, verb)
}
case s.Flag('#'):
fmt.Fprintf(&b, "%#v", []Frame(st))
@@ -125,29 +151,20 @@
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 {
diff --git a/stack_test.go b/stack_test.go
index 7f76694..172a57d 100644
--- a/stack_test.go
+++ b/stack_test.go
@@ -35,11 +35,11 @@
"github.com/pkg/errors.init\n" +
"\t.+/github.com/pkg/errors/stack_test.go",
}, {
- Frame{},
+ 0,
"%s",
"unknown",
}, {
- Frame{},
+ 0,
"%+s",
"unknown",
}, {
@@ -47,7 +47,7 @@
"%d",
"9",
}, {
- Frame{},
+ 0,
"%d",
"0",
}, {
@@ -69,7 +69,7 @@
"%n",
"X.val",
}, {
- Frame{},
+ 0,
"%n",
"",
}, {
@@ -82,7 +82,7 @@
"github.com/pkg/errors.init\n" +
"\t.+/github.com/pkg/errors/stack_test.go:9",
}, {
- Frame{},
+ 0,
"%v",
"unknown:0",
}}
@@ -246,7 +246,7 @@
n := runtime.Callers(2, pcs[:])
frames := runtime.CallersFrames(pcs[:n])
frame, _ := frames.Next()
- return Frame(frame)
+ return Frame(frame.PC)
}
//go:noinline