]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/crash_test.go
[dev.boringcrypto] all: merge master into dev.boringcrypto
[gostls13.git] / src / runtime / crash_test.go
1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package runtime_test
6
7 import (
8         "bytes"
9         "flag"
10         "fmt"
11         "internal/testenv"
12         "io/ioutil"
13         "os"
14         "os/exec"
15         "path/filepath"
16         "regexp"
17         "runtime"
18         "strconv"
19         "strings"
20         "sync"
21         "testing"
22         "time"
23 )
24
25 var toRemove []string
26
27 func TestMain(m *testing.M) {
28         status := m.Run()
29         for _, file := range toRemove {
30                 os.RemoveAll(file)
31         }
32         os.Exit(status)
33 }
34
35 var testprog struct {
36         sync.Mutex
37         dir    string
38         target map[string]buildexe
39 }
40
41 type buildexe struct {
42         exe string
43         err error
44 }
45
46 func runTestProg(t *testing.T, binary, name string, env ...string) string {
47         if *flagQuick {
48                 t.Skip("-quick")
49         }
50
51         testenv.MustHaveGoBuild(t)
52
53         exe, err := buildTestProg(t, binary)
54         if err != nil {
55                 t.Fatal(err)
56         }
57
58         return runBuiltTestProg(t, exe, name, env...)
59 }
60
61 func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
62         if *flagQuick {
63                 t.Skip("-quick")
64         }
65
66         testenv.MustHaveGoBuild(t)
67
68         cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
69         cmd.Env = append(cmd.Env, env...)
70         if testing.Short() {
71                 cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
72         }
73         var b bytes.Buffer
74         cmd.Stdout = &b
75         cmd.Stderr = &b
76         if err := cmd.Start(); err != nil {
77                 t.Fatalf("starting %s %s: %v", exe, name, err)
78         }
79
80         // If the process doesn't complete within 1 minute,
81         // assume it is hanging and kill it to get a stack trace.
82         p := cmd.Process
83         done := make(chan bool)
84         go func() {
85                 scale := 1
86                 // This GOARCH/GOOS test is copied from cmd/dist/test.go.
87                 // TODO(iant): Have cmd/dist update the environment variable.
88                 if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
89                         scale = 2
90                 }
91                 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
92                         if sc, err := strconv.Atoi(s); err == nil {
93                                 scale = sc
94                         }
95                 }
96
97                 select {
98                 case <-done:
99                 case <-time.After(time.Duration(scale) * time.Minute):
100                         p.Signal(sigquit)
101                 }
102         }()
103
104         if err := cmd.Wait(); err != nil {
105                 t.Logf("%s %s exit status: %v", exe, name, err)
106         }
107         close(done)
108
109         return b.String()
110 }
111
112 func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
113         if *flagQuick {
114                 t.Skip("-quick")
115         }
116
117         testprog.Lock()
118         defer testprog.Unlock()
119         if testprog.dir == "" {
120                 dir, err := ioutil.TempDir("", "go-build")
121                 if err != nil {
122                         t.Fatalf("failed to create temp directory: %v", err)
123                 }
124                 testprog.dir = dir
125                 toRemove = append(toRemove, dir)
126         }
127
128         if testprog.target == nil {
129                 testprog.target = make(map[string]buildexe)
130         }
131         name := binary
132         if len(flags) > 0 {
133                 name += "_" + strings.Join(flags, "_")
134         }
135         target, ok := testprog.target[name]
136         if ok {
137                 return target.exe, target.err
138         }
139
140         exe := filepath.Join(testprog.dir, name+".exe")
141         cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
142         cmd.Dir = "testdata/" + binary
143         out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
144         if err != nil {
145                 target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
146                 testprog.target[name] = target
147                 return "", target.err
148         }
149         target.exe = exe
150         testprog.target[name] = target
151         return exe, nil
152 }
153
154 func TestVDSO(t *testing.T) {
155         t.Parallel()
156         output := runTestProg(t, "testprog", "SignalInVDSO")
157         want := "success\n"
158         if output != want {
159                 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
160         }
161 }
162
163 func testCrashHandler(t *testing.T, cgo bool) {
164         type crashTest struct {
165                 Cgo bool
166         }
167         var output string
168         if cgo {
169                 output = runTestProg(t, "testprogcgo", "Crash")
170         } else {
171                 output = runTestProg(t, "testprog", "Crash")
172         }
173         want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
174         if output != want {
175                 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
176         }
177 }
178
179 func TestCrashHandler(t *testing.T) {
180         testCrashHandler(t, false)
181 }
182
183 func testDeadlock(t *testing.T, name string) {
184         // External linking brings in cgo, causing deadlock detection not working.
185         testenv.MustInternalLink(t)
186
187         output := runTestProg(t, "testprog", name)
188         want := "fatal error: all goroutines are asleep - deadlock!\n"
189         if !strings.HasPrefix(output, want) {
190                 t.Fatalf("output does not start with %q:\n%s", want, output)
191         }
192 }
193
194 func TestSimpleDeadlock(t *testing.T) {
195         testDeadlock(t, "SimpleDeadlock")
196 }
197
198 func TestInitDeadlock(t *testing.T) {
199         testDeadlock(t, "InitDeadlock")
200 }
201
202 func TestLockedDeadlock(t *testing.T) {
203         testDeadlock(t, "LockedDeadlock")
204 }
205
206 func TestLockedDeadlock2(t *testing.T) {
207         testDeadlock(t, "LockedDeadlock2")
208 }
209
210 func TestGoexitDeadlock(t *testing.T) {
211         // External linking brings in cgo, causing deadlock detection not working.
212         testenv.MustInternalLink(t)
213
214         output := runTestProg(t, "testprog", "GoexitDeadlock")
215         want := "no goroutines (main called runtime.Goexit) - deadlock!"
216         if !strings.Contains(output, want) {
217                 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
218         }
219 }
220
221 func TestStackOverflow(t *testing.T) {
222         output := runTestProg(t, "testprog", "StackOverflow")
223         want := []string{
224                 "runtime: goroutine stack exceeds 1474560-byte limit\n",
225                 "fatal error: stack overflow",
226                 // information about the current SP and stack bounds
227                 "runtime: sp=",
228                 "stack=[",
229         }
230         if !strings.HasPrefix(output, want[0]) {
231                 t.Errorf("output does not start with %q", want[0])
232         }
233         for _, s := range want[1:] {
234                 if !strings.Contains(output, s) {
235                         t.Errorf("output does not contain %q", s)
236                 }
237         }
238         if t.Failed() {
239                 t.Logf("output:\n%s", output)
240         }
241 }
242
243 func TestStackOverflowTopAndBottomTraces(t *testing.T) {
244         output := runTestProg(t, "testprog", "StackOverflowTopAndBottomTraces")
245
246         // 1. First things first, we expect to traverse from
247         //    runtime: goroutine stack exceeds 10000-byte limit
248         // and down to the very end until we see:
249         //    runtime.goexit()
250         mustHaves := []string{
251                 // Top half expectations
252                 "\\s*runtime: goroutine stack exceeds 10000-byte limit\n",
253                 "\\s*fatal error: stack overflow\n",
254                 "\\s*runtime stack:\n",
255                 "\\s*runtime.throw[^\n]+\n\t.+:\\d+ [^\n]+",
256                 "\\s+runtime\\.newstack[^\n]+\n\t.+:\\d+ [^\n]+",
257                 "\\s+runtime.morestack[^\n]+\n\t.+:\\d+ [^\n]+",
258                 "\\s+goroutine 1 \\[running\\]:",
259
260                 // Bottom half expectations
261                 "\\s*main.main\\(\\)\n",
262                 "\\s*runtime.main\\(\\)\n",
263                 "\\s*runtime.goexit\\(\\)\n",
264         }
265
266         for _, pat := range mustHaves {
267                 reg := regexp.MustCompile(pat)
268                 match := reg.FindAllString(output, -1)
269                 if len(match) == 0 {
270                         t.Errorf("Failed to find pattern %q", pat)
271                 }
272         }
273
274         // 2. Split top and bottom halves by the "... ({n} stack frames omitted)" message
275         regHalving := regexp.MustCompile("\\.{3} \\(\\d+ stack frames omitted\\)")
276         halverMatches := regHalving.FindAllString(output, -1)
277         if len(halverMatches) != 1 {
278                 t.Fatal("Failed to find the `stack frames omitted` pattern")
279         }
280         str := string(output)
281         halver := halverMatches[0]
282         midIndex := strings.Index(str, halver)
283         topHalf, bottomHalf := str[:midIndex], str[midIndex+len(halver):]
284         // 2.1. Sanity check, len(topHalf) >= halver || len(bottomHalf) >= halver
285         if len(topHalf) < len(halver) || len(bottomHalf) < len(halver) {
286                 t.Fatalf("Sanity check len(topHalf) = %d len(bottomHalf) = %d; both must be >= len(halver) %d",
287                         len(topHalf), len(bottomHalf), len(halver))
288         }
289
290         // 3. In each of the halves, we should have an equal number
291         // of stacktraces before and after the "omitted frames" message.
292         regStackTraces := regexp.MustCompile("\n[^\n]+\n\t.+:\\d+ .+ fp=0x.+ sp=0x.+ pc=0x.+")
293         topHalfStackTraces := regStackTraces.FindAllString(topHalf, -1)
294         bottomHalfStackTraces := regStackTraces.FindAllString(bottomHalf, -1)
295         nTopHalf, nBottomHalf := len(topHalfStackTraces), len(bottomHalfStackTraces)
296         if nTopHalf == 0 || nBottomHalf == 0 {
297                 t.Fatal("Both lengths of stack-halves should be non-zero")
298         }
299         // The bottom half will always have the 50 non-runtime frames along with these 3 frames:
300         // * main.main()
301         // * "runtime.main"
302         // * "runtime.goexit"
303         // hence we need to decrement 3 counted lines.
304         if nTopHalf != nBottomHalf-3 {
305                 t.Errorf("len(topHalfStackTraces)=%d len(bottomHalfStackTraces)-3=%d yet must be equal\n", nTopHalf, nBottomHalf-3)
306         }
307
308         // 4. Next, prune out the:
309         // func...
310         //    line...
311         // pairs in both of the halves.
312         prunes := []struct {
313                 src     *string
314                 matches []string
315         }{
316                 {src: &topHalf, matches: topHalfStackTraces},
317                 {src: &bottomHalf, matches: bottomHalfStackTraces},
318         }
319
320         for _, prune := range prunes {
321                 str := *prune.src
322                 for _, match := range prune.matches {
323                         index := strings.Index(str, match)
324                         str = str[:index] + str[index+len(match):]
325                 }
326                 *prune.src = str
327         }
328
329         // 5. Now match and prune out the remaining patterns in the top and bottom halves.
330         // We aren't touching the bottom stack since its patterns are already matched
331         // by the:
332         //    func...
333         //       line...
334         // pairs
335         topPartPrunables := []string{
336                 "^\\s*runtime: goroutine stack exceeds 10000-byte limit\n",
337                 "\\s*fatal error: stack overflow\n",
338                 "\\s*runtime stack:\n",
339                 "\\s*runtime.throw[^\n]+\n\t.+:\\d+ [^\n]+",
340                 "\\s+runtime\\.newstack[^\n]+\n\t.+:\\d+ [^\n]+",
341                 "\\s+runtime.morestack[^\n]+\n\t.+:\\d+ [^\n]+",
342                 "\\s+goroutine 1 \\[running\\]:",
343         }
344
345         for _, pat := range topPartPrunables {
346                 reg := regexp.MustCompile(pat)
347                 matches := reg.FindAllString(topHalf, -1)
348                 if len(matches) == 0 {
349                         t.Errorf("top stack traces do not contain pattern: %q", reg)
350                 } else if len(matches) != 1 {
351                         t.Errorf("inconsistent state got %d matches want only 1", len(matches))
352                 } else {
353                         match := matches[0]
354                         idx := strings.Index(topHalf, match)
355                         topHalf = topHalf[:idx] + topHalf[idx+len(match):]
356                 }
357         }
358
359         // 6. At the end we should only be left with
360         // newlines in both the top and bottom halves.
361         topHalf = strings.TrimSpace(topHalf)
362         bottomHalf = strings.TrimSpace(bottomHalf)
363         if topHalf != "" && bottomHalf != "" {
364                 t.Fatalf("len(topHalf)=%d len(bottomHalf)=%d\ntopHalf=\n%s\n\nbottomHalf=\n%s",
365                         len(topHalf), len(bottomHalf), topHalf, bottomHalf)
366         }
367 }
368
369 func TestThreadExhaustion(t *testing.T) {
370         output := runTestProg(t, "testprog", "ThreadExhaustion")
371         want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
372         if !strings.HasPrefix(output, want) {
373                 t.Fatalf("output does not start with %q:\n%s", want, output)
374         }
375 }
376
377 func TestRecursivePanic(t *testing.T) {
378         output := runTestProg(t, "testprog", "RecursivePanic")
379         want := `wrap: bad
380 panic: again
381
382 `
383         if !strings.HasPrefix(output, want) {
384                 t.Fatalf("output does not start with %q:\n%s", want, output)
385         }
386
387 }
388
389 func TestRecursivePanic2(t *testing.T) {
390         output := runTestProg(t, "testprog", "RecursivePanic2")
391         want := `first panic
392 second panic
393 panic: third panic
394
395 `
396         if !strings.HasPrefix(output, want) {
397                 t.Fatalf("output does not start with %q:\n%s", want, output)
398         }
399
400 }
401
402 func TestRecursivePanic3(t *testing.T) {
403         output := runTestProg(t, "testprog", "RecursivePanic3")
404         want := `panic: first panic
405
406 `
407         if !strings.HasPrefix(output, want) {
408                 t.Fatalf("output does not start with %q:\n%s", want, output)
409         }
410
411 }
412
413 func TestRecursivePanic4(t *testing.T) {
414         output := runTestProg(t, "testprog", "RecursivePanic4")
415         want := `panic: first panic [recovered]
416         panic: second panic
417 `
418         if !strings.HasPrefix(output, want) {
419                 t.Fatalf("output does not start with %q:\n%s", want, output)
420         }
421
422 }
423
424 func TestGoexitCrash(t *testing.T) {
425         // External linking brings in cgo, causing deadlock detection not working.
426         testenv.MustInternalLink(t)
427
428         output := runTestProg(t, "testprog", "GoexitExit")
429         want := "no goroutines (main called runtime.Goexit) - deadlock!"
430         if !strings.Contains(output, want) {
431                 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
432         }
433 }
434
435 func TestGoexitDefer(t *testing.T) {
436         c := make(chan struct{})
437         go func() {
438                 defer func() {
439                         r := recover()
440                         if r != nil {
441                                 t.Errorf("non-nil recover during Goexit")
442                         }
443                         c <- struct{}{}
444                 }()
445                 runtime.Goexit()
446         }()
447         // Note: if the defer fails to run, we will get a deadlock here
448         <-c
449 }
450
451 func TestGoNil(t *testing.T) {
452         output := runTestProg(t, "testprog", "GoNil")
453         want := "go of nil func value"
454         if !strings.Contains(output, want) {
455                 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
456         }
457 }
458
459 func TestMainGoroutineID(t *testing.T) {
460         output := runTestProg(t, "testprog", "MainGoroutineID")
461         want := "panic: test\n\ngoroutine 1 [running]:\n"
462         if !strings.HasPrefix(output, want) {
463                 t.Fatalf("output does not start with %q:\n%s", want, output)
464         }
465 }
466
467 func TestNoHelperGoroutines(t *testing.T) {
468         output := runTestProg(t, "testprog", "NoHelperGoroutines")
469         matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
470         if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
471                 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
472         }
473 }
474
475 func TestBreakpoint(t *testing.T) {
476         output := runTestProg(t, "testprog", "Breakpoint")
477         // If runtime.Breakpoint() is inlined, then the stack trace prints
478         // "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
479         want := "runtime.Breakpoint("
480         if !strings.Contains(output, want) {
481                 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
482         }
483 }
484
485 func TestGoexitInPanic(t *testing.T) {
486         // External linking brings in cgo, causing deadlock detection not working.
487         testenv.MustInternalLink(t)
488
489         // see issue 8774: this code used to trigger an infinite recursion
490         output := runTestProg(t, "testprog", "GoexitInPanic")
491         want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
492         if !strings.HasPrefix(output, want) {
493                 t.Fatalf("output does not start with %q:\n%s", want, output)
494         }
495 }
496
497 // Issue 14965: Runtime panics should be of type runtime.Error
498 func TestRuntimePanicWithRuntimeError(t *testing.T) {
499         testCases := [...]func(){
500                 0: func() {
501                         var m map[uint64]bool
502                         m[1234] = true
503                 },
504                 1: func() {
505                         ch := make(chan struct{})
506                         close(ch)
507                         close(ch)
508                 },
509                 2: func() {
510                         var ch = make(chan struct{})
511                         close(ch)
512                         ch <- struct{}{}
513                 },
514                 3: func() {
515                         var s = make([]int, 2)
516                         _ = s[2]
517                 },
518                 4: func() {
519                         n := -1
520                         _ = make(chan bool, n)
521                 },
522                 5: func() {
523                         close((chan bool)(nil))
524                 },
525         }
526
527         for i, fn := range testCases {
528                 got := panicValue(fn)
529                 if _, ok := got.(runtime.Error); !ok {
530                         t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
531                 }
532         }
533 }
534
535 func panicValue(fn func()) (recovered interface{}) {
536         defer func() {
537                 recovered = recover()
538         }()
539         fn()
540         return
541 }
542
543 func TestPanicAfterGoexit(t *testing.T) {
544         // an uncaught panic should still work after goexit
545         output := runTestProg(t, "testprog", "PanicAfterGoexit")
546         want := "panic: hello"
547         if !strings.HasPrefix(output, want) {
548                 t.Fatalf("output does not start with %q:\n%s", want, output)
549         }
550 }
551
552 func TestRecoveredPanicAfterGoexit(t *testing.T) {
553         // External linking brings in cgo, causing deadlock detection not working.
554         testenv.MustInternalLink(t)
555
556         output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
557         want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
558         if !strings.HasPrefix(output, want) {
559                 t.Fatalf("output does not start with %q:\n%s", want, output)
560         }
561 }
562
563 func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
564         // External linking brings in cgo, causing deadlock detection not working.
565         testenv.MustInternalLink(t)
566
567         t.Parallel()
568         output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
569         want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
570         if !strings.HasPrefix(output, want) {
571                 t.Fatalf("output does not start with %q:\n%s", want, output)
572         }
573 }
574
575 func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
576         // External linking brings in cgo, causing deadlock detection not working.
577         testenv.MustInternalLink(t)
578
579         t.Parallel()
580         output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
581         want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
582         if !strings.HasPrefix(output, want) {
583                 t.Fatalf("output does not start with %q:\n%s", want, output)
584         }
585 }
586
587 func TestNetpollDeadlock(t *testing.T) {
588         if os.Getenv("GO_BUILDER_NAME") == "darwin-amd64-10_12" {
589                 // A suspected kernel bug in macOS 10.12 occasionally results in
590                 // an apparent deadlock when dialing localhost. The errors have not
591                 // been observed on newer versions of the OS, so we don't plan to work
592                 // around them. See https://golang.org/issue/22019.
593                 testenv.SkipFlaky(t, 22019)
594         }
595
596         t.Parallel()
597         output := runTestProg(t, "testprognet", "NetpollDeadlock")
598         want := "done\n"
599         if !strings.HasSuffix(output, want) {
600                 t.Fatalf("output does not start with %q:\n%s", want, output)
601         }
602 }
603
604 func TestPanicTraceback(t *testing.T) {
605         t.Parallel()
606         output := runTestProg(t, "testprog", "PanicTraceback")
607         want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
608         if !strings.HasPrefix(output, want) {
609                 t.Fatalf("output does not start with %q:\n%s", want, output)
610         }
611
612         // Check functions in the traceback.
613         fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
614         for _, fn := range fns {
615                 re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
616                 idx := re.FindStringIndex(output)
617                 if idx == nil {
618                         t.Fatalf("expected %q function in traceback:\n%s", fn, output)
619                 }
620                 output = output[idx[1]:]
621         }
622 }
623
624 func testPanicDeadlock(t *testing.T, name string, want string) {
625         // test issue 14432
626         output := runTestProg(t, "testprog", name)
627         if !strings.HasPrefix(output, want) {
628                 t.Fatalf("output does not start with %q:\n%s", want, output)
629         }
630 }
631
632 func TestPanicDeadlockGosched(t *testing.T) {
633         testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
634 }
635
636 func TestPanicDeadlockSyscall(t *testing.T) {
637         testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
638 }
639
640 func TestPanicLoop(t *testing.T) {
641         output := runTestProg(t, "testprog", "PanicLoop")
642         if want := "panic while printing panic value"; !strings.Contains(output, want) {
643                 t.Errorf("output does not contain %q:\n%s", want, output)
644         }
645 }
646
647 func TestMemPprof(t *testing.T) {
648         testenv.MustHaveGoRun(t)
649
650         exe, err := buildTestProg(t, "testprog")
651         if err != nil {
652                 t.Fatal(err)
653         }
654
655         got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
656         if err != nil {
657                 t.Fatal(err)
658         }
659         fn := strings.TrimSpace(string(got))
660         defer os.Remove(fn)
661
662         for try := 0; try < 2; try++ {
663                 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
664                 // Check that pprof works both with and without explicit executable on command line.
665                 if try == 0 {
666                         cmd.Args = append(cmd.Args, exe, fn)
667                 } else {
668                         cmd.Args = append(cmd.Args, fn)
669                 }
670                 found := false
671                 for i, e := range cmd.Env {
672                         if strings.HasPrefix(e, "PPROF_TMPDIR=") {
673                                 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
674                                 found = true
675                                 break
676                         }
677                 }
678                 if !found {
679                         cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
680                 }
681
682                 top, err := cmd.CombinedOutput()
683                 t.Logf("%s:\n%s", cmd.Args, top)
684                 if err != nil {
685                         t.Error(err)
686                 } else if !bytes.Contains(top, []byte("MemProf")) {
687                         t.Error("missing MemProf in pprof output")
688                 }
689         }
690 }
691
692 var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
693
694 func TestConcurrentMapWrites(t *testing.T) {
695         if !*concurrentMapTest {
696                 t.Skip("skipping without -run_concurrent_map_tests")
697         }
698         testenv.MustHaveGoRun(t)
699         output := runTestProg(t, "testprog", "concurrentMapWrites")
700         want := "fatal error: concurrent map writes"
701         if !strings.HasPrefix(output, want) {
702                 t.Fatalf("output does not start with %q:\n%s", want, output)
703         }
704 }
705 func TestConcurrentMapReadWrite(t *testing.T) {
706         if !*concurrentMapTest {
707                 t.Skip("skipping without -run_concurrent_map_tests")
708         }
709         testenv.MustHaveGoRun(t)
710         output := runTestProg(t, "testprog", "concurrentMapReadWrite")
711         want := "fatal error: concurrent map read and map write"
712         if !strings.HasPrefix(output, want) {
713                 t.Fatalf("output does not start with %q:\n%s", want, output)
714         }
715 }
716 func TestConcurrentMapIterateWrite(t *testing.T) {
717         if !*concurrentMapTest {
718                 t.Skip("skipping without -run_concurrent_map_tests")
719         }
720         testenv.MustHaveGoRun(t)
721         output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
722         want := "fatal error: concurrent map iteration and map write"
723         if !strings.HasPrefix(output, want) {
724                 t.Fatalf("output does not start with %q:\n%s", want, output)
725         }
726 }
727
728 type point struct {
729         x, y *int
730 }
731
732 func (p *point) negate() {
733         *p.x = *p.x * -1
734         *p.y = *p.y * -1
735 }
736
737 // Test for issue #10152.
738 func TestPanicInlined(t *testing.T) {
739         defer func() {
740                 r := recover()
741                 if r == nil {
742                         t.Fatalf("recover failed")
743                 }
744                 buf := make([]byte, 2048)
745                 n := runtime.Stack(buf, false)
746                 buf = buf[:n]
747                 if !bytes.Contains(buf, []byte("(*point).negate(")) {
748                         t.Fatalf("expecting stack trace to contain call to (*point).negate()")
749                 }
750         }()
751
752         pt := new(point)
753         pt.negate()
754 }
755
756 // Test for issues #3934 and #20018.
757 // We want to delay exiting until a panic print is complete.
758 func TestPanicRace(t *testing.T) {
759         testenv.MustHaveGoRun(t)
760
761         exe, err := buildTestProg(t, "testprog")
762         if err != nil {
763                 t.Fatal(err)
764         }
765
766         // The test is intentionally racy, and in my testing does not
767         // produce the expected output about 0.05% of the time.
768         // So run the program in a loop and only fail the test if we
769         // get the wrong output ten times in a row.
770         const tries = 10
771 retry:
772         for i := 0; i < tries; i++ {
773                 got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
774                 if err == nil {
775                         t.Logf("try %d: program exited successfully, should have failed", i+1)
776                         continue
777                 }
778
779                 if i > 0 {
780                         t.Logf("try %d:\n", i+1)
781                 }
782                 t.Logf("%s\n", got)
783
784                 wants := []string{
785                         "panic: crash",
786                         "PanicRace",
787                         "created by ",
788                 }
789                 for _, want := range wants {
790                         if !bytes.Contains(got, []byte(want)) {
791                                 t.Logf("did not find expected string %q", want)
792                                 continue retry
793                         }
794                 }
795
796                 // Test generated expected output.
797                 return
798         }
799         t.Errorf("test ran %d times without producing expected output", tries)
800 }
801
802 func TestBadTraceback(t *testing.T) {
803         output := runTestProg(t, "testprog", "BadTraceback")
804         for _, want := range []string{
805                 "runtime: unexpected return pc",
806                 "called from 0xbad",
807                 "00000bad",    // Smashed LR in hex dump
808                 "<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
809         } {
810                 if !strings.Contains(output, want) {
811                         t.Errorf("output does not contain %q:\n%s", want, output)
812                 }
813         }
814 }
815
816 func TestTimePprof(t *testing.T) {
817         // Pass GOTRACEBACK for issue #41120 to try to get more
818         // information on timeout.
819         fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
820         fn = strings.TrimSpace(fn)
821         defer os.Remove(fn)
822
823         cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
824         cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
825         top, err := cmd.CombinedOutput()
826         t.Logf("%s", top)
827         if err != nil {
828                 t.Error(err)
829         } else if bytes.Contains(top, []byte("ExternalCode")) {
830                 t.Error("profiler refers to ExternalCode")
831         }
832 }
833
834 // Test that runtime.abort does so.
835 func TestAbort(t *testing.T) {
836         // Pass GOTRACEBACK to ensure we get runtime frames.
837         output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
838         if want := "runtime.abort"; !strings.Contains(output, want) {
839                 t.Errorf("output does not contain %q:\n%s", want, output)
840         }
841         if strings.Contains(output, "BAD") {
842                 t.Errorf("output contains BAD:\n%s", output)
843         }
844         // Check that it's a signal traceback.
845         want := "PC="
846         // For systems that use a breakpoint, check specifically for that.
847         switch runtime.GOARCH {
848         case "386", "amd64":
849                 switch runtime.GOOS {
850                 case "plan9":
851                         want = "sys: breakpoint"
852                 case "windows":
853                         want = "Exception 0x80000003"
854                 default:
855                         want = "SIGTRAP"
856                 }
857         }
858         if !strings.Contains(output, want) {
859                 t.Errorf("output does not contain %q:\n%s", want, output)
860         }
861 }
862
863 // For TestRuntimePanic: test a panic in the runtime package without
864 // involving the testing harness.
865 func init() {
866         if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
867                 defer func() {
868                         if r := recover(); r != nil {
869                                 // We expect to crash, so exit 0
870                                 // to indicate failure.
871                                 os.Exit(0)
872                         }
873                 }()
874                 runtime.PanicForTesting(nil, 1)
875                 // We expect to crash, so exit 0 to indicate failure.
876                 os.Exit(0)
877         }
878 }
879
880 func TestRuntimePanic(t *testing.T) {
881         testenv.MustHaveExec(t)
882         cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestRuntimePanic"))
883         cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
884         out, err := cmd.CombinedOutput()
885         t.Logf("%s", out)
886         if err == nil {
887                 t.Error("child process did not fail")
888         } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
889                 t.Errorf("output did not contain expected string %q", want)
890         }
891 }
892
893 // Test that g0 stack overflows are handled gracefully.
894 func TestG0StackOverflow(t *testing.T) {
895         testenv.MustHaveExec(t)
896
897         switch runtime.GOOS {
898         case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "android":
899                 t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)")
900         }
901
902         if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
903                 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestG0StackOverflow", "-test.v"))
904                 cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
905                 out, err := cmd.CombinedOutput()
906                 // Don't check err since it's expected to crash.
907                 if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
908                         t.Fatalf("%s\n(exit status %v)", out, err)
909                 }
910                 // Check that it's a signal-style traceback.
911                 if runtime.GOOS != "windows" {
912                         if want := "PC="; !strings.Contains(string(out), want) {
913                                 t.Errorf("output does not contain %q:\n%s", want, out)
914                         }
915                 }
916                 return
917         }
918
919         runtime.G0StackOverflow()
920 }
921
922 // Test that panic message is not clobbered.
923 // See issue 30150.
924 func TestDoublePanic(t *testing.T) {
925         output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
926         wants := []string{"panic: XXX", "panic: YYY"}
927         for _, want := range wants {
928                 if !strings.Contains(output, want) {
929                         t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
930                 }
931         }
932 }