blob: 8efce4da2d4d5b626e6163dafa205f5b6f1bec1e [file] [log] [blame]
Alex Brainmanafe0e972012-05-30 15:10:54 +10001// 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
5package runtime_test
6
7import (
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -08008 "fmt"
Russ Cox7bc3e582015-06-05 11:01:53 -04009 "internal/testenv"
Alex Brainmanafe0e972012-05-30 15:10:54 +100010 "io/ioutil"
11 "os"
12 "os/exec"
13 "path/filepath"
Dmitry Vyukov59495e82015-02-07 15:31:18 +030014 "regexp"
Russ Cox0c2a7272014-05-20 12:10:19 -040015 "runtime"
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040016 "strings"
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -080017 "sync"
Alex Brainmanafe0e972012-05-30 15:10:54 +100018 "testing"
19 "text/template"
20)
21
Albert Strasheim4235fa82013-04-07 11:37:37 -070022func testEnv(cmd *exec.Cmd) *exec.Cmd {
23 if cmd.Env != nil {
24 panic("environment already set")
25 }
26 for _, env := range os.Environ() {
Dmitry Vyukov59495e82015-02-07 15:31:18 +030027 // Exclude GODEBUG from the environment to prevent its output
28 // from breaking tests that are trying to parse other command output.
Dmitriy Vyukov4b536a52013-06-28 18:37:06 +040029 if strings.HasPrefix(env, "GODEBUG=") {
Albert Strasheim4235fa82013-04-07 11:37:37 -070030 continue
31 }
Dmitry Vyukov59495e82015-02-07 15:31:18 +030032 // Exclude GOTRACEBACK for the same reason.
33 if strings.HasPrefix(env, "GOTRACEBACK=") {
34 continue
35 }
Albert Strasheim4235fa82013-04-07 11:37:37 -070036 cmd.Env = append(cmd.Env, env)
37 }
38 return cmd
39}
40
Russ Coxc4efaac2014-10-28 21:53:09 -040041func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
Russ Cox7bc3e582015-06-05 11:01:53 -040042 testenv.MustHaveGoBuild(t)
Russ Cox0c2a7272014-05-20 12:10:19 -040043
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040044 checkStaleRuntime(t)
Alex Brainmanafe0e972012-05-30 15:10:54 +100045
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040046 st := template.Must(template.New("crashSource").Parse(templ))
Alex Brainmanafe0e972012-05-30 15:10:54 +100047
48 dir, err := ioutil.TempDir("", "go-build")
49 if err != nil {
50 t.Fatalf("failed to create temp directory: %v", err)
51 }
52 defer os.RemoveAll(dir)
53
54 src := filepath.Join(dir, "main.go")
55 f, err := os.Create(src)
56 if err != nil {
Dmitriy Vyukov2791ef02013-08-12 22:04:10 +040057 t.Fatalf("failed to create file: %v", err)
Alex Brainmanafe0e972012-05-30 15:10:54 +100058 }
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040059 err = st.Execute(f, data)
Alex Brainmanafe0e972012-05-30 15:10:54 +100060 if err != nil {
61 f.Close()
62 t.Fatalf("failed to execute template: %v", err)
63 }
Dmitriy Vyukov2791ef02013-08-12 22:04:10 +040064 if err := f.Close(); err != nil {
65 t.Fatalf("failed to close file: %v", err)
66 }
Alex Brainmanafe0e972012-05-30 15:10:54 +100067
Russ Coxc4efaac2014-10-28 21:53:09 -040068 for i := 0; i < len(extra); i += 2 {
Alex Brainman9b691962015-03-16 15:46:22 +110069 fname := extra[i]
70 contents := extra[i+1]
71 if d, _ := filepath.Split(fname); d != "" {
72 if err := os.Mkdir(filepath.Join(dir, d), 0755); err != nil {
73 t.Fatal(err)
74 }
75 }
76 if err := ioutil.WriteFile(filepath.Join(dir, fname), []byte(contents), 0666); err != nil {
Russ Coxc4efaac2014-10-28 21:53:09 -040077 t.Fatal(err)
78 }
79 }
80
81 cmd := exec.Command("go", "build", "-o", "a.exe")
82 cmd.Dir = dir
83 out, err := testEnv(cmd).CombinedOutput()
84 if err != nil {
85 t.Fatalf("building source: %v\n%s", err, out)
86 }
87
88 got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040089 return string(got)
90}
91
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -080092var (
93 staleRuntimeOnce sync.Once // guards init of staleRuntimeErr
94 staleRuntimeErr error
95)
96
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040097func checkStaleRuntime(t *testing.T) {
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -080098 staleRuntimeOnce.Do(func() {
99 // 'go run' uses the installed copy of runtime.a, which may be out of date.
100 out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
101 if err != nil {
102 staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
103 return
104 }
105 if string(out) != "false\n" {
106 staleRuntimeErr = fmt.Errorf("Stale runtime.a. Run 'go install runtime'.")
107 }
108 })
109 if staleRuntimeErr != nil {
110 t.Fatal(staleRuntimeErr)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400111 }
112}
113
114func testCrashHandler(t *testing.T, cgo bool) {
115 type crashTest struct {
116 Cgo bool
117 }
Russ Cox757e0de2013-08-15 22:34:06 -0400118 output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
Alex Brainmanafe0e972012-05-30 15:10:54 +1000119 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
Russ Cox757e0de2013-08-15 22:34:06 -0400120 if output != want {
121 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
Alex Brainmanafe0e972012-05-30 15:10:54 +1000122 }
123}
124
125func TestCrashHandler(t *testing.T) {
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400126 testCrashHandler(t, false)
127}
128
129func testDeadlock(t *testing.T, source string) {
Russ Cox757e0de2013-08-15 22:34:06 -0400130 output := executeTest(t, source, nil)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400131 want := "fatal error: all goroutines are asleep - deadlock!\n"
Russ Cox757e0de2013-08-15 22:34:06 -0400132 if !strings.HasPrefix(output, want) {
133 t.Fatalf("output does not start with %q:\n%s", want, output)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400134 }
135}
136
137func TestSimpleDeadlock(t *testing.T) {
138 testDeadlock(t, simpleDeadlockSource)
139}
140
141func TestInitDeadlock(t *testing.T) {
142 testDeadlock(t, initDeadlockSource)
143}
144
145func TestLockedDeadlock(t *testing.T) {
146 testDeadlock(t, lockedDeadlockSource)
147}
148
149func TestLockedDeadlock2(t *testing.T) {
150 testDeadlock(t, lockedDeadlockSource2)
Alex Brainmanafe0e972012-05-30 15:10:54 +1000151}
152
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200153func TestGoexitDeadlock(t *testing.T) {
Russ Cox757e0de2013-08-15 22:34:06 -0400154 output := executeTest(t, goexitDeadlockSource, nil)
Russ Coxade6bc62014-04-16 13:12:18 -0400155 want := "no goroutines (main called runtime.Goexit) - deadlock!"
156 if !strings.Contains(output, want) {
157 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
Russ Cox757e0de2013-08-15 22:34:06 -0400158 }
159}
160
161func TestStackOverflow(t *testing.T) {
162 output := executeTest(t, stackOverflowSource, nil)
163 want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
164 if !strings.HasPrefix(output, want) {
165 t.Fatalf("output does not start with %q:\n%s", want, output)
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200166 }
167}
168
Russ Cox665feee2013-08-16 22:25:26 -0400169func TestThreadExhaustion(t *testing.T) {
170 output := executeTest(t, threadExhaustionSource, nil)
171 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
172 if !strings.HasPrefix(output, want) {
173 t.Fatalf("output does not start with %q:\n%s", want, output)
174 }
175}
176
Dmitriy Vyukovf946a7c2014-03-07 20:50:30 +0400177func TestRecursivePanic(t *testing.T) {
178 output := executeTest(t, recursivePanicSource, nil)
179 want := `wrap: bad
180panic: again
181
182`
183 if !strings.HasPrefix(output, want) {
184 t.Fatalf("output does not start with %q:\n%s", want, output)
185 }
186
187}
188
Russ Coxade6bc62014-04-16 13:12:18 -0400189func TestGoexitCrash(t *testing.T) {
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400190 output := executeTest(t, goexitExitSource, nil)
Russ Coxade6bc62014-04-16 13:12:18 -0400191 want := "no goroutines (main called runtime.Goexit) - deadlock!"
192 if !strings.Contains(output, want) {
193 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400194 }
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400195}
196
Keith Randall7e623162014-09-15 15:09:17 -0700197func TestGoexitDefer(t *testing.T) {
198 c := make(chan struct{})
199 go func() {
200 defer func() {
201 r := recover()
202 if r != nil {
203 t.Errorf("non-nil recover during Goexit")
204 }
205 c <- struct{}{}
206 }()
207 runtime.Goexit()
208 }()
209 // Note: if the defer fails to run, we will get a deadlock here
210 <-c
211}
212
Dmitriy Vyukovb5caa022014-05-28 00:00:01 -0400213func TestGoNil(t *testing.T) {
214 output := executeTest(t, goNilSource, nil)
215 want := "go of nil func value"
216 if !strings.Contains(output, want) {
217 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
218 }
219}
220
Dmitriy Vyukovaa763772014-07-16 12:19:33 +0400221func TestMainGoroutineId(t *testing.T) {
222 output := executeTest(t, mainGoroutineIdSource, nil)
223 want := "panic: test\n\ngoroutine 1 [running]:\n"
224 if !strings.HasPrefix(output, want) {
225 t.Fatalf("output does not start with %q:\n%s", want, output)
226 }
227}
228
Dmitry Vyukov59495e82015-02-07 15:31:18 +0300229func TestNoHelperGoroutines(t *testing.T) {
230 output := executeTest(t, noHelperGoroutinesSource, nil)
231 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
232 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
233 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
234 }
235}
236
Russ Cox1d550b82014-09-11 12:08:30 -0400237func TestBreakpoint(t *testing.T) {
238 output := executeTest(t, breakpointSource, nil)
239 want := "runtime.Breakpoint()"
240 if !strings.Contains(output, want) {
241 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
242 }
243}
244
Alex Brainmanafe0e972012-05-30 15:10:54 +1000245const crashSource = `
246package main
247
248import (
249 "fmt"
250 "runtime"
251)
252
253{{if .Cgo}}
254import "C"
255{{end}}
256
257func test(name string) {
258 defer func() {
259 if x := recover(); x != nil {
260 fmt.Printf(" recovered")
261 }
262 fmt.Printf(" done\n")
263 }()
264 fmt.Printf("%s:", name)
265 var s *string
266 _ = *s
267 fmt.Print("SHOULD NOT BE HERE")
268}
269
270func testInNewThread(name string) {
271 c := make(chan bool)
272 go func() {
273 runtime.LockOSThread()
274 test(name)
275 c <- true
276 }()
277 <-c
278}
279
280func main() {
281 runtime.LockOSThread()
282 test("main")
283 testInNewThread("new-thread")
284 testInNewThread("second-new-thread")
285 test("main-again")
286}
287`
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400288
289const simpleDeadlockSource = `
290package main
291func main() {
292 select {}
293}
294`
295
296const initDeadlockSource = `
297package main
298func init() {
299 select {}
300}
301func main() {
302}
303`
304
305const lockedDeadlockSource = `
306package main
307import "runtime"
308func main() {
309 runtime.LockOSThread()
310 select {}
311}
312`
313
314const lockedDeadlockSource2 = `
315package main
316import (
317 "runtime"
318 "time"
319)
320func main() {
321 go func() {
322 runtime.LockOSThread()
323 select {}
324 }()
325 time.Sleep(time.Millisecond)
326 select {}
327}
328`
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200329
330const goexitDeadlockSource = `
331package main
332import (
333 "runtime"
334)
335
336func F() {
337 for i := 0; i < 10; i++ {
338 }
339}
340
341func main() {
342 go F()
343 go F()
344 runtime.Goexit()
345}
346`
Russ Cox757e0de2013-08-15 22:34:06 -0400347
348const stackOverflowSource = `
349package main
350
351import "runtime/debug"
352
353func main() {
354 debug.SetMaxStack(4<<20)
355 f(make([]byte, 10))
356}
357
358func f(x []byte) byte {
359 var buf [64<<10]byte
360 return x[0] + f(buf[:])
361}
362`
Russ Cox665feee2013-08-16 22:25:26 -0400363
364const threadExhaustionSource = `
365package main
366
367import (
368 "runtime"
369 "runtime/debug"
370)
371
372func main() {
373 debug.SetMaxThreads(10)
374 c := make(chan int)
375 for i := 0; i < 100; i++ {
376 go func() {
377 runtime.LockOSThread()
378 c <- 0
379 select{}
380 }()
381 <-c
382 }
383}
384`
Dmitriy Vyukovf946a7c2014-03-07 20:50:30 +0400385
386const recursivePanicSource = `
387package main
388
389import (
390 "fmt"
391)
392
393func main() {
394 func() {
395 defer func() {
396 fmt.Println(recover())
397 }()
398 var x [8192]byte
399 func(x [8192]byte) {
400 defer func() {
401 if err := recover(); err != nil {
402 panic("wrap: " + err.(string))
403 }
404 }()
405 panic("bad")
406 }(x)
407 }()
408 panic("again")
409}
410`
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400411
412const goexitExitSource = `
413package main
414
415import (
416 "runtime"
417 "time"
418)
419
420func main() {
421 go func() {
422 time.Sleep(time.Millisecond)
423 }()
424 i := 0
425 runtime.SetFinalizer(&i, func(p *int) {})
426 runtime.GC()
427 runtime.Goexit()
428}
429`
Dmitriy Vyukovb5caa022014-05-28 00:00:01 -0400430
431const goNilSource = `
432package main
433
434func main() {
435 defer func() {
436 recover()
437 }()
438 var f func()
439 go f()
440 select{}
441}
442`
Dmitriy Vyukovaa763772014-07-16 12:19:33 +0400443
444const mainGoroutineIdSource = `
445package main
446func main() {
447 panic("test")
448}
449`
Russ Cox1d550b82014-09-11 12:08:30 -0400450
Dmitry Vyukov59495e82015-02-07 15:31:18 +0300451const noHelperGoroutinesSource = `
452package main
453import (
454 "runtime"
455 "time"
456)
457func init() {
458 i := 0
459 runtime.SetFinalizer(&i, func(p *int) {})
460 time.AfterFunc(time.Hour, func() {})
461 panic("oops")
462}
463func main() {
464}
465`
466
Russ Cox1d550b82014-09-11 12:08:30 -0400467const breakpointSource = `
468package main
469import "runtime"
470func main() {
471 runtime.Breakpoint()
472}
473`
Keith Randall03064782014-09-19 16:33:14 -0700474
475func TestGoexitInPanic(t *testing.T) {
476 // see issue 8774: this code used to trigger an infinite recursion
477 output := executeTest(t, goexitInPanicSource, nil)
478 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
479 if !strings.HasPrefix(output, want) {
480 t.Fatalf("output does not start with %q:\n%s", want, output)
481 }
482}
483
484const goexitInPanicSource = `
485package main
486import "runtime"
487func main() {
488 go func() {
489 defer func() {
490 runtime.Goexit()
491 }()
492 panic("hello")
493 }()
494 runtime.Goexit()
495}
496`
497
498func TestPanicAfterGoexit(t *testing.T) {
499 // an uncaught panic should still work after goexit
500 output := executeTest(t, panicAfterGoexitSource, nil)
501 want := "panic: hello"
502 if !strings.HasPrefix(output, want) {
503 t.Fatalf("output does not start with %q:\n%s", want, output)
504 }
505}
506
507const panicAfterGoexitSource = `
508package main
509import "runtime"
510func main() {
511 defer func() {
512 panic("hello")
513 }()
514 runtime.Goexit()
515}
516`
517
518func TestRecoveredPanicAfterGoexit(t *testing.T) {
519 output := executeTest(t, recoveredPanicAfterGoexitSource, nil)
520 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
521 if !strings.HasPrefix(output, want) {
522 t.Fatalf("output does not start with %q:\n%s", want, output)
523 }
524}
525
526const recoveredPanicAfterGoexitSource = `
527package main
528import "runtime"
529func main() {
530 defer func() {
531 defer func() {
532 r := recover()
533 if r == nil {
534 panic("bad recover")
535 }
536 }()
537 panic("hello")
538 }()
539 runtime.Goexit()
540}
541`
542
543func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
544 // 1. defer a function that recovers
545 // 2. defer a function that panics
546 // 3. call goexit
547 // Goexit should run the #2 defer. Its panic
548 // should be caught by the #1 defer, and execution
549 // should resume in the caller. Like the Goexit
550 // never happened!
551 defer func() {
552 r := recover()
553 if r == nil {
554 panic("bad recover")
555 }
556 }()
557 defer func() {
558 panic("hello")
559 }()
560 runtime.Goexit()
561}
Dmitry Vyukov776aeca2015-01-13 20:12:50 +0300562
563func TestNetpollDeadlock(t *testing.T) {
564 output := executeTest(t, netpollDeadlockSource, nil)
565 want := "done\n"
566 if !strings.HasSuffix(output, want) {
567 t.Fatalf("output does not start with %q:\n%s", want, output)
568 }
569}
570
571const netpollDeadlockSource = `
572package main
573import (
574 "fmt"
575 "net"
576)
577func init() {
578 fmt.Println("dialing")
579 c, err := net.Dial("tcp", "localhost:14356")
580 if err == nil {
581 c.Close()
582 } else {
583 fmt.Println("error: ", err)
584 }
585}
586func main() {
587 fmt.Println("done")
588}
589`