OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / go-stack / stack / stack.go
1 // Package stack implements utilities to capture, manipulate, and format call
2 // stacks. It provides a simpler API than package runtime.
3 //
4 // The implementation takes care of the minutia and special cases of
5 // interpreting the program counter (pc) values returned by runtime.Callers.
6 //
7 // Package stack's types implement fmt.Formatter, which provides a simple and
8 // flexible way to declaratively configure formatting when used with logging
9 // or error tracking packages.
10 package stack
11
12 import (
13         "bytes"
14         "errors"
15         "fmt"
16         "io"
17         "runtime"
18         "strconv"
19         "strings"
20 )
21
22 // Call records a single function invocation from a goroutine stack.
23 type Call struct {
24         fn *runtime.Func
25         pc uintptr
26 }
27
28 // Caller returns a Call from the stack of the current goroutine. The argument
29 // skip is the number of stack frames to ascend, with 0 identifying the
30 // calling function.
31 func Caller(skip int) Call {
32         var pcs [2]uintptr
33         n := runtime.Callers(skip+1, pcs[:])
34
35         var c Call
36
37         if n < 2 {
38                 return c
39         }
40
41         c.pc = pcs[1]
42         if runtime.FuncForPC(pcs[0]).Name() != "runtime.sigpanic" {
43                 c.pc--
44         }
45         c.fn = runtime.FuncForPC(c.pc)
46         return c
47 }
48
49 // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
50 func (c Call) String() string {
51         return fmt.Sprint(c)
52 }
53
54 // MarshalText implements encoding.TextMarshaler. It formats the Call the same
55 // as fmt.Sprintf("%v", c).
56 func (c Call) MarshalText() ([]byte, error) {
57         if c.fn == nil {
58                 return nil, ErrNoFunc
59         }
60         buf := bytes.Buffer{}
61         fmt.Fprint(&buf, c)
62         return buf.Bytes(), nil
63 }
64
65 // ErrNoFunc means that the Call has a nil *runtime.Func. The most likely
66 // cause is a Call with the zero value.
67 var ErrNoFunc = errors.New("no call stack information")
68
69 // Format implements fmt.Formatter with support for the following verbs.
70 //
71 //    %s    source file
72 //    %d    line number
73 //    %n    function name
74 //    %k    last segment of the package path
75 //    %v    equivalent to %s:%d
76 //
77 // It accepts the '+' and '#' flags for most of the verbs as follows.
78 //
79 //    %+s   path of source file relative to the compile time GOPATH
80 //    %#s   full path of source file
81 //    %+n   import path qualified function name
82 //    %+k   full package path
83 //    %+v   equivalent to %+s:%d
84 //    %#v   equivalent to %#s:%d
85 func (c Call) Format(s fmt.State, verb rune) {
86         if c.fn == nil {
87                 fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
88                 return
89         }
90
91         switch verb {
92         case 's', 'v':
93                 file, line := c.fn.FileLine(c.pc)
94                 switch {
95                 case s.Flag('#'):
96                         // done
97                 case s.Flag('+'):
98                         file = file[pkgIndex(file, c.fn.Name()):]
99                 default:
100                         const sep = "/"
101                         if i := strings.LastIndex(file, sep); i != -1 {
102                                 file = file[i+len(sep):]
103                         }
104                 }
105                 io.WriteString(s, file)
106                 if verb == 'v' {
107                         buf := [7]byte{':'}
108                         s.Write(strconv.AppendInt(buf[:1], int64(line), 10))
109                 }
110
111         case 'd':
112                 _, line := c.fn.FileLine(c.pc)
113                 buf := [6]byte{}
114                 s.Write(strconv.AppendInt(buf[:0], int64(line), 10))
115
116         case 'k':
117                 name := c.fn.Name()
118                 const pathSep = "/"
119                 start, end := 0, len(name)
120                 if i := strings.LastIndex(name, pathSep); i != -1 {
121                         start = i + len(pathSep)
122                 }
123                 const pkgSep = "."
124                 if i := strings.Index(name[start:], pkgSep); i != -1 {
125                         end = start + i
126                 }
127                 if s.Flag('+') {
128                         start = 0
129                 }
130                 io.WriteString(s, name[start:end])
131
132         case 'n':
133                 name := c.fn.Name()
134                 if !s.Flag('+') {
135                         const pathSep = "/"
136                         if i := strings.LastIndex(name, pathSep); i != -1 {
137                                 name = name[i+len(pathSep):]
138                         }
139                         const pkgSep = "."
140                         if i := strings.Index(name, pkgSep); i != -1 {
141                                 name = name[i+len(pkgSep):]
142                         }
143                 }
144                 io.WriteString(s, name)
145         }
146 }
147
148 // PC returns the program counter for this call frame; multiple frames may
149 // have the same PC value.
150 func (c Call) PC() uintptr {
151         return c.pc
152 }
153
154 // name returns the import path qualified name of the function containing the
155 // call.
156 func (c Call) name() string {
157         if c.fn == nil {
158                 return "???"
159         }
160         return c.fn.Name()
161 }
162
163 func (c Call) file() string {
164         if c.fn == nil {
165                 return "???"
166         }
167         file, _ := c.fn.FileLine(c.pc)
168         return file
169 }
170
171 func (c Call) line() int {
172         if c.fn == nil {
173                 return 0
174         }
175         _, line := c.fn.FileLine(c.pc)
176         return line
177 }
178
179 // CallStack records a sequence of function invocations from a goroutine
180 // stack.
181 type CallStack []Call
182
183 // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs).
184 func (cs CallStack) String() string {
185         return fmt.Sprint(cs)
186 }
187
188 var (
189         openBracketBytes  = []byte("[")
190         closeBracketBytes = []byte("]")
191         spaceBytes        = []byte(" ")
192 )
193
194 // MarshalText implements encoding.TextMarshaler. It formats the CallStack the
195 // same as fmt.Sprintf("%v", cs).
196 func (cs CallStack) MarshalText() ([]byte, error) {
197         buf := bytes.Buffer{}
198         buf.Write(openBracketBytes)
199         for i, pc := range cs {
200                 if pc.fn == nil {
201                         return nil, ErrNoFunc
202                 }
203                 if i > 0 {
204                         buf.Write(spaceBytes)
205                 }
206                 fmt.Fprint(&buf, pc)
207         }
208         buf.Write(closeBracketBytes)
209         return buf.Bytes(), nil
210 }
211
212 // Format implements fmt.Formatter by printing the CallStack as square brackets
213 // ([, ]) surrounding a space separated list of Calls each formatted with the
214 // supplied verb and options.
215 func (cs CallStack) Format(s fmt.State, verb rune) {
216         s.Write(openBracketBytes)
217         for i, pc := range cs {
218                 if i > 0 {
219                         s.Write(spaceBytes)
220                 }
221                 pc.Format(s, verb)
222         }
223         s.Write(closeBracketBytes)
224 }
225
226 // Trace returns a CallStack for the current goroutine with element 0
227 // identifying the calling function.
228 func Trace() CallStack {
229         var pcs [512]uintptr
230         n := runtime.Callers(2, pcs[:])
231         cs := make([]Call, n)
232
233         for i, pc := range pcs[:n] {
234                 pcFix := pc
235                 if i > 0 && cs[i-1].fn.Name() != "runtime.sigpanic" {
236                         pcFix--
237                 }
238                 cs[i] = Call{
239                         fn: runtime.FuncForPC(pcFix),
240                         pc: pcFix,
241                 }
242         }
243
244         return cs
245 }
246
247 // TrimBelow returns a slice of the CallStack with all entries below c
248 // removed.
249 func (cs CallStack) TrimBelow(c Call) CallStack {
250         for len(cs) > 0 && cs[0].pc != c.pc {
251                 cs = cs[1:]
252         }
253         return cs
254 }
255
256 // TrimAbove returns a slice of the CallStack with all entries above c
257 // removed.
258 func (cs CallStack) TrimAbove(c Call) CallStack {
259         for len(cs) > 0 && cs[len(cs)-1].pc != c.pc {
260                 cs = cs[:len(cs)-1]
261         }
262         return cs
263 }
264
265 // pkgIndex returns the index that results in file[index:] being the path of
266 // file relative to the compile time GOPATH, and file[:index] being the
267 // $GOPATH/src/ portion of file. funcName must be the name of a function in
268 // file as returned by runtime.Func.Name.
269 func pkgIndex(file, funcName string) int {
270         // As of Go 1.6.2 there is no direct way to know the compile time GOPATH
271         // at runtime, but we can infer the number of path segments in the GOPATH.
272         // We note that runtime.Func.Name() returns the function name qualified by
273         // the import path, which does not include the GOPATH. Thus we can trim
274         // segments from the beginning of the file path until the number of path
275         // separators remaining is one more than the number of path separators in
276         // the function name. For example, given:
277         //
278         //    GOPATH     /home/user
279         //    file       /home/user/src/pkg/sub/file.go
280         //    fn.Name()  pkg/sub.Type.Method
281         //
282         // We want to produce:
283         //
284         //    file[:idx] == /home/user/src/
285         //    file[idx:] == pkg/sub/file.go
286         //
287         // From this we can easily see that fn.Name() has one less path separator
288         // than our desired result for file[idx:]. We count separators from the
289         // end of the file path until it finds two more than in the function name
290         // and then move one character forward to preserve the initial path
291         // segment without a leading separator.
292         const sep = "/"
293         i := len(file)
294         for n := strings.Count(funcName, sep) + 2; n > 0; n-- {
295                 i = strings.LastIndex(file[:i], sep)
296                 if i == -1 {
297                         i = -len(sep)
298                         break
299                 }
300         }
301         // get back to 0 or trim the leading separator
302         return i + len(sep)
303 }
304
305 var runtimePath string
306
307 func init() {
308         var pcs [1]uintptr
309         runtime.Callers(0, pcs[:])
310         fn := runtime.FuncForPC(pcs[0])
311         file, _ := fn.FileLine(pcs[0])
312
313         idx := pkgIndex(file, fn.Name())
314
315         runtimePath = file[:idx]
316         if runtime.GOOS == "windows" {
317                 runtimePath = strings.ToLower(runtimePath)
318         }
319 }
320
321 func inGoroot(c Call) bool {
322         file := c.file()
323         if len(file) == 0 || file[0] == '?' {
324                 return true
325         }
326         if runtime.GOOS == "windows" {
327                 file = strings.ToLower(file)
328         }
329         return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go")
330 }
331
332 // TrimRuntime returns a slice of the CallStack with the topmost entries from
333 // the go runtime removed. It considers any calls originating from unknown
334 // files, files under GOROOT, or _testmain.go as part of the runtime.
335 func (cs CallStack) TrimRuntime() CallStack {
336         for len(cs) > 0 && inGoroot(cs[len(cs)-1]) {
337                 cs = cs[:len(cs)-1]
338         }
339         return cs
340 }