pkg / errors

Simple error handling primitives

Home Page:https://godoc.org/github.com/pkg/errors

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Revert change to errors.Frame's type

davecheney opened this issue · comments

In #183 the definition of errors.Frame was altered to type Frame runtime.Frame. This was necessary because of the changes to mid stack inlining in Go 1.12 and the deprecation of runtime.FuncForPC.

https://go-review.googlesource.com/c/go/+/156364 suggests that it may be possible to continue to use runtime.FuncForPC. If this CL lands #183 should be mostly reverted for the 1.0 release.

A future v2 release would probably drop the use of runtime.FuncForPC and redeclare errors.Frame as an opaque struct.

I agree.. Since we can obtain runtime.Frame from PC's we could have just let good ol' Frame stay there. But I don't know whether it's too late to revert this breaking change or not. Other libraries might have adopted this change :(

.. Since we can obtain runtime.Frame

How would you do that?

As to breaking change on master, I tagged v0.8.1 before I made this change.

using runtime.CallersFrames? maybe?

Thank you for tagging.. much appreciated ^_^

we could alter implementation of format like this:

// format allows stack trace printing calls to be made with a bytes.Buffer.
func (ptrFrame Frame) format(w io.Writer, s fmt.State, verb rune) {
	frames := runtime.CallersFrames([]uintptr{uintptr(ptrFrame)})
	if frames == nil {
		return
	}
	f, _ := frames.Next()

....

I understand. I was merely pointing out that we could obtain an instance of runtime.Frame this way instead of #183 . And we could use runtime.Frame.Function, runtime.Frame.Line.. etc. And I've confirmed that we can accurately convert a single PC to runtime.Frame.

Complete listing is as follows:
Basically it's #183 without changing type of errors.Frame. Instead a we convert a single PC to runtime.Frame and the rest is still #183

// format allows stack trace printing calls to be made with a bytes.Buffer.
func (ptrFrame Frame) format(w io.Writer, s fmt.State, verb rune) {
	frames := runtime.CallersFrames([]uintptr{uintptr(ptrFrame)})
	if frames == nil {
		return
	}
	f, _ := frames.Next()

	switch verb {
	case 's':
		switch {
		case s.Flag('+'):
			fn := runtime.Frame(f).Func
			if fn == nil {
				io.WriteString(w, "unknown")
			} else {
				file := runtime.Frame(f).File
				io.WriteString(w, fn.Name())
				io.WriteString(w, "\n\t")
				io.WriteString(w, file)
			}
		default:
			file := runtime.Frame(f).File
			if file == "" {
				file = "unknown"
			}
			io.WriteString(w, path.Base(file))
		}
	case 'd':
		io.WriteString(w, strconv.Itoa(runtime.Frame(f).Line))
	case 'n':
		name := runtime.Frame(f).Function
		io.WriteString(s, funcname(name))
	case 'v':
		ptrFrame.format(w, s, 's')
		io.WriteString(w, ":")
		ptrFrame.format(w, s, 'd')
	}
}

Is there any reason for using runtime.Frame(f).Func.Name() instead of runtime.Frame(f).Function?

Closed in #193