gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

stack.go (4221B)


      1 package errors
      2 
      3 import (
      4 	"fmt"
      5 	"io"
      6 	"path"
      7 	"runtime"
      8 	"strconv"
      9 	"strings"
     10 )
     11 
     12 // Frame represents a program counter inside a stack frame.
     13 // For historical reasons if Frame is interpreted as a uintptr
     14 // its value represents the program counter + 1.
     15 type Frame uintptr
     16 
     17 // pc returns the program counter for this frame;
     18 // multiple frames may have the same PC value.
     19 func (f Frame) pc() uintptr { return uintptr(f) - 1 }
     20 
     21 // file returns the full path to the file that contains the
     22 // function for this Frame's pc.
     23 func (f Frame) file() string {
     24 	fn := runtime.FuncForPC(f.pc())
     25 	if fn == nil {
     26 		return "unknown"
     27 	}
     28 	file, _ := fn.FileLine(f.pc())
     29 	return file
     30 }
     31 
     32 // line returns the line number of source code of the
     33 // function for this Frame's pc.
     34 func (f Frame) line() int {
     35 	fn := runtime.FuncForPC(f.pc())
     36 	if fn == nil {
     37 		return 0
     38 	}
     39 	_, line := fn.FileLine(f.pc())
     40 	return line
     41 }
     42 
     43 // name returns the name of this function, if known.
     44 func (f Frame) name() string {
     45 	fn := runtime.FuncForPC(f.pc())
     46 	if fn == nil {
     47 		return "unknown"
     48 	}
     49 	return fn.Name()
     50 }
     51 
     52 // Format formats the frame according to the fmt.Formatter interface.
     53 //
     54 //    %s    source file
     55 //    %d    source line
     56 //    %n    function name
     57 //    %v    equivalent to %s:%d
     58 //
     59 // Format accepts flags that alter the printing of some verbs, as follows:
     60 //
     61 //    %+s   function name and path of source file relative to the compile time
     62 //          GOPATH separated by \n\t (<funcname>\n\t<path>)
     63 //    %+v   equivalent to %+s:%d
     64 func (f Frame) Format(s fmt.State, verb rune) {
     65 	switch verb {
     66 	case 's':
     67 		switch {
     68 		case s.Flag('+'):
     69 			io.WriteString(s, f.name())
     70 			io.WriteString(s, "\n\t")
     71 			io.WriteString(s, f.file())
     72 		default:
     73 			io.WriteString(s, path.Base(f.file()))
     74 		}
     75 	case 'd':
     76 		io.WriteString(s, strconv.Itoa(f.line()))
     77 	case 'n':
     78 		io.WriteString(s, funcname(f.name()))
     79 	case 'v':
     80 		f.Format(s, 's')
     81 		io.WriteString(s, ":")
     82 		f.Format(s, 'd')
     83 	}
     84 }
     85 
     86 // MarshalText formats a stacktrace Frame as a text string. The output is the
     87 // same as that of fmt.Sprintf("%+v", f), but without newlines or tabs.
     88 func (f Frame) MarshalText() ([]byte, error) {
     89 	name := f.name()
     90 	if name == "unknown" {
     91 		return []byte(name), nil
     92 	}
     93 	return []byte(fmt.Sprintf("%s %s:%d", name, f.file(), f.line())), nil
     94 }
     95 
     96 // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
     97 type StackTrace []Frame
     98 
     99 // Format formats the stack of Frames according to the fmt.Formatter interface.
    100 //
    101 //    %s	lists source files for each Frame in the stack
    102 //    %v	lists the source file and line number for each Frame in the stack
    103 //
    104 // Format accepts flags that alter the printing of some verbs, as follows:
    105 //
    106 //    %+v   Prints filename, function, and line number for each Frame in the stack.
    107 func (st StackTrace) Format(s fmt.State, verb rune) {
    108 	switch verb {
    109 	case 'v':
    110 		switch {
    111 		case s.Flag('+'):
    112 			for _, f := range st {
    113 				io.WriteString(s, "\n")
    114 				f.Format(s, verb)
    115 			}
    116 		case s.Flag('#'):
    117 			fmt.Fprintf(s, "%#v", []Frame(st))
    118 		default:
    119 			st.formatSlice(s, verb)
    120 		}
    121 	case 's':
    122 		st.formatSlice(s, verb)
    123 	}
    124 }
    125 
    126 // formatSlice will format this StackTrace into the given buffer as a slice of
    127 // Frame, only valid when called with '%s' or '%v'.
    128 func (st StackTrace) formatSlice(s fmt.State, verb rune) {
    129 	io.WriteString(s, "[")
    130 	for i, f := range st {
    131 		if i > 0 {
    132 			io.WriteString(s, " ")
    133 		}
    134 		f.Format(s, verb)
    135 	}
    136 	io.WriteString(s, "]")
    137 }
    138 
    139 // stack represents a stack of program counters.
    140 type stack []uintptr
    141 
    142 func (s *stack) Format(st fmt.State, verb rune) {
    143 	switch verb {
    144 	case 'v':
    145 		switch {
    146 		case st.Flag('+'):
    147 			for _, pc := range *s {
    148 				f := Frame(pc)
    149 				fmt.Fprintf(st, "\n%+v", f)
    150 			}
    151 		}
    152 	}
    153 }
    154 
    155 func (s *stack) StackTrace() StackTrace {
    156 	f := make([]Frame, len(*s))
    157 	for i := 0; i < len(f); i++ {
    158 		f[i] = Frame((*s)[i])
    159 	}
    160 	return f
    161 }
    162 
    163 func callers() *stack {
    164 	const depth = 32
    165 	var pcs [depth]uintptr
    166 	n := runtime.Callers(3, pcs[:])
    167 	var st stack = pcs[0:n]
    168 	return &st
    169 }
    170 
    171 // funcname removes the path prefix component of a function's name reported by func.Name().
    172 func funcname(name string) string {
    173 	i := strings.LastIndex(name, "/")
    174 	name = name[i+1:]
    175 	i = strings.Index(name, ".")
    176 	return name[i+1:]
    177 }