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