1// +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}
401