mirror of https://github.com/golang/go.git
errors: record only single frame
See Issue #29382 and Issue #30468. 3 frames are no longer needed as of https://go-review.googlesource.com/c/go/+/152537/ name old time/op new time/op delta New-8 475ns ± 3% 352ns ± 2% -25.87% (p=0.008 n=5+5) Errorf/no_format-8 661ns ± 4% 558ns ± 2% -15.63% (p=0.008 n=5+5) Errorf/with_format-8 729ns ± 6% 626ns ± 2% -14.23% (p=0.008 n=5+5) Errorf/method:_mytype-8 1.00µs ± 9% 0.84µs ± 2% -15.94% (p=0.008 n=5+5) Errorf/method:_number-8 1.25µs ± 7% 1.04µs ± 2% -16.38% (p=0.008 n=5+5) Change-Id: I30377e769b3b3be623f63ecbe365f8950ca08dda Reviewed-on: https://go-review.googlesource.com/c/go/+/167400 Run-TryBot: Marcel van Lohuizen <mpvl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
b6fb6673bc
commit
5402854c35
|
|
@ -10,10 +10,7 @@ import (
|
||||||
|
|
||||||
// A Frame contains part of a call stack.
|
// A Frame contains part of a call stack.
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
// Make room for three PCs: the one we were asked for, what it called,
|
frames [1]uintptr
|
||||||
// and possibly a PC for skipPleaseUseCallersFrames. See:
|
|
||||||
// https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169
|
|
||||||
frames [3]uintptr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Caller returns a Frame that describes a frame on the caller's stack.
|
// Caller returns a Frame that describes a frame on the caller's stack.
|
||||||
|
|
@ -21,7 +18,7 @@ type Frame struct {
|
||||||
// Caller(0) returns the frame for the caller of Caller.
|
// Caller(0) returns the frame for the caller of Caller.
|
||||||
func Caller(skip int) Frame {
|
func Caller(skip int) Frame {
|
||||||
var s Frame
|
var s Frame
|
||||||
runtime.Callers(skip+1, s.frames[:])
|
runtime.Callers(skip+2, s.frames[:])
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,13 +27,7 @@ func Caller(skip int) Frame {
|
||||||
// The returned function may be "" even if file and line are not.
|
// The returned function may be "" even if file and line are not.
|
||||||
func (f Frame) location() (function, file string, line int) {
|
func (f Frame) location() (function, file string, line int) {
|
||||||
frames := runtime.CallersFrames(f.frames[:])
|
frames := runtime.CallersFrames(f.frames[:])
|
||||||
if _, ok := frames.Next(); !ok {
|
fr, _ := frames.Next()
|
||||||
return "", "", 0
|
|
||||||
}
|
|
||||||
fr, ok := frames.Next()
|
|
||||||
if !ok {
|
|
||||||
return "", "", 0
|
|
||||||
}
|
|
||||||
return fr.Function, fr.File, fr.Line
|
return fr.Function, fr.File, fr.Line
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,42 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestFrame(t *testing.T) {
|
||||||
|
|
||||||
|
// Extra line
|
||||||
|
got := fmt.Sprintf("%+v", errors.New("Test"))
|
||||||
|
got = got[strings.Index(got, "Test"):]
|
||||||
|
const want = "^Test:" +
|
||||||
|
"\n errors_test.TestFrame" +
|
||||||
|
"\n .*/errors/frame_test.go:20$"
|
||||||
|
ok, err := regexp.MatchString(want, got)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("\n got %v;\nwant %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type myType struct{}
|
type myType struct{}
|
||||||
|
|
||||||
func (myType) Format(s fmt.State, v rune) {
|
func (myType) Format(s fmt.State, v rune) {
|
||||||
s.Write(bytes.Repeat([]byte("Hi! "), 10))
|
s.Write(bytes.Repeat([]byte("Hi! "), 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkNew(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_ = errors.New("new error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkErrorf(b *testing.B) {
|
func BenchmarkErrorf(b *testing.B) {
|
||||||
err := errors.New("foo")
|
err := errors.New("foo")
|
||||||
// pi := big.NewFloat(3.14) // Something expensive.
|
|
||||||
num := big.NewInt(5)
|
|
||||||
args := func(a ...interface{}) []interface{} { return a }
|
args := func(a ...interface{}) []interface{} { return a }
|
||||||
benchCases := []struct {
|
benchCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
|
@ -30,8 +53,8 @@ func BenchmarkErrorf(b *testing.B) {
|
||||||
}{
|
}{
|
||||||
{"no_format", "msg: %v", args(err)},
|
{"no_format", "msg: %v", args(err)},
|
||||||
{"with_format", "failed %d times: %v", args(5, err)},
|
{"with_format", "failed %d times: %v", args(5, err)},
|
||||||
{"method: mytype", "pi: %v", args("myfile.go", myType{}, err)},
|
{"method: mytype", "pi %s %v: %v", args("myfile.go", myType{}, err)},
|
||||||
{"method: number", "pi: %v", args("myfile.go", num, err)},
|
{"method: number", "pi %s %d: %v", args("myfile.go", big.NewInt(5), err)},
|
||||||
}
|
}
|
||||||
for _, bc := range benchCases {
|
for _, bc := range benchCases {
|
||||||
b.Run(bc.name, func(b *testing.B) {
|
b.Run(bc.name, func(b *testing.B) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue