blob: a86a3b7904e4e7439d99cff9cc12bb30ee8c9613 [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 (
8 "io/ioutil"
9 "os"
10 "os/exec"
11 "path/filepath"
Russ Cox0c2a7272014-05-20 12:10:19 -040012 "runtime"
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040013 "strings"
Alex Brainmanafe0e972012-05-30 15:10:54 +100014 "testing"
15 "text/template"
16)
17
Dmitriy Vyukov4b536a52013-06-28 18:37:06 +040018// testEnv excludes GODEBUG from the environment
Albert Strasheim4235fa82013-04-07 11:37:37 -070019// to prevent its output from breaking tests that
20// are trying to parse other command output.
21func testEnv(cmd *exec.Cmd) *exec.Cmd {
22 if cmd.Env != nil {
23 panic("environment already set")
24 }
25 for _, env := range os.Environ() {
Dmitriy Vyukov4b536a52013-06-28 18:37:06 +040026 if strings.HasPrefix(env, "GODEBUG=") {
Albert Strasheim4235fa82013-04-07 11:37:37 -070027 continue
28 }
29 cmd.Env = append(cmd.Env, env)
30 }
31 return cmd
32}
33
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040034func executeTest(t *testing.T, templ string, data interface{}) string {
David Crawshaw1648df62014-07-08 14:47:52 -040035 switch runtime.GOOS {
36 case "android", "nacl":
37 t.Skipf("skipping on %s", runtime.GOOS)
Russ Cox0c2a7272014-05-20 12:10:19 -040038 }
39
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040040 checkStaleRuntime(t)
Alex Brainmanafe0e972012-05-30 15:10:54 +100041
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040042 st := template.Must(template.New("crashSource").Parse(templ))
Alex Brainmanafe0e972012-05-30 15:10:54 +100043
44 dir, err := ioutil.TempDir("", "go-build")
45 if err != nil {
46 t.Fatalf("failed to create temp directory: %v", err)
47 }
48 defer os.RemoveAll(dir)
49
50 src := filepath.Join(dir, "main.go")
51 f, err := os.Create(src)
52 if err != nil {
Dmitriy Vyukov2791ef02013-08-12 22:04:10 +040053 t.Fatalf("failed to create file: %v", err)
Alex Brainmanafe0e972012-05-30 15:10:54 +100054 }
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040055 err = st.Execute(f, data)
Alex Brainmanafe0e972012-05-30 15:10:54 +100056 if err != nil {
57 f.Close()
58 t.Fatalf("failed to execute template: %v", err)
59 }
Dmitriy Vyukov2791ef02013-08-12 22:04:10 +040060 if err := f.Close(); err != nil {
61 t.Fatalf("failed to close file: %v", err)
62 }
Alex Brainmanafe0e972012-05-30 15:10:54 +100063
Albert Strasheim4235fa82013-04-07 11:37:37 -070064 got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput()
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040065 return string(got)
66}
67
68func checkStaleRuntime(t *testing.T) {
69 // 'go run' uses the installed copy of runtime.a, which may be out of date.
Albert Strasheim4235fa82013-04-07 11:37:37 -070070 out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput()
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040071 if err != nil {
72 t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out))
73 }
74 if string(out) != "false\n" {
75 t.Fatalf("Stale runtime.a. Run 'go install runtime'.")
76 }
77}
78
79func testCrashHandler(t *testing.T, cgo bool) {
80 type crashTest struct {
81 Cgo bool
82 }
Russ Cox757e0de2013-08-15 22:34:06 -040083 output := executeTest(t, crashSource, &crashTest{Cgo: cgo})
Alex Brainmanafe0e972012-05-30 15:10:54 +100084 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 -040085 if output != want {
86 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
Alex Brainmanafe0e972012-05-30 15:10:54 +100087 }
88}
89
90func TestCrashHandler(t *testing.T) {
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040091 testCrashHandler(t, false)
92}
93
94func testDeadlock(t *testing.T, source string) {
Russ Cox757e0de2013-08-15 22:34:06 -040095 output := executeTest(t, source, nil)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040096 want := "fatal error: all goroutines are asleep - deadlock!\n"
Russ Cox757e0de2013-08-15 22:34:06 -040097 if !strings.HasPrefix(output, want) {
98 t.Fatalf("output does not start with %q:\n%s", want, output)
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +040099 }
100}
101
102func TestSimpleDeadlock(t *testing.T) {
103 testDeadlock(t, simpleDeadlockSource)
104}
105
106func TestInitDeadlock(t *testing.T) {
107 testDeadlock(t, initDeadlockSource)
108}
109
110func TestLockedDeadlock(t *testing.T) {
111 testDeadlock(t, lockedDeadlockSource)
112}
113
114func TestLockedDeadlock2(t *testing.T) {
115 testDeadlock(t, lockedDeadlockSource2)
Alex Brainmanafe0e972012-05-30 15:10:54 +1000116}
117
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200118func TestGoexitDeadlock(t *testing.T) {
Russ Cox757e0de2013-08-15 22:34:06 -0400119 output := executeTest(t, goexitDeadlockSource, nil)
Russ Coxade6bc62014-04-16 13:12:18 -0400120 want := "no goroutines (main called runtime.Goexit) - deadlock!"
121 if !strings.Contains(output, want) {
122 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
Russ Cox757e0de2013-08-15 22:34:06 -0400123 }
124}
125
126func TestStackOverflow(t *testing.T) {
127 output := executeTest(t, stackOverflowSource, nil)
128 want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow"
129 if !strings.HasPrefix(output, want) {
130 t.Fatalf("output does not start with %q:\n%s", want, output)
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200131 }
132}
133
Russ Cox665feee2013-08-16 22:25:26 -0400134func TestThreadExhaustion(t *testing.T) {
135 output := executeTest(t, threadExhaustionSource, nil)
136 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
137 if !strings.HasPrefix(output, want) {
138 t.Fatalf("output does not start with %q:\n%s", want, output)
139 }
140}
141
Dmitriy Vyukovf946a7c2014-03-07 20:50:30 +0400142func TestRecursivePanic(t *testing.T) {
143 output := executeTest(t, recursivePanicSource, nil)
144 want := `wrap: bad
145panic: again
146
147`
148 if !strings.HasPrefix(output, want) {
149 t.Fatalf("output does not start with %q:\n%s", want, output)
150 }
151
152}
153
Russ Coxade6bc62014-04-16 13:12:18 -0400154func TestGoexitCrash(t *testing.T) {
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400155 output := executeTest(t, goexitExitSource, nil)
Russ Coxade6bc62014-04-16 13:12:18 -0400156 want := "no goroutines (main called runtime.Goexit) - deadlock!"
157 if !strings.Contains(output, want) {
158 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400159 }
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400160}
161
Dmitriy Vyukovb5caa022014-05-28 00:00:01 -0400162func TestGoNil(t *testing.T) {
163 output := executeTest(t, goNilSource, nil)
164 want := "go of nil func value"
165 if !strings.Contains(output, want) {
166 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
167 }
168}
169
Dmitriy Vyukovaa763772014-07-16 12:19:33 +0400170func TestMainGoroutineId(t *testing.T) {
171 output := executeTest(t, mainGoroutineIdSource, nil)
172 want := "panic: test\n\ngoroutine 1 [running]:\n"
173 if !strings.HasPrefix(output, want) {
174 t.Fatalf("output does not start with %q:\n%s", want, output)
175 }
176}
177
Russ Cox1d550b82014-09-11 12:08:30 -0400178func TestBreakpoint(t *testing.T) {
179 output := executeTest(t, breakpointSource, nil)
180 want := "runtime.Breakpoint()"
181 if !strings.Contains(output, want) {
182 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
183 }
184}
185
Alex Brainmanafe0e972012-05-30 15:10:54 +1000186const crashSource = `
187package main
188
189import (
190 "fmt"
191 "runtime"
192)
193
194{{if .Cgo}}
195import "C"
196{{end}}
197
198func test(name string) {
199 defer func() {
200 if x := recover(); x != nil {
201 fmt.Printf(" recovered")
202 }
203 fmt.Printf(" done\n")
204 }()
205 fmt.Printf("%s:", name)
206 var s *string
207 _ = *s
208 fmt.Print("SHOULD NOT BE HERE")
209}
210
211func testInNewThread(name string) {
212 c := make(chan bool)
213 go func() {
214 runtime.LockOSThread()
215 test(name)
216 c <- true
217 }()
218 <-c
219}
220
221func main() {
222 runtime.LockOSThread()
223 test("main")
224 testInNewThread("new-thread")
225 testInNewThread("second-new-thread")
226 test("main-again")
227}
228`
Dmitriy Vyukov06a488f2013-02-20 12:15:02 +0400229
230const simpleDeadlockSource = `
231package main
232func main() {
233 select {}
234}
235`
236
237const initDeadlockSource = `
238package main
239func init() {
240 select {}
241}
242func main() {
243}
244`
245
246const lockedDeadlockSource = `
247package main
248import "runtime"
249func main() {
250 runtime.LockOSThread()
251 select {}
252}
253`
254
255const lockedDeadlockSource2 = `
256package main
257import (
258 "runtime"
259 "time"
260)
261func main() {
262 go func() {
263 runtime.LockOSThread()
264 select {}
265 }()
266 time.Sleep(time.Millisecond)
267 select {}
268}
269`
Dmitriy Vyukov2fe840f2013-03-05 09:40:17 +0200270
271const goexitDeadlockSource = `
272package main
273import (
274 "runtime"
275)
276
277func F() {
278 for i := 0; i < 10; i++ {
279 }
280}
281
282func main() {
283 go F()
284 go F()
285 runtime.Goexit()
286}
287`
Russ Cox757e0de2013-08-15 22:34:06 -0400288
289const stackOverflowSource = `
290package main
291
292import "runtime/debug"
293
294func main() {
295 debug.SetMaxStack(4<<20)
296 f(make([]byte, 10))
297}
298
299func f(x []byte) byte {
300 var buf [64<<10]byte
301 return x[0] + f(buf[:])
302}
303`
Russ Cox665feee2013-08-16 22:25:26 -0400304
305const threadExhaustionSource = `
306package main
307
308import (
309 "runtime"
310 "runtime/debug"
311)
312
313func main() {
314 debug.SetMaxThreads(10)
315 c := make(chan int)
316 for i := 0; i < 100; i++ {
317 go func() {
318 runtime.LockOSThread()
319 c <- 0
320 select{}
321 }()
322 <-c
323 }
324}
325`
Dmitriy Vyukovf946a7c2014-03-07 20:50:30 +0400326
327const recursivePanicSource = `
328package main
329
330import (
331 "fmt"
332)
333
334func main() {
335 func() {
336 defer func() {
337 fmt.Println(recover())
338 }()
339 var x [8192]byte
340 func(x [8192]byte) {
341 defer func() {
342 if err := recover(); err != nil {
343 panic("wrap: " + err.(string))
344 }
345 }()
346 panic("bad")
347 }(x)
348 }()
349 panic("again")
350}
351`
Dmitriy Vyukov55e0f362014-04-15 19:48:17 +0400352
353const goexitExitSource = `
354package main
355
356import (
357 "runtime"
358 "time"
359)
360
361func main() {
362 go func() {
363 time.Sleep(time.Millisecond)
364 }()
365 i := 0
366 runtime.SetFinalizer(&i, func(p *int) {})
367 runtime.GC()
368 runtime.Goexit()
369}
370`
Dmitriy Vyukovb5caa022014-05-28 00:00:01 -0400371
372const goNilSource = `
373package main
374
375func main() {
376 defer func() {
377 recover()
378 }()
379 var f func()
380 go f()
381 select{}
382}
383`
Dmitriy Vyukovaa763772014-07-16 12:19:33 +0400384
385const mainGoroutineIdSource = `
386package main
387func main() {
388 panic("test")
389}
390`
Russ Cox1d550b82014-09-11 12:08:30 -0400391
392const breakpointSource = `
393package main
394import "runtime"
395func main() {
396 runtime.Breakpoint()
397}
398`