177 lines · 4.1 KB · Go
RawDownload
package errors import (	"fmt"	"io"	"path"	"runtime"	"strconv"	"strings") // Frame represents a program counter inside a stack frame.// For historical reasons if Frame is interpreted as a uintptr// its value represents the program counter + 1.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.////    %s    source file//    %d    source line//    %n    function name//    %v    equivalent to %s:%d//// Format accepts flags that alter the printing of some verbs, as follows:////    %+s   function name and path of source file relative to the compile time//          GOPATH separated by \n\t (<funcname>\n\t<path>)//    %+v   equivalent to %+s:%dfunc (f Frame) Format(s fmt.State, verb rune) {	switch verb {	case 's':		switch {		case s.Flag('+'):			io.WriteString(s, f.name())			io.WriteString(s, "\n\t")			io.WriteString(s, f.file())		default:			io.WriteString(s, path.Base(f.file()))		}	case 'd':		io.WriteString(s, strconv.Itoa(f.line()))	case 'n':		io.WriteString(s, funcname(f.name()))	case 'v':		f.Format(s, 's')		io.WriteString(s, ":")		f.Format(s, 'd')	}} // MarshalText formats a stacktrace Frame as a text string. The output is the// same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.func (f Frame) MarshalText() ([]byte, error) {	name := f.name()	if name == "unknown" {		return []byte(name), nil	}	return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil} // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).type StackTrace []Frame // Format formats the stack of Frames according to the fmt.Formatter interface.////    %s	lists source files for each Frame in the stack//    %v	lists the source file and line number for each Frame in the stack//// Format accepts flags that alter the printing of some verbs, as follows:////    %+v   Prints filename, function, and line number for each Frame in the stack.func (st StackTrace) Format(s fmt.State, verb rune) {	switch verb {	case 'v':		switch {		case s.Flag('+'):			for _, f := range st {				io.WriteString(s, "\n")				f.Format(s, verb)			}		case s.Flag('#'):			fmt.Fprintf(s, "%#v", []Frame(st))		default:			st.formatSlice(s, verb)		}	case 's':		st.formatSlice(s, verb)	}} // 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(s fmt.State, verb rune) {	io.WriteString(s, "[")	for i, f := range st {		if i > 0 {			io.WriteString(s, " ")		}		f.Format(s, verb)	}	io.WriteString(s, "]")} // stack represents a stack of program counters.type stack []uintptr func (s *stack) Format(st fmt.State, verb rune) {	switch verb {	case 'v':		switch {		case st.Flag('+'):			for _, pc := range *s {				f := Frame(pc)				fmt.Fprintf(st, "\n%+v", f)			}		}	}} func (s *stack) StackTrace() StackTrace {	f := make([]Frame, len(*s))	for i := 0; i < len(f); i++ {		f[i] = Frame((*s)[i])	}	return f} func callers() *stack {	const depth = 32	var pcs [depth]uintptr	n := runtime.Callers(3, pcs[:])	var st stack = pcs[0:n]	return &st} // funcname removes the path prefix component of a function's name reported by func.Name().func funcname(name string) string {	i := strings.LastIndex(name, "/")	name = name[i+1:]	i = strings.Index(name, ".")	return name[i+1:]}