OSDN Git Service

versoin1.1.9 (#594)
[bytom/vapor.git] / vendor / github.com / pkg / errors / stack.go
1 package errors
2
3 import (
4         "fmt"
5         "io"
6         "path"
7         "runtime"
8         "strings"
9 )
10
11 // Frame represents a program counter inside a stack frame.
12 type Frame uintptr
13
14 // pc returns the program counter for this frame;
15 // multiple frames may have the same PC value.
16 func (f Frame) pc() uintptr { return uintptr(f) - 1 }
17
18 // file returns the full path to the file that contains the
19 // function for this Frame's pc.
20 func (f Frame) file() string {
21         fn := runtime.FuncForPC(f.pc())
22         if fn == nil {
23                 return "unknown"
24         }
25         file, _ := fn.FileLine(f.pc())
26         return file
27 }
28
29 // line returns the line number of source code of the
30 // function for this Frame's pc.
31 func (f Frame) line() int {
32         fn := runtime.FuncForPC(f.pc())
33         if fn == nil {
34                 return 0
35         }
36         _, line := fn.FileLine(f.pc())
37         return line
38 }
39
40 // Format formats the frame according to the fmt.Formatter interface.
41 //
42 //    %s    source file
43 //    %d    source line
44 //    %n    function name
45 //    %v    equivalent to %s:%d
46 //
47 // Format accepts flags that alter the printing of some verbs, as follows:
48 //
49 //    %+s   path of source file relative to the compile time GOPATH
50 //    %+v   equivalent to %+s:%d
51 func (f Frame) Format(s fmt.State, verb rune) {
52         switch verb {
53         case 's':
54                 switch {
55                 case s.Flag('+'):
56                         pc := f.pc()
57                         fn := runtime.FuncForPC(pc)
58                         if fn == nil {
59                                 io.WriteString(s, "unknown")
60                         } else {
61                                 file, _ := fn.FileLine(pc)
62                                 fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
63                         }
64                 default:
65                         io.WriteString(s, path.Base(f.file()))
66                 }
67         case 'd':
68                 fmt.Fprintf(s, "%d", f.line())
69         case 'n':
70                 name := runtime.FuncForPC(f.pc()).Name()
71                 io.WriteString(s, funcname(name))
72         case 'v':
73                 f.Format(s, 's')
74                 io.WriteString(s, ":")
75                 f.Format(s, 'd')
76         }
77 }
78
79 // StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
80 type StackTrace []Frame
81
82 // Format formats the stack of Frames according to the fmt.Formatter interface.
83 //
84 //    %s        lists source files for each Frame in the stack
85 //    %v        lists the source file and line number for each Frame in the stack
86 //
87 // Format accepts flags that alter the printing of some verbs, as follows:
88 //
89 //    %+v   Prints filename, function, and line number for each Frame in the stack.
90 func (st StackTrace) Format(s fmt.State, verb rune) {
91         switch verb {
92         case 'v':
93                 switch {
94                 case s.Flag('+'):
95                         for _, f := range st {
96                                 fmt.Fprintf(s, "\n%+v", f)
97                         }
98                 case s.Flag('#'):
99                         fmt.Fprintf(s, "%#v", []Frame(st))
100                 default:
101                         fmt.Fprintf(s, "%v", []Frame(st))
102                 }
103         case 's':
104                 fmt.Fprintf(s, "%s", []Frame(st))
105         }
106 }
107
108 // stack represents a stack of program counters.
109 type stack []uintptr
110
111 func (s *stack) Format(st fmt.State, verb rune) {
112         switch verb {
113         case 'v':
114                 switch {
115                 case st.Flag('+'):
116                         for _, pc := range *s {
117                                 f := Frame(pc)
118                                 fmt.Fprintf(st, "\n%+v", f)
119                         }
120                 }
121         }
122 }
123
124 func (s *stack) StackTrace() StackTrace {
125         f := make([]Frame, len(*s))
126         for i := 0; i < len(f); i++ {
127                 f[i] = Frame((*s)[i])
128         }
129         return f
130 }
131
132 func callers() *stack {
133         const depth = 32
134         var pcs [depth]uintptr
135         n := runtime.Callers(3, pcs[:])
136         var st stack = pcs[0:n]
137         return &st
138 }
139
140 // funcname removes the path prefix component of a function's name reported by func.Name().
141 func funcname(name string) string {
142         i := strings.LastIndex(name, "/")
143         name = name[i+1:]
144         i = strings.Index(name, ".")
145         return name[i+1:]
146 }
147
148 func trimGOPATH(name, file string) string {
149         // Here we want to get the source file path relative to the compile time
150         // GOPATH. As of Go 1.6.x there is no direct way to know the compiled
151         // GOPATH at runtime, but we can infer the number of path segments in the
152         // GOPATH. We note that fn.Name() returns the function name qualified by
153         // the import path, which does not include the GOPATH. Thus we can trim
154         // segments from the beginning of the file path until the number of path
155         // separators remaining is one more than the number of path separators in
156         // the function name. For example, given:
157         //
158         //    GOPATH     /home/user
159         //    file       /home/user/src/pkg/sub/file.go
160         //    fn.Name()  pkg/sub.Type.Method
161         //
162         // We want to produce:
163         //
164         //    pkg/sub/file.go
165         //
166         // From this we can easily see that fn.Name() has one less path separator
167         // than our desired output. We count separators from the end of the file
168         // path until it finds two more than in the function name and then move
169         // one character forward to preserve the initial path segment without a
170         // leading separator.
171         const sep = "/"
172         goal := strings.Count(name, sep) + 2
173         i := len(file)
174         for n := 0; n < goal; n++ {
175                 i = strings.LastIndex(file[:i], sep)
176                 if i == -1 {
177                         // not enough separators found, set i so that the slice expression
178                         // below leaves file unmodified
179                         i = -len(sep)
180                         break
181                 }
182         }
183         // get back to 0 or trim the leading separator
184         file = file[i+len(sep):]
185         return file
186 }