Update upstream source from tag 'upstream/1.8.0'
Update to upstream version '1.8.0'
with Debian dir cecb5028e296fb2779a2e4edbf51f649ee09a6ef
Shengjing Zhu
4 years ago
0 | 0 | language: go |
1 | 1 | sudo: false |
2 | 2 | go: |
3 | - 1.2 | |
4 | - 1.3 | |
5 | - 1.4 | |
6 | - 1.5 | |
7 | - 1.6 | |
3 | - 1.7.x | |
4 | - 1.8.x | |
5 | - 1.9.x | |
6 | - 1.10.x | |
7 | - 1.11.x | |
8 | 8 | - tip |
9 | 9 | |
10 | 10 | before_install: |
11 | 11 | - go get github.com/mattn/goveralls |
12 | - go get golang.org/x/tools/cmd/cover | |
13 | 12 | |
14 | 13 | script: |
15 | 14 | - goveralls -service=travis-ci |
0 | Copyright 2014 Chris Hines | |
0 | The MIT License (MIT) | |
1 | 1 | |
2 | Licensed under the Apache License, Version 2.0 (the "License"); | |
3 | you may not use this file except in compliance with the License. | |
4 | You may obtain a copy of the License at | |
2 | Copyright (c) 2014 Chris Hines | |
5 | 3 | |
6 | http://www.apache.org/licenses/LICENSE-2.0 | |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | of this software and associated documentation files (the "Software"), to deal | |
6 | in the Software without restriction, including without limitation the rights | |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | copies of the Software, and to permit persons to whom the Software is | |
9 | furnished to do so, subject to the following conditions: | |
7 | 10 | |
8 | Unless required by applicable law or agreed to in writing, software | |
9 | distributed under the License is distributed on an "AS IS" BASIS, | |
10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
11 | See the License for the specific language governing permissions and | |
12 | limitations under the License. | |
11 | The above copyright notice and this permission notice shall be included in all | |
12 | copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | SOFTWARE. |
0 | // +build go1.9 | |
1 | ||
2 | package stack_test | |
3 | ||
4 | import ( | |
5 | "runtime" | |
6 | "testing" | |
7 | ||
8 | "github.com/go-stack/stack" | |
9 | ) | |
10 | ||
11 | func TestCallerInlinedPanic(t *testing.T) { | |
12 | t.Parallel() | |
13 | ||
14 | var line int | |
15 | ||
16 | defer func() { | |
17 | if recover() != nil { | |
18 | var pcs [32]uintptr | |
19 | n := runtime.Callers(1, pcs[:]) | |
20 | frames := runtime.CallersFrames(pcs[:n]) | |
21 | // count frames to runtime.sigpanic | |
22 | panicIdx := 0 | |
23 | for { | |
24 | f, more := frames.Next() | |
25 | if f.Function == "runtime.sigpanic" { | |
26 | break | |
27 | } | |
28 | panicIdx++ | |
29 | if !more { | |
30 | t.Fatal("no runtime.sigpanic entry on the stack") | |
31 | } | |
32 | } | |
33 | ||
34 | c := stack.Caller(panicIdx) | |
35 | if got, want := c.Frame().Function, "runtime.sigpanic"; got != want { | |
36 | t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want) | |
37 | } | |
38 | ||
39 | c1 := stack.Caller(panicIdx + 1) | |
40 | if got, want := c1.Frame().Function, "github.com/go-stack/stack_test.inlinablePanic"; got != want { | |
41 | t.Errorf("TestCallerInlinedPanic frame: got name == %v, want name == %v", got, want) | |
42 | } | |
43 | if got, want := c1.Frame().Line, line; got != want { | |
44 | t.Errorf("TestCallerInlinedPanic frame: got line == %v, want line == %v", got, want) | |
45 | } | |
46 | } | |
47 | }() | |
48 | ||
49 | doPanic(t, &line) | |
50 | t.Fatal("failed to panic") | |
51 | } | |
52 | ||
53 | func doPanic(t *testing.T, panicLine *int) { | |
54 | _, _, line, ok := runtime.Caller(0) | |
55 | *panicLine = line + 11 // adjust to match line of panic below | |
56 | if !ok { | |
57 | t.Fatal("runtime.Caller(0) failed") | |
58 | } | |
59 | inlinablePanic() | |
60 | } | |
61 | ||
62 | func inlinablePanic() { | |
63 | // Initiate a sigpanic. | |
64 | var x *uintptr | |
65 | _ = *x | |
66 | } |
0 | // +build go1.7 | |
1 | ||
0 | 2 | // Package stack implements utilities to capture, manipulate, and format call |
1 | 3 | // stacks. It provides a simpler API than package runtime. |
2 | 4 | // |
20 | 22 | |
21 | 23 | // Call records a single function invocation from a goroutine stack. |
22 | 24 | type Call struct { |
23 | fn *runtime.Func | |
24 | pc uintptr | |
25 | frame runtime.Frame | |
25 | 26 | } |
26 | 27 | |
27 | 28 | // Caller returns a Call from the stack of the current goroutine. The argument |
28 | 29 | // skip is the number of stack frames to ascend, with 0 identifying the |
29 | 30 | // calling function. |
30 | 31 | func Caller(skip int) Call { |
31 | var pcs [2]uintptr | |
32 | // As of Go 1.9 we need room for up to three PC entries. | |
33 | // | |
34 | // 0. An entry for the stack frame prior to the target to check for | |
35 | // special handling needed if that prior entry is runtime.sigpanic. | |
36 | // 1. A possible second entry to hold metadata about skipped inlined | |
37 | // functions. If inline functions were not skipped the target frame | |
38 | // PC will be here. | |
39 | // 2. A third entry for the target frame PC when the second entry | |
40 | // is used for skipped inline functions. | |
41 | var pcs [3]uintptr | |
32 | 42 | n := runtime.Callers(skip+1, pcs[:]) |
33 | ||
34 | var c Call | |
35 | ||
36 | if n < 2 { | |
37 | return c | |
38 | } | |
39 | ||
40 | c.pc = pcs[1] | |
41 | if runtime.FuncForPC(pcs[0]) != sigpanic { | |
42 | c.pc-- | |
43 | } | |
44 | c.fn = runtime.FuncForPC(c.pc) | |
45 | return c | |
43 | frames := runtime.CallersFrames(pcs[:n]) | |
44 | frame, _ := frames.Next() | |
45 | frame, _ = frames.Next() | |
46 | ||
47 | return Call{ | |
48 | frame: frame, | |
49 | } | |
46 | 50 | } |
47 | 51 | |
48 | 52 | // String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c). |
53 | 57 | // MarshalText implements encoding.TextMarshaler. It formats the Call the same |
54 | 58 | // as fmt.Sprintf("%v", c). |
55 | 59 | func (c Call) MarshalText() ([]byte, error) { |
56 | if c.fn == nil { | |
60 | if c.frame == (runtime.Frame{}) { | |
57 | 61 | return nil, ErrNoFunc |
58 | 62 | } |
63 | ||
59 | 64 | buf := bytes.Buffer{} |
60 | 65 | fmt.Fprint(&buf, c) |
61 | 66 | return buf.Bytes(), nil |
70 | 75 | // %s source file |
71 | 76 | // %d line number |
72 | 77 | // %n function name |
78 | // %k last segment of the package path | |
73 | 79 | // %v equivalent to %s:%d |
74 | 80 | // |
75 | 81 | // It accepts the '+' and '#' flags for most of the verbs as follows. |
76 | 82 | // |
77 | // %+s path of source file relative to the compile time GOPATH | |
83 | // %+s path of source file relative to the compile time GOPATH, | |
84 | // or the module path joined to the path of source file relative | |
85 | // to module root | |
78 | 86 | // %#s full path of source file |
79 | 87 | // %+n import path qualified function name |
88 | // %+k full package path | |
80 | 89 | // %+v equivalent to %+s:%d |
81 | 90 | // %#v equivalent to %#s:%d |
82 | 91 | func (c Call) Format(s fmt.State, verb rune) { |
83 | if c.fn == nil { | |
92 | if c.frame == (runtime.Frame{}) { | |
84 | 93 | fmt.Fprintf(s, "%%!%c(NOFUNC)", verb) |
85 | 94 | return |
86 | 95 | } |
87 | 96 | |
88 | 97 | switch verb { |
89 | 98 | case 's', 'v': |
90 | file, line := c.fn.FileLine(c.pc) | |
99 | file := c.frame.File | |
91 | 100 | switch { |
92 | 101 | case s.Flag('#'): |
93 | 102 | // done |
94 | 103 | case s.Flag('+'): |
95 | file = file[pkgIndex(file, c.fn.Name()):] | |
104 | file = pkgFilePath(&c.frame) | |
96 | 105 | default: |
97 | 106 | const sep = "/" |
98 | 107 | if i := strings.LastIndex(file, sep); i != -1 { |
102 | 111 | io.WriteString(s, file) |
103 | 112 | if verb == 'v' { |
104 | 113 | buf := [7]byte{':'} |
105 | s.Write(strconv.AppendInt(buf[:1], int64(line), 10)) | |
114 | s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10)) | |
106 | 115 | } |
107 | 116 | |
108 | 117 | case 'd': |
109 | _, line := c.fn.FileLine(c.pc) | |
110 | 118 | buf := [6]byte{} |
111 | s.Write(strconv.AppendInt(buf[:0], int64(line), 10)) | |
119 | s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10)) | |
120 | ||
121 | case 'k': | |
122 | name := c.frame.Function | |
123 | const pathSep = "/" | |
124 | start, end := 0, len(name) | |
125 | if i := strings.LastIndex(name, pathSep); i != -1 { | |
126 | start = i + len(pathSep) | |
127 | } | |
128 | const pkgSep = "." | |
129 | if i := strings.Index(name[start:], pkgSep); i != -1 { | |
130 | end = start + i | |
131 | } | |
132 | if s.Flag('+') { | |
133 | start = 0 | |
134 | } | |
135 | io.WriteString(s, name[start:end]) | |
112 | 136 | |
113 | 137 | case 'n': |
114 | name := c.fn.Name() | |
138 | name := c.frame.Function | |
115 | 139 | if !s.Flag('+') { |
116 | 140 | const pathSep = "/" |
117 | 141 | if i := strings.LastIndex(name, pathSep); i != -1 { |
126 | 150 | } |
127 | 151 | } |
128 | 152 | |
153 | // Frame returns the call frame infomation for the Call. | |
154 | func (c Call) Frame() runtime.Frame { | |
155 | return c.frame | |
156 | } | |
157 | ||
129 | 158 | // PC returns the program counter for this call frame; multiple frames may |
130 | 159 | // have the same PC value. |
160 | // | |
161 | // Deprecated: Use Call.Frame instead. | |
131 | 162 | func (c Call) PC() uintptr { |
132 | return c.pc | |
133 | } | |
134 | ||
135 | // name returns the import path qualified name of the function containing the | |
136 | // call. | |
137 | func (c Call) name() string { | |
138 | if c.fn == nil { | |
139 | return "???" | |
140 | } | |
141 | return c.fn.Name() | |
142 | } | |
143 | ||
144 | func (c Call) file() string { | |
145 | if c.fn == nil { | |
146 | return "???" | |
147 | } | |
148 | file, _ := c.fn.FileLine(c.pc) | |
149 | return file | |
150 | } | |
151 | ||
152 | func (c Call) line() int { | |
153 | if c.fn == nil { | |
154 | return 0 | |
155 | } | |
156 | _, line := c.fn.FileLine(c.pc) | |
157 | return line | |
163 | return c.frame.PC | |
158 | 164 | } |
159 | 165 | |
160 | 166 | // CallStack records a sequence of function invocations from a goroutine |
178 | 184 | buf := bytes.Buffer{} |
179 | 185 | buf.Write(openBracketBytes) |
180 | 186 | for i, pc := range cs { |
181 | if pc.fn == nil { | |
182 | return nil, ErrNoFunc | |
183 | } | |
184 | 187 | if i > 0 { |
185 | 188 | buf.Write(spaceBytes) |
186 | 189 | } |
204 | 207 | s.Write(closeBracketBytes) |
205 | 208 | } |
206 | 209 | |
207 | // findSigpanic intentionally executes faulting code to generate a stack trace | |
208 | // containing an entry for runtime.sigpanic. | |
209 | func findSigpanic() *runtime.Func { | |
210 | var fn *runtime.Func | |
211 | var p *int | |
212 | func() int { | |
213 | defer func() { | |
214 | if p := recover(); p != nil { | |
215 | var pcs [512]uintptr | |
216 | n := runtime.Callers(2, pcs[:]) | |
217 | for _, pc := range pcs[:n] { | |
218 | f := runtime.FuncForPC(pc) | |
219 | if f.Name() == "runtime.sigpanic" { | |
220 | fn = f | |
221 | break | |
222 | } | |
223 | } | |
224 | } | |
225 | }() | |
226 | // intentional nil pointer dereference to trigger sigpanic | |
227 | return *p | |
228 | }() | |
229 | return fn | |
230 | } | |
231 | ||
232 | var sigpanic = findSigpanic() | |
233 | ||
234 | 210 | // Trace returns a CallStack for the current goroutine with element 0 |
235 | 211 | // identifying the calling function. |
236 | 212 | func Trace() CallStack { |
237 | 213 | var pcs [512]uintptr |
238 | n := runtime.Callers(2, pcs[:]) | |
239 | cs := make([]Call, n) | |
240 | ||
241 | for i, pc := range pcs[:n] { | |
242 | pcFix := pc | |
243 | if i > 0 && cs[i-1].fn != sigpanic { | |
244 | pcFix-- | |
245 | } | |
246 | cs[i] = Call{ | |
247 | fn: runtime.FuncForPC(pcFix), | |
248 | pc: pcFix, | |
249 | } | |
214 | n := runtime.Callers(1, pcs[:]) | |
215 | ||
216 | frames := runtime.CallersFrames(pcs[:n]) | |
217 | cs := make(CallStack, 0, n) | |
218 | ||
219 | // Skip extra frame retrieved just to make sure the runtime.sigpanic | |
220 | // special case is handled. | |
221 | frame, more := frames.Next() | |
222 | ||
223 | for more { | |
224 | frame, more = frames.Next() | |
225 | cs = append(cs, Call{frame: frame}) | |
250 | 226 | } |
251 | 227 | |
252 | 228 | return cs |
255 | 231 | // TrimBelow returns a slice of the CallStack with all entries below c |
256 | 232 | // removed. |
257 | 233 | func (cs CallStack) TrimBelow(c Call) CallStack { |
258 | for len(cs) > 0 && cs[0].pc != c.pc { | |
234 | for len(cs) > 0 && cs[0] != c { | |
259 | 235 | cs = cs[1:] |
260 | 236 | } |
261 | 237 | return cs |
264 | 240 | // TrimAbove returns a slice of the CallStack with all entries above c |
265 | 241 | // removed. |
266 | 242 | func (cs CallStack) TrimAbove(c Call) CallStack { |
267 | for len(cs) > 0 && cs[len(cs)-1].pc != c.pc { | |
243 | for len(cs) > 0 && cs[len(cs)-1] != c { | |
268 | 244 | cs = cs[:len(cs)-1] |
269 | 245 | } |
270 | 246 | return cs |
310 | 286 | return i + len(sep) |
311 | 287 | } |
312 | 288 | |
289 | // pkgFilePath returns the frame's filepath relative to the compile-time GOPATH, | |
290 | // or its module path joined to its path relative to the module root. | |
291 | // | |
292 | // As of Go 1.11 there is no direct way to know the compile time GOPATH or | |
293 | // module paths at runtime, but we can piece together the desired information | |
294 | // from available information. We note that runtime.Frame.Function contains the | |
295 | // function name qualified by the package path, which includes the module path | |
296 | // but not the GOPATH. We can extract the package path from that and append the | |
297 | // last segments of the file path to arrive at the desired package qualified | |
298 | // file path. For example, given: | |
299 | // | |
300 | // GOPATH /home/user | |
301 | // import path pkg/sub | |
302 | // frame.File /home/user/src/pkg/sub/file.go | |
303 | // frame.Function pkg/sub.Type.Method | |
304 | // Desired return pkg/sub/file.go | |
305 | // | |
306 | // It appears that we simply need to trim ".Type.Method" from frame.Function and | |
307 | // append "/" + path.Base(file). | |
308 | // | |
309 | // But there are other wrinkles. Although it is idiomatic to do so, the internal | |
310 | // name of a package is not required to match the last segment of its import | |
311 | // path. In addition, the introduction of modules in Go 1.11 allows working | |
312 | // without a GOPATH. So we also must make these work right: | |
313 | // | |
314 | // GOPATH /home/user | |
315 | // import path pkg/go-sub | |
316 | // package name sub | |
317 | // frame.File /home/user/src/pkg/go-sub/file.go | |
318 | // frame.Function pkg/sub.Type.Method | |
319 | // Desired return pkg/go-sub/file.go | |
320 | // | |
321 | // Module path pkg/v2 | |
322 | // import path pkg/v2/go-sub | |
323 | // package name sub | |
324 | // frame.File /home/user/cloned-pkg/go-sub/file.go | |
325 | // frame.Function pkg/v2/sub.Type.Method | |
326 | // Desired return pkg/v2/go-sub/file.go | |
327 | // | |
328 | // We can handle all of these situations by using the package path extracted | |
329 | // from frame.Function up to, but not including, the last segment as the prefix | |
330 | // and the last two segments of frame.File as the suffix of the returned path. | |
331 | // This preserves the existing behavior when working in a GOPATH without modules | |
332 | // and a semantically equivalent behavior when used in module aware project. | |
333 | func pkgFilePath(frame *runtime.Frame) string { | |
334 | pre := pkgPrefix(frame.Function) | |
335 | post := pathSuffix(frame.File) | |
336 | if pre == "" { | |
337 | return post | |
338 | } | |
339 | return pre + "/" + post | |
340 | } | |
341 | ||
342 | // pkgPrefix returns the import path of the function's package with the final | |
343 | // segment removed. | |
344 | func pkgPrefix(funcName string) string { | |
345 | const pathSep = "/" | |
346 | end := strings.LastIndex(funcName, pathSep) | |
347 | if end == -1 { | |
348 | return "" | |
349 | } | |
350 | return funcName[:end] | |
351 | } | |
352 | ||
353 | // pathSuffix returns the last two segments of path. | |
354 | func pathSuffix(path string) string { | |
355 | const pathSep = "/" | |
356 | lastSep := strings.LastIndex(path, pathSep) | |
357 | if lastSep == -1 { | |
358 | return path | |
359 | } | |
360 | return path[strings.LastIndex(path[:lastSep], pathSep)+1:] | |
361 | } | |
362 | ||
313 | 363 | var runtimePath string |
314 | 364 | |
315 | 365 | func init() { |
316 | var pcs [1]uintptr | |
366 | var pcs [3]uintptr | |
317 | 367 | runtime.Callers(0, pcs[:]) |
318 | fn := runtime.FuncForPC(pcs[0]) | |
319 | file, _ := fn.FileLine(pcs[0]) | |
320 | ||
321 | idx := pkgIndex(file, fn.Name()) | |
368 | frames := runtime.CallersFrames(pcs[:]) | |
369 | frame, _ := frames.Next() | |
370 | file := frame.File | |
371 | ||
372 | idx := pkgIndex(frame.File, frame.Function) | |
322 | 373 | |
323 | 374 | runtimePath = file[:idx] |
324 | 375 | if runtime.GOOS == "windows" { |
327 | 378 | } |
328 | 379 | |
329 | 380 | func inGoroot(c Call) bool { |
330 | file := c.file() | |
381 | file := c.frame.File | |
331 | 382 | if len(file) == 0 || file[0] == '?' { |
332 | 383 | return true |
333 | 384 | } |
12 | 12 | "github.com/go-stack/stack" |
13 | 13 | ) |
14 | 14 | |
15 | func TestCaller(t *testing.T) { | |
16 | t.Parallel() | |
17 | ||
18 | c := stack.Caller(0) | |
19 | _, file, line, ok := runtime.Caller(0) | |
20 | line-- | |
21 | if !ok { | |
22 | t.Fatal("runtime.Caller(0) failed") | |
23 | } | |
24 | ||
25 | if got, want := c.Frame().File, file; got != want { | |
26 | t.Errorf("got file == %v, want file == %v", got, want) | |
27 | } | |
28 | ||
29 | if got, want := c.Frame().Line, line; got != want { | |
30 | t.Errorf("got line == %v, want line == %v", got, want) | |
31 | } | |
32 | } | |
33 | ||
34 | func f3(f1 func() stack.Call) stack.Call { | |
35 | return f2(f1) | |
36 | } | |
37 | ||
38 | func f2(f1 func() stack.Call) stack.Call { | |
39 | return f1() | |
40 | } | |
41 | ||
42 | func TestCallerMidstackInlined(t *testing.T) { | |
43 | t.Parallel() | |
44 | ||
45 | _, _, line, ok := runtime.Caller(0) | |
46 | line -= 10 // adjust to return f1() line inside f2() | |
47 | if !ok { | |
48 | t.Fatal("runtime.Caller(0) failed") | |
49 | } | |
50 | ||
51 | c := f3(func() stack.Call { | |
52 | return stack.Caller(2) | |
53 | }) | |
54 | ||
55 | if got, want := c.Frame().Line, line; got != want { | |
56 | t.Errorf("got line == %v, want line == %v", got, want) | |
57 | } | |
58 | if got, want := c.Frame().Function, "github.com/go-stack/stack_test.f3"; got != want { | |
59 | t.Errorf("got func name == %v, want func name == %v", got, want) | |
60 | } | |
61 | } | |
62 | ||
63 | func TestCallerPanic(t *testing.T) { | |
64 | t.Parallel() | |
65 | ||
66 | var ( | |
67 | line int | |
68 | ok bool | |
69 | ) | |
70 | ||
71 | defer func() { | |
72 | if recover() != nil { | |
73 | var pcs [32]uintptr | |
74 | n := runtime.Callers(1, pcs[:]) | |
75 | frames := runtime.CallersFrames(pcs[:n]) | |
76 | // count frames to runtime.sigpanic | |
77 | panicIdx := 0 | |
78 | for { | |
79 | f, more := frames.Next() | |
80 | if f.Function == "runtime.sigpanic" { | |
81 | break | |
82 | } | |
83 | panicIdx++ | |
84 | if !more { | |
85 | t.Fatal("no runtime.sigpanic entry on the stack") | |
86 | } | |
87 | } | |
88 | c := stack.Caller(panicIdx) | |
89 | if got, want := c.Frame().Function, "runtime.sigpanic"; got != want { | |
90 | t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want) | |
91 | } | |
92 | c1 := stack.Caller(panicIdx + 1) | |
93 | if got, want := c1.Frame().Function, "github.com/go-stack/stack_test.TestCallerPanic"; got != want { | |
94 | t.Errorf("TestCallerPanic frame: got name == %v, want name == %v", got, want) | |
95 | } | |
96 | if got, want := c1.Frame().Line, line; got != want { | |
97 | t.Errorf("TestCallerPanic frame: got line == %v, want line == %v", got, want) | |
98 | } | |
99 | } | |
100 | }() | |
101 | ||
102 | _, _, line, ok = runtime.Caller(0) | |
103 | line += 7 // adjust to match line of panic below | |
104 | if !ok { | |
105 | t.Fatal("runtime.Caller(0) failed") | |
106 | } | |
107 | // Initiate a sigpanic. | |
108 | var x *uintptr | |
109 | _ = *x | |
110 | } | |
111 | ||
112 | type tholder struct { | |
113 | trace func() stack.CallStack | |
114 | } | |
115 | ||
116 | func (th *tholder) traceLabyrinth() stack.CallStack { | |
117 | for { | |
118 | return th.trace() | |
119 | } | |
120 | } | |
121 | ||
122 | func TestTrace(t *testing.T) { | |
123 | t.Parallel() | |
124 | ||
125 | _, _, line, ok := runtime.Caller(0) | |
126 | if !ok { | |
127 | t.Fatal("runtime.Caller(0) failed") | |
128 | } | |
129 | ||
130 | fh := tholder{ | |
131 | trace: func() stack.CallStack { | |
132 | cs := stack.Trace() | |
133 | return cs | |
134 | }, | |
135 | } | |
136 | ||
137 | cs := fh.traceLabyrinth() | |
138 | ||
139 | lines := []int{line + 7, line - 7, line + 12} | |
140 | ||
141 | for i, line := range lines { | |
142 | if got, want := cs[i].Frame().Line, line; got != want { | |
143 | t.Errorf("got line[%d] == %v, want line[%d] == %v", i, got, i, want) | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
148 | // Test stack handling originating from a sigpanic. | |
149 | func TestTracePanic(t *testing.T) { | |
150 | t.Parallel() | |
151 | ||
152 | var ( | |
153 | line int | |
154 | ok bool | |
155 | ) | |
156 | ||
157 | defer func() { | |
158 | if recover() != nil { | |
159 | trace := stack.Trace() | |
160 | ||
161 | // find runtime.sigpanic | |
162 | panicIdx := -1 | |
163 | for i, c := range trace { | |
164 | if c.Frame().Function == "runtime.sigpanic" { | |
165 | panicIdx = i | |
166 | break | |
167 | } | |
168 | } | |
169 | if panicIdx == -1 { | |
170 | t.Fatal("no runtime.sigpanic entry on the stack") | |
171 | } | |
172 | if got, want := trace[panicIdx].Frame().Function, "runtime.sigpanic"; got != want { | |
173 | t.Errorf("sigpanic frame: got name == %v, want name == %v", got, want) | |
174 | } | |
175 | if got, want := trace[panicIdx+1].Frame().Function, "github.com/go-stack/stack_test.TestTracePanic"; got != want { | |
176 | t.Errorf("TestTracePanic frame: got name == %v, want name == %v", got, want) | |
177 | } | |
178 | if got, want := trace[panicIdx+1].Frame().Line, line; got != want { | |
179 | t.Errorf("TestTracePanic frame: got line == %v, want line == %v", got, want) | |
180 | } | |
181 | } | |
182 | }() | |
183 | ||
184 | _, _, line, ok = runtime.Caller(0) | |
185 | line += 7 // adjust to match line of panic below | |
186 | if !ok { | |
187 | t.Fatal("runtime.Caller(0) failed") | |
188 | } | |
189 | // Initiate a sigpanic. | |
190 | var x *uintptr | |
191 | _ = *x | |
192 | } | |
193 | ||
15 | 194 | const importPath = "github.com/go-stack/stack" |
16 | 195 | |
17 | 196 | type testType struct{} |
18 | 197 | |
19 | func (tt testType) testMethod() (c stack.Call, pc uintptr, file string, line int, ok bool) { | |
198 | func (tt testType) testMethod() (c stack.Call, file string, line int, ok bool) { | |
20 | 199 | c = stack.Caller(0) |
21 | pc, file, line, ok = runtime.Caller(0) | |
200 | _, file, line, ok = runtime.Caller(0) | |
22 | 201 | line-- |
23 | 202 | return |
24 | 203 | } |
27 | 206 | t.Parallel() |
28 | 207 | |
29 | 208 | c := stack.Caller(0) |
30 | pc, file, line, ok := runtime.Caller(0) | |
209 | _, file, line, ok := runtime.Caller(0) | |
31 | 210 | line-- |
32 | 211 | if !ok { |
33 | 212 | t.Fatal("runtime.Caller(0) failed") |
34 | 213 | } |
35 | 214 | relFile := path.Join(importPath, filepath.Base(file)) |
36 | 215 | |
37 | c2, pc2, file2, line2, ok2 := testType{}.testMethod() | |
216 | c2, file2, line2, ok2 := testType{}.testMethod() | |
38 | 217 | if !ok2 { |
39 | 218 | t.Fatal("runtime.Caller(0) failed") |
40 | 219 | } |
53 | 232 | {c, "func", "%#s", file}, |
54 | 233 | {c, "func", "%d", fmt.Sprint(line)}, |
55 | 234 | {c, "func", "%n", "TestCallFormat"}, |
56 | {c, "func", "%+n", runtime.FuncForPC(pc - 1).Name()}, | |
235 | {c, "func", "%+n", "github.com/go-stack/stack_test.TestCallFormat"}, | |
236 | {c, "func", "%k", "stack_test"}, | |
237 | {c, "func", "%+k", "github.com/go-stack/stack_test"}, | |
57 | 238 | {c, "func", "%v", fmt.Sprint(path.Base(file), ":", line)}, |
58 | 239 | {c, "func", "%+v", fmt.Sprint(relFile, ":", line)}, |
59 | 240 | {c, "func", "%#v", fmt.Sprint(file, ":", line)}, |
63 | 244 | {c2, "meth", "%#s", file2}, |
64 | 245 | {c2, "meth", "%d", fmt.Sprint(line2)}, |
65 | 246 | {c2, "meth", "%n", "testType.testMethod"}, |
66 | {c2, "meth", "%+n", runtime.FuncForPC(pc2).Name()}, | |
247 | {c2, "meth", "%+n", "github.com/go-stack/stack_test.testType.testMethod"}, | |
248 | {c2, "meth", "%k", "stack_test"}, | |
249 | {c2, "meth", "%+k", "github.com/go-stack/stack_test"}, | |
67 | 250 | {c2, "meth", "%v", fmt.Sprint(path.Base(file2), ":", line2)}, |
68 | 251 | {c2, "meth", "%+v", fmt.Sprint(relFile2, ":", line2)}, |
69 | 252 | {c2, "meth", "%#v", fmt.Sprint(file2, ":", line2)}, |
87 | 270 | t.Fatal("runtime.Caller(0) failed") |
88 | 271 | } |
89 | 272 | |
90 | c2, _, file2, line2, ok2 := testType{}.testMethod() | |
273 | c2, file2, line2, ok2 := testType{}.testMethod() | |
91 | 274 | if !ok2 { |
92 | 275 | t.Fatal("runtime.Caller(0) failed") |
93 | 276 | } |
120 | 303 | t.Fatal("runtime.Caller(0) failed") |
121 | 304 | } |
122 | 305 | |
123 | c2, _, file2, line2, ok2 := testType{}.testMethod() | |
306 | c2, file2, line2, ok2 := testType{}.testMethod() | |
124 | 307 | if !ok2 { |
125 | 308 | t.Fatal("runtime.Caller(0) failed") |
126 | 309 | } |
173 | 356 | t.Errorf("\n got %v\nwant %v", got, want) |
174 | 357 | } |
175 | 358 | } |
359 | ||
176 | 360 | func getTrace(t *testing.T) (stack.CallStack, int) { |
177 | 361 | cs := stack.Trace().TrimRuntime() |
178 | 362 | _, _, line, ok := runtime.Caller(0) |
186 | 370 | func TestTrimAbove(t *testing.T) { |
187 | 371 | trace := trimAbove() |
188 | 372 | if got, want := len(trace), 2; got != want { |
189 | t.Errorf("got len(trace) == %v, want %v, trace: %n", got, want, trace) | |
373 | t.Fatalf("got len(trace) == %v, want %v, trace: %n", got, want, trace) | |
190 | 374 | } |
191 | 375 | if got, want := fmt.Sprintf("%n", trace[1]), "TestTrimAbove"; got != want { |
192 | 376 | t.Errorf("got %q, want %q", got, want) |
0 | package stack | |
1 | ||
2 | import ( | |
3 | "runtime" | |
4 | "testing" | |
5 | ) | |
6 | ||
7 | func TestFindSigpanic(t *testing.T) { | |
8 | t.Parallel() | |
9 | sp := findSigpanic() | |
10 | if got, want := sp.Name(), "runtime.sigpanic"; got != want { | |
11 | t.Errorf("got == %v, want == %v", got, want) | |
12 | } | |
13 | } | |
14 | ||
15 | func TestCaller(t *testing.T) { | |
16 | t.Parallel() | |
17 | ||
18 | c := Caller(0) | |
19 | _, file, line, ok := runtime.Caller(0) | |
20 | line-- | |
21 | if !ok { | |
22 | t.Fatal("runtime.Caller(0) failed") | |
23 | } | |
24 | ||
25 | if got, want := c.file(), file; got != want { | |
26 | t.Errorf("got file == %v, want file == %v", got, want) | |
27 | } | |
28 | ||
29 | if got, want := c.line(), line; got != want { | |
30 | t.Errorf("got line == %v, want line == %v", got, want) | |
31 | } | |
32 | } | |
33 | ||
34 | type fholder struct { | |
35 | f func() CallStack | |
36 | } | |
37 | ||
38 | func (fh *fholder) labyrinth() CallStack { | |
39 | for { | |
40 | return fh.f() | |
41 | } | |
42 | panic("this line only needed for go 1.0") | |
43 | } | |
44 | ||
45 | func TestTrace(t *testing.T) { | |
46 | t.Parallel() | |
47 | ||
48 | fh := fholder{ | |
49 | f: func() CallStack { | |
50 | cs := Trace() | |
51 | return cs | |
52 | }, | |
53 | } | |
54 | ||
55 | cs := fh.labyrinth() | |
56 | ||
57 | lines := []int{51, 41, 56} | |
58 | ||
59 | for i, line := range lines { | |
60 | if got, want := cs[i].line(), line; got != want { | |
61 | t.Errorf("got line[%d] == %v, want line[%d] == %v", i, got, i, want) | |
62 | } | |
63 | } | |
64 | } |