blob: ac3b93b14f48fea3c94d23045a19a613f4c0d6c6 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001// +build go1.7
2
3// Package stack implements utilities to capture, manipulate, and format call
4// stacks. It provides a simpler API than package runtime.
5//
6// The implementation takes care of the minutia and special cases of
7// interpreting the program counter (pc) values returned by runtime.Callers.
8//
9// Package stack's types implement fmt.Formatter, which provides a simple and
10// flexible way to declaratively configure formatting when used with logging
11// or error tracking packages.
12package stack
13
14import (
15 "bytes"
16 "errors"
17 "fmt"
18 "io"
19 "runtime"
20 "strconv"
21 "strings"
22)
23
24// Call records a single function invocation from a goroutine stack.
25type Call struct {
26 frame runtime.Frame
27}
28
29// Caller returns a Call from the stack of the current goroutine. The argument
30// skip is the number of stack frames to ascend, with 0 identifying the
31// calling function.
32func Caller(skip int) Call {
33 // As of Go 1.9 we need room for up to three PC entries.
34 //
35 // 0. An entry for the stack frame prior to the target to check for
36 // special handling needed if that prior entry is runtime.sigpanic.
37 // 1. A possible second entry to hold metadata about skipped inlined
38 // functions. If inline functions were not skipped the target frame
39 // PC will be here.
40 // 2. A third entry for the target frame PC when the second entry
41 // is used for skipped inline functions.
42 var pcs [3]uintptr
43 n := runtime.Callers(skip+1, pcs[:])
44 frames := runtime.CallersFrames(pcs[:n])
45 frame, _ := frames.Next()
46 frame, _ = frames.Next()
47
48 return Call{
49 frame: frame,
50 }
51}
52
53// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c).
54func (c Call) String() string {
55 return fmt.Sprint(c)
56}
57
58// MarshalText implements encoding.TextMarshaler. It formats the Call the same
59// as fmt.Sprintf("%v", c).
60func (c Call) MarshalText() ([]byte, error) {
61 if c.frame == (runtime.Frame{}) {
62 return nil, ErrNoFunc
63 }
64
65 buf := bytes.Buffer{}
66 fmt.Fprint(&buf, c)
67 return buf.Bytes(), nil
68}
69
70// ErrNoFunc means that the Call has a nil *runtime.Func. The most likely
71// cause is a Call with the zero value.
72var ErrNoFunc = errors.New("no call stack information")
73
74// Format implements fmt.Formatter with support for the following verbs.
75//
76// %s source file
77// %d line number
78// %n function name
79// %k last segment of the package path
80// %v equivalent to %s:%d
81//
82// It accepts the '+' and '#' flags for most of the verbs as follows.
83//
84// %+s path of source file relative to the compile time GOPATH,
85// or the module path joined to the path of source file relative
86// to module root
87// %#s full path of source file
88// %+n import path qualified function name
89// %+k full package path
90// %+v equivalent to %+s:%d
91// %#v equivalent to %#s:%d
92func (c Call) Format(s fmt.State, verb rune) {
93 if c.frame == (runtime.Frame{}) {
94 fmt.Fprintf(s, "%%!%c(NOFUNC)", verb)
95 return
96 }
97
98 switch verb {
99 case 's', 'v':
100 file := c.frame.File
101 switch {
102 case s.Flag('#'):
103 // done
104 case s.Flag('+'):
105 file = pkgFilePath(&c.frame)
106 default:
107 const sep = "/"
108 if i := strings.LastIndex(file, sep); i != -1 {
109 file = file[i+len(sep):]
110 }
111 }
112 io.WriteString(s, file)
113 if verb == 'v' {
114 buf := [7]byte{':'}
115 s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10))
116 }
117
118 case 'd':
119 buf := [6]byte{}
120 s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10))
121
122 case 'k':
123 name := c.frame.Function
124 const pathSep = "/"
125 start, end := 0, len(name)
126 if i := strings.LastIndex(name, pathSep); i != -1 {
127 start = i + len(pathSep)
128 }
129 const pkgSep = "."
130 if i := strings.Index(name[start:], pkgSep); i != -1 {
131 end = start + i
132 }
133 if s.Flag('+') {
134 start = 0
135 }
136 io.WriteString(s, name[start:end])
137
138 case 'n':
139 name := c.frame.Function
140 if !s.Flag('+') {
141 const pathSep = "/"
142 if i := strings.LastIndex(name, pathSep); i != -1 {
143 name = name[i+len(pathSep):]
144 }
145 const pkgSep = "."
146 if i := strings.Index(name, pkgSep); i != -1 {
147 name = name[i+len(pkgSep):]
148 }
149 }
150 io.WriteString(s, name)
151 }
152}
153
154// Frame returns the call frame infomation for the Call.
155func (c Call) Frame() runtime.Frame {
156 return c.frame
157}
158
159// PC returns the program counter for this call frame; multiple frames may
160// have the same PC value.
161//
162// Deprecated: Use Call.Frame instead.
163func (c Call) PC() uintptr {
164 return c.frame.PC
165}
166
167// CallStack records a sequence of function invocations from a goroutine
168// stack.
169type CallStack []Call
170
171// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs).
172func (cs CallStack) String() string {
173 return fmt.Sprint(cs)
174}
175
176var (
177 openBracketBytes = []byte("[")
178 closeBracketBytes = []byte("]")
179 spaceBytes = []byte(" ")
180)
181
182// MarshalText implements encoding.TextMarshaler. It formats the CallStack the
183// same as fmt.Sprintf("%v", cs).
184func (cs CallStack) MarshalText() ([]byte, error) {
185 buf := bytes.Buffer{}
186 buf.Write(openBracketBytes)
187 for i, pc := range cs {
188 if i > 0 {
189 buf.Write(spaceBytes)
190 }
191 fmt.Fprint(&buf, pc)
192 }
193 buf.Write(closeBracketBytes)
194 return buf.Bytes(), nil
195}
196
197// Format implements fmt.Formatter by printing the CallStack as square brackets
198// ([, ]) surrounding a space separated list of Calls each formatted with the
199// supplied verb and options.
200func (cs CallStack) Format(s fmt.State, verb rune) {
201 s.Write(openBracketBytes)
202 for i, pc := range cs {
203 if i > 0 {
204 s.Write(spaceBytes)
205 }
206 pc.Format(s, verb)
207 }
208 s.Write(closeBracketBytes)
209}
210
211// Trace returns a CallStack for the current goroutine with element 0
212// identifying the calling function.
213func Trace() CallStack {
214 var pcs [512]uintptr
215 n := runtime.Callers(1, pcs[:])
216
217 frames := runtime.CallersFrames(pcs[:n])
218 cs := make(CallStack, 0, n)
219
220 // Skip extra frame retrieved just to make sure the runtime.sigpanic
221 // special case is handled.
222 frame, more := frames.Next()
223
224 for more {
225 frame, more = frames.Next()
226 cs = append(cs, Call{frame: frame})
227 }
228
229 return cs
230}
231
232// TrimBelow returns a slice of the CallStack with all entries below c
233// removed.
234func (cs CallStack) TrimBelow(c Call) CallStack {
235 for len(cs) > 0 && cs[0] != c {
236 cs = cs[1:]
237 }
238 return cs
239}
240
241// TrimAbove returns a slice of the CallStack with all entries above c
242// removed.
243func (cs CallStack) TrimAbove(c Call) CallStack {
244 for len(cs) > 0 && cs[len(cs)-1] != c {
245 cs = cs[:len(cs)-1]
246 }
247 return cs
248}
249
250// pkgIndex returns the index that results in file[index:] being the path of
251// file relative to the compile time GOPATH, and file[:index] being the
252// $GOPATH/src/ portion of file. funcName must be the name of a function in
253// file as returned by runtime.Func.Name.
254func pkgIndex(file, funcName string) int {
255 // As of Go 1.6.2 there is no direct way to know the compile time GOPATH
256 // at runtime, but we can infer the number of path segments in the GOPATH.
257 // We note that runtime.Func.Name() returns the function name qualified by
258 // the import path, which does not include the GOPATH. Thus we can trim
259 // segments from the beginning of the file path until the number of path
260 // separators remaining is one more than the number of path separators in
261 // the function name. For example, given:
262 //
263 // GOPATH /home/user
264 // file /home/user/src/pkg/sub/file.go
265 // fn.Name() pkg/sub.Type.Method
266 //
267 // We want to produce:
268 //
269 // file[:idx] == /home/user/src/
270 // file[idx:] == pkg/sub/file.go
271 //
272 // From this we can easily see that fn.Name() has one less path separator
273 // than our desired result for file[idx:]. We count separators from the
274 // end of the file path until it finds two more than in the function name
275 // and then move one character forward to preserve the initial path
276 // segment without a leading separator.
277 const sep = "/"
278 i := len(file)
279 for n := strings.Count(funcName, sep) + 2; n > 0; n-- {
280 i = strings.LastIndex(file[:i], sep)
281 if i == -1 {
282 i = -len(sep)
283 break
284 }
285 }
286 // get back to 0 or trim the leading separator
287 return i + len(sep)
288}
289
290// pkgFilePath returns the frame's filepath relative to the compile-time GOPATH,
291// or its module path joined to its path relative to the module root.
292//
293// As of Go 1.11 there is no direct way to know the compile time GOPATH or
294// module paths at runtime, but we can piece together the desired information
295// from available information. We note that runtime.Frame.Function contains the
296// function name qualified by the package path, which includes the module path
297// but not the GOPATH. We can extract the package path from that and append the
298// last segments of the file path to arrive at the desired package qualified
299// file path. For example, given:
300//
301// GOPATH /home/user
302// import path pkg/sub
303// frame.File /home/user/src/pkg/sub/file.go
304// frame.Function pkg/sub.Type.Method
305// Desired return pkg/sub/file.go
306//
307// It appears that we simply need to trim ".Type.Method" from frame.Function and
308// append "/" + path.Base(file).
309//
310// But there are other wrinkles. Although it is idiomatic to do so, the internal
311// name of a package is not required to match the last segment of its import
312// path. In addition, the introduction of modules in Go 1.11 allows working
313// without a GOPATH. So we also must make these work right:
314//
315// GOPATH /home/user
316// import path pkg/go-sub
317// package name sub
318// frame.File /home/user/src/pkg/go-sub/file.go
319// frame.Function pkg/sub.Type.Method
320// Desired return pkg/go-sub/file.go
321//
322// Module path pkg/v2
323// import path pkg/v2/go-sub
324// package name sub
325// frame.File /home/user/cloned-pkg/go-sub/file.go
326// frame.Function pkg/v2/sub.Type.Method
327// Desired return pkg/v2/go-sub/file.go
328//
329// We can handle all of these situations by using the package path extracted
330// from frame.Function up to, but not including, the last segment as the prefix
331// and the last two segments of frame.File as the suffix of the returned path.
332// This preserves the existing behavior when working in a GOPATH without modules
333// and a semantically equivalent behavior when used in module aware project.
334func pkgFilePath(frame *runtime.Frame) string {
335 pre := pkgPrefix(frame.Function)
336 post := pathSuffix(frame.File)
337 if pre == "" {
338 return post
339 }
340 return pre + "/" + post
341}
342
343// pkgPrefix returns the import path of the function's package with the final
344// segment removed.
345func pkgPrefix(funcName string) string {
346 const pathSep = "/"
347 end := strings.LastIndex(funcName, pathSep)
348 if end == -1 {
349 return ""
350 }
351 return funcName[:end]
352}
353
354// pathSuffix returns the last two segments of path.
355func pathSuffix(path string) string {
356 const pathSep = "/"
357 lastSep := strings.LastIndex(path, pathSep)
358 if lastSep == -1 {
359 return path
360 }
361 return path[strings.LastIndex(path[:lastSep], pathSep)+1:]
362}
363
364var runtimePath string
365
366func init() {
367 var pcs [3]uintptr
368 runtime.Callers(0, pcs[:])
369 frames := runtime.CallersFrames(pcs[:])
370 frame, _ := frames.Next()
371 file := frame.File
372
373 idx := pkgIndex(frame.File, frame.Function)
374
375 runtimePath = file[:idx]
376 if runtime.GOOS == "windows" {
377 runtimePath = strings.ToLower(runtimePath)
378 }
379}
380
381func inGoroot(c Call) bool {
382 file := c.frame.File
383 if len(file) == 0 || file[0] == '?' {
384 return true
385 }
386 if runtime.GOOS == "windows" {
387 file = strings.ToLower(file)
388 }
389 return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go")
390}
391
392// TrimRuntime returns a slice of the CallStack with the topmost entries from
393// the go runtime removed. It considers any calls originating from unknown
394// files, files under GOROOT, or _testmain.go as part of the runtime.
395func (cs CallStack) TrimRuntime() CallStack {
396 for len(cs) > 0 && inGoroot(cs[len(cs)-1]) {
397 cs = cs[:len(cs)-1]
398 }
399 return cs
400}