blob: 975defdb604a29410f9294ec03d51f3a9dc0e8d2 [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"
Alex Brainmanafe0e972012-05-30 15:10:54 +10009 "io/ioutil"
10 "os"
11 "os/exec"
12 "path/filepath"
Dmitry Vyukov59495e82015-02-07 15:31:18 +030013 "regexp"
Russ Cox0c2a7272014-05-20 12:10:19 -040014 "runtime"
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040015 "strings"
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -080016 "sync"
Alex Brainmanafe0e972012-05-30 15:10:54 +100017 "testing"
18 "text/template"
19)
20
Albert Strasheim4235fa82013-04-07 11:37:37 -070021func testEnv(cmd *exec.Cmd) *exec.Cmd {
22 if cmd.Env != nil {
23 panic("environment already set")
24 }
25 for _, env := range os.Environ() {
Dmitry Vyukov59495e82015-02-07 15:31:18 +030026 // Exclude GODEBUG from the environment to prevent its output
27 // from breaking tests that are trying to parse other command output.
Dmitriy Vyukov4b536a52013-06-28 18:37:06 +040028 if strings.HasPrefix(env, "GODEBUG=") {
Albert Strasheim4235fa82013-04-07 11:37:37 -070029 continue
30 }
Dmitry Vyukov59495e82015-02-07 15:31:18 +030031 // Exclude GOTRACEBACK for the same reason.
32 if strings.HasPrefix(env, "GOTRACEBACK=") {
33 continue
34 }
Albert Strasheim4235fa82013-04-07 11:37:37 -070035 cmd.Env = append(cmd.Env, env)
36 }
37 return cmd
38}
39
Russ Coxc4efaac2014-10-28 21:53:09 -040040func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string {
David Crawshaw1648df62014-07-08 14:47:52 -040041 switch runtime.GOOS {
42 case "android", "nacl":
43 t.Skipf("skipping on %s", runtime.GOOS)
David Crawshaw95bf77b2015-02-26 18:05:47 -050044 case "darwin":
David Crawshawd6d423b2015-04-11 19:00:53 -040045 switch runtime.GOARCH {
46 case "arm", "arm64":
47 t.Skipf("skipping on %s/%s, no fork", runtime.GOOS, runtime.GOARCH)
David Crawshaw95bf77b2015-02-26 18:05:47 -050048 }
Russ Cox0c2a7272014-05-20 12:10:19 -040049 }
50
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040051 checkStaleRuntime(t)
Alex Brainmanafe0e972012-05-30 15:10:54 +100052
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040053 st := template.Must(template.New("crashSource").Parse(templ))
Alex Brainmanafe0e972012-05-30 15:10:54 +100054
55 dir, err := ioutil.TempDir("", "go-build")
56 if err != nil {
57 t.Fatalf("failed to create temp directory: %v", err)
58 }
59 defer os.RemoveAll(dir)
60
61 src := filepath.Join(dir, "main.go")
62 f, err := os.Create(src)
63 if err != nil {
Dmitriy Vyukov2791ef02013-08-12 22:04:10 +040064 t.Fatalf("failed to create file: %v", err)
Alex Brainmanafe0e972012-05-30 15:10:54 +100065 }
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040066 err = st.Execute(f, data)
Alex Brainmanafe0e972012-05-30 15:10:54 +100067 if err != nil {
68 f.Close()
69 t.Fatalf("failed to execute template: %v", err)
70 }
Dmitriy Vyukov2791ef02013-08-12 22:04:10 +040071 if err := f.Close(); err != nil {
72 t.Fatalf("failed to close file: %v", err)
73 }
Alex Brainmanafe0e972012-05-30 15:10:54 +100074
Russ Coxc4efaac2014-10-28 21:53:09 -040075 for i := 0; i < len(extra); i += 2 {
Alex Brainman9b691962015-03-16 15:46:22 +110076 fname := extra[i]
77 contents := extra[i+1]
78 if d, _ := filepath.Split(fname); d != "" {
79 if err := os.Mkdir(filepath.Join(dir, d), 0755); err != nil {
80 t.Fatal(err)
81 }
82 }
83 if err := ioutil.WriteFile(filepath.Join(dir, fname), []byte(contents), 0666); err != nil {
Russ Coxc4efaac2014-10-28 21:53:09 -040084 t.Fatal(err)
85 }
86 }
87
88 cmd := exec.Command("go", "build", "-o", "a.exe")
89 cmd.Dir = dir
90 out, err := testEnv(cmd).CombinedOutput()
91 if err != nil {
92 t.Fatalf("building source: %v\n%s", err, out)
93 }
94
95 got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput()
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040096 return string(got)
97}
98
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -080099var (
100 staleRuntimeOnce sync.Once // guards init of staleRuntimeErr
101 staleRuntimeErr error
102)
103
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400104func checkStaleRuntime(t *testing.T) {
Brad Fitzpatrickb70ddc02015-01-05 13:14:08 -0800105 staleRuntimeOnce.Do(func() {
106 // 'go run' uses the installed copy of runtime.a, which may be out of date.
107 out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
108 if err != nil {
109 staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out))
110 return
111 }
112 if string(out) != "false\n" {
113 staleRuntimeErr = fmt.Errorf("Stale runtime.a. Run 'go install runtime'.")
114 }
115 })
116 if staleRuntimeErr != nil {
117 t.Fatal(staleRuntimeErr)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400118 }
119}
120
121func testCrashHandler(t *testing.T, cgo bool) {
122 type crashTest struct {
123 Cgo bool
124 }
Russ Cox757e0de2013-08-15 22:34:06 -0400125 output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
Alex Brainmanafe0e972012-05-30 15:10:54 +1000126 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 -0400127 if output != want {
128 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
Alex Brainmanafe0e972012-05-30 15:10:54 +1000129 }
130}
131
132func TestCrashHandler(t *testing.T) {
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400133 testCrashHandler(t, false)
134}
135
136func testDeadlock(t *testing.T, source string) {
Russ Cox757e0de2013-08-15 22:34:06 -0400137 output := executeTest(t, source, nil)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400138 want := "fatal error: all goroutines are asleep - deadlock!\n"
Russ Cox757e0de2013-08-15 22:34:06 -0400139 if !strings.HasPrefix(output, want) {
140 t.Fatalf("output does not start with %q:\n%s", want, output)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400141 }
142}
143
144func TestSimpleDeadlock(t *testing.T) {
145 testDeadlock(t, simpleDeadlockSource)
146}
147
148func TestInitDeadlock(t *testing.T) {
149 testDeadlock(t, initDeadlockSource)
150}
151
152func TestLockedDeadlock(t *testing.T) {
153 testDeadlock(t, lockedDeadlockSource)
154}
155
156func TestLockedDeadlock2(t *testing.T) {
157 testDeadlock(t, lockedDeadlockSource2)
Alex Brainmanafe0e972012-05-30 15:10:54 +1000158}
159
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200160func TestGoexitDeadlock(t *testing.T) {
Russ Cox757e0de2013-08-15 22:34:06 -0400161 output := executeTest(t, goexitDeadlockSource, nil)
Russ Coxade6bc62014-04-16 13:12:18 -0400162 want := "no goroutines (main called runtime.Goexit) - deadlock!"
163 if !strings.Contains(output, want) {
164 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
Russ Cox757e0de2013-08-15 22:34:06 -0400165 }
166}
167
168func TestStackOverflow(t *testing.T) {
169 output := executeTest(t, stackOverflowSource, nil)
170 want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
171 if !strings.HasPrefix(output, want) {
172 t.Fatalf("output does not start with %q:\n%s", want, output)
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200173 }
174}
175
Russ Cox665feee2013-08-16 22:25:26 -0400176func TestThreadExhaustion(t *testing.T) {
177 output := executeTest(t, threadExhaustionSource, nil)
178 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
179 if !strings.HasPrefix(output, want) {
180 t.Fatalf("output does not start with %q:\n%s", want, output)
181 }
182}
183
Dmitriy Vyukovf946a7c2014-03-07 20:50:30 +0400184func TestRecursivePanic(t *testing.T) {
185 output := executeTest(t, recursivePanicSource, nil)
186 want := `wrap: bad
187panic: again
188
189`
190 if !strings.HasPrefix(output, want) {
191 t.Fatalf("output does not start with %q:\n%s", want, output)
192 }
193
194}
195
Russ Coxade6bc62014-04-16 13:12:18 -0400196func TestGoexitCrash(t *testing.T) {
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400197 output := executeTest(t, goexitExitSource, nil)
Russ Coxade6bc62014-04-16 13:12:18 -0400198 want := "no goroutines (main called runtime.Goexit) - deadlock!"
199 if !strings.Contains(output, want) {
200 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400201 }
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400202}
203
Keith Randall7e623162014-09-15 15:09:17 -0700204func TestGoexitDefer(t *testing.T) {
205 c := make(chan struct{})
206 go func() {
207 defer func() {
208 r := recover()
209 if r != nil {
210 t.Errorf("non-nil recover during Goexit")
211 }
212 c <- struct{}{}
213 }()
214 runtime.Goexit()
215 }()
216 // Note: if the defer fails to run, we will get a deadlock here
217 <-c
218}
219
Dmitriy Vyukovb5caa022014-05-28 00:00:01 -0400220func TestGoNil(t *testing.T) {
221 output := executeTest(t, goNilSource, nil)
222 want := "go of nil func value"
223 if !strings.Contains(output, want) {
224 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
225 }
226}
227
Dmitriy Vyukovaa763772014-07-16 12:19:33 +0400228func TestMainGoroutineId(t *testing.T) {
229 output := executeTest(t, mainGoroutineIdSource, nil)
230 want := "panic: test\n\ngoroutine 1 [running]:\n"
231 if !strings.HasPrefix(output, want) {
232 t.Fatalf("output does not start with %q:\n%s", want, output)
233 }
234}
235
Dmitry Vyukov59495e82015-02-07 15:31:18 +0300236func TestNoHelperGoroutines(t *testing.T) {
237 output := executeTest(t, noHelperGoroutinesSource, nil)
238 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
239 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
240 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
241 }
242}
243
Russ Cox1d550b82014-09-11 12:08:30 -0400244func TestBreakpoint(t *testing.T) {
245 output := executeTest(t, breakpointSource, nil)
246 want := "runtime.Breakpoint()"
247 if !strings.Contains(output, want) {
248 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
249 }
250}
251
Alex Brainmanafe0e972012-05-30 15:10:54 +1000252const crashSource = `
253package main
254
255import (
256 "fmt"
257 "runtime"
258)
259
260{{if .Cgo}}
261import "C"
262{{end}}
263
264func test(name string) {
265 defer func() {
266 if x := recover(); x != nil {
267 fmt.Printf(" recovered")
268 }
269 fmt.Printf(" done\n")
270 }()
271 fmt.Printf("%s:", name)
272 var s *string
273 _ = *s
274 fmt.Print("SHOULD NOT BE HERE")
275}
276
277func testInNewThread(name string) {
278 c := make(chan bool)
279 go func() {
280 runtime.LockOSThread()
281 test(name)
282 c <- true
283 }()
284 <-c
285}
286
287func main() {
288 runtime.LockOSThread()
289 test("main")
290 testInNewThread("new-thread")
291 testInNewThread("second-new-thread")
292 test("main-again")
293}
294`
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400295
296const simpleDeadlockSource = `
297package main
298func main() {
299 select {}
300}
301`
302
303const initDeadlockSource = `
304package main
305func init() {
306 select {}
307}
308func main() {
309}
310`
311
312const lockedDeadlockSource = `
313package main
314import "runtime"
315func main() {
316 runtime.LockOSThread()
317 select {}
318}
319`
320
321const lockedDeadlockSource2 = `
322package main
323import (
324 "runtime"
325 "time"
326)
327func main() {
328 go func() {
329 runtime.LockOSThread()
330 select {}
331 }()
332 time.Sleep(time.Millisecond)
333 select {}
334}
335`
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200336
337const goexitDeadlockSource = `
338package main
339import (
340 "runtime"
341)
342
343func F() {
344 for i := 0; i < 10; i++ {
345 }
346}
347
348func main() {
349 go F()
350 go F()
351 runtime.Goexit()
352}
353`
Russ Cox757e0de2013-08-15 22:34:06 -0400354
355const stackOverflowSource = `
356package main
357
358import "runtime/debug"
359
360func main() {
361 debug.SetMaxStack(4<<20)
362 f(make([]byte, 10))
363}
364
365func f(x []byte) byte {
366 var buf [64<<10]byte
367 return x[0] + f(buf[:])
368}
369`
Russ Cox665feee2013-08-16 22:25:26 -0400370
371const threadExhaustionSource = `
372package main
373
374import (
375 "runtime"
376 "runtime/debug"
377)
378
379func main() {
380 debug.SetMaxThreads(10)
381 c := make(chan int)
382 for i := 0; i < 100; i++ {
383 go func() {
384 runtime.LockOSThread()
385 c <- 0
386 select{}
387 }()
388 <-c
389 }
390}
391`
Dmitriy Vyukovf946a7c2014-03-07 20:50:30 +0400392
393const recursivePanicSource = `
394package main
395
396import (
397 "fmt"
398)
399
400func main() {
401 func() {
402 defer func() {
403 fmt.Println(recover())
404 }()
405 var x [8192]byte
406 func(x [8192]byte) {
407 defer func() {
408 if err := recover(); err != nil {
409 panic("wrap: " + err.(string))
410 }
411 }()
412 panic("bad")
413 }(x)
414 }()
415 panic("again")
416}
417`
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400418
419const goexitExitSource = `
420package main
421
422import (
423 "runtime"
424 "time"
425)
426
427func main() {
428 go func() {
429 time.Sleep(time.Millisecond)
430 }()
431 i := 0
432 runtime.SetFinalizer(&i, func(p *int) {})
433 runtime.GC()
434 runtime.Goexit()
435}
436`
Dmitriy Vyukovb5caa022014-05-28 00:00:01 -0400437
438const goNilSource = `
439package main
440
441func main() {
442 defer func() {
443 recover()
444 }()
445 var f func()
446 go f()
447 select{}
448}
449`
Dmitriy Vyukovaa763772014-07-16 12:19:33 +0400450
451const mainGoroutineIdSource = `
452package main
453func main() {
454 panic("test")
455}
456`
Russ Cox1d550b82014-09-11 12:08:30 -0400457
Dmitry Vyukov59495e82015-02-07 15:31:18 +0300458const noHelperGoroutinesSource = `
459package main
460import (
461 "runtime"
462 "time"
463)
464func init() {
465 i := 0
466 runtime.SetFinalizer(&i, func(p *int) {})
467 time.AfterFunc(time.Hour, func() {})
468 panic("oops")
469}
470func main() {
471}
472`
473
Russ Cox1d550b82014-09-11 12:08:30 -0400474const breakpointSource = `
475package main
476import "runtime"
477func main() {
478 runtime.Breakpoint()
479}
480`
Keith Randall03064782014-09-19 16:33:14 -0700481
482func TestGoexitInPanic(t *testing.T) {
483 // see issue 8774: this code used to trigger an infinite recursion
484 output := executeTest(t, goexitInPanicSource, nil)
485 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
486 if !strings.HasPrefix(output, want) {
487 t.Fatalf("output does not start with %q:\n%s", want, output)
488 }
489}
490
491const goexitInPanicSource = `
492package main
493import "runtime"
494func main() {
495 go func() {
496 defer func() {
497 runtime.Goexit()
498 }()
499 panic("hello")
500 }()
501 runtime.Goexit()
502}
503`
504
505func TestPanicAfterGoexit(t *testing.T) {
506 // an uncaught panic should still work after goexit
507 output := executeTest(t, panicAfterGoexitSource, nil)
508 want := "panic: hello"
509 if !strings.HasPrefix(output, want) {
510 t.Fatalf("output does not start with %q:\n%s", want, output)
511 }
512}
513
514const panicAfterGoexitSource = `
515package main
516import "runtime"
517func main() {
518 defer func() {
519 panic("hello")
520 }()
521 runtime.Goexit()
522}
523`
524
525func TestRecoveredPanicAfterGoexit(t *testing.T) {
526 output := executeTest(t, recoveredPanicAfterGoexitSource, nil)
527 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
528 if !strings.HasPrefix(output, want) {
529 t.Fatalf("output does not start with %q:\n%s", want, output)
530 }
531}
532
533const recoveredPanicAfterGoexitSource = `
534package main
535import "runtime"
536func main() {
537 defer func() {
538 defer func() {
539 r := recover()
540 if r == nil {
541 panic("bad recover")
542 }
543 }()
544 panic("hello")
545 }()
546 runtime.Goexit()
547}
548`
549
550func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
551 // 1. defer a function that recovers
552 // 2. defer a function that panics
553 // 3. call goexit
554 // Goexit should run the #2 defer. Its panic
555 // should be caught by the #1 defer, and execution
556 // should resume in the caller. Like the Goexit
557 // never happened!
558 defer func() {
559 r := recover()
560 if r == nil {
561 panic("bad recover")
562 }
563 }()
564 defer func() {
565 panic("hello")
566 }()
567 runtime.Goexit()
568}
Dmitry Vyukov776aeca2015-01-13 20:12:50 +0300569
570func TestNetpollDeadlock(t *testing.T) {
571 output := executeTest(t, netpollDeadlockSource, nil)
572 want := "done\n"
573 if !strings.HasSuffix(output, want) {
574 t.Fatalf("output does not start with %q:\n%s", want, output)
575 }
576}
577
578const netpollDeadlockSource = `
579package main
580import (
581 "fmt"
582 "net"
583)
584func init() {
585 fmt.Println("dialing")
586 c, err := net.Dial("tcp", "localhost:14356")
587 if err == nil {
588 c.Close()
589 } else {
590 fmt.Println("error: ", err)
591 }
592}
593func main() {
594 fmt.Println("done")
595}
596`