Commit 5402854c authored by Marcel van Lohuizen's avatar Marcel van Lohuizen

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: default avatarDamien Neil <dneil@google.com>
parent b6fb6673
...@@ -10,10 +10,7 @@ import ( ...@@ -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 { ...@@ -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 { ...@@ -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 ( ...@@ -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) { ...@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment