github.com/reiver/go@v0.0.0-20150109200633-1d0c7792f172/src/runtime/crash_test.go (about) 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 "fmt" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "sync" 16 "testing" 17 "text/template" 18 ) 19 20 // testEnv excludes GODEBUG from the environment 21 // to prevent its output from breaking tests that 22 // are trying to parse other command output. 23 func testEnv(cmd *exec.Cmd) *exec.Cmd { 24 if cmd.Env != nil { 25 panic("environment already set") 26 } 27 for _, env := range os.Environ() { 28 if strings.HasPrefix(env, "GODEBUG=") { 29 continue 30 } 31 cmd.Env = append(cmd.Env, env) 32 } 33 return cmd 34 } 35 36 func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string { 37 switch runtime.GOOS { 38 case "android", "nacl": 39 t.Skipf("skipping on %s", runtime.GOOS) 40 } 41 42 checkStaleRuntime(t) 43 44 st := template.Must(template.New("crashSource").Parse(templ)) 45 46 dir, err := ioutil.TempDir("", "go-build") 47 if err != nil { 48 t.Fatalf("failed to create temp directory: %v", err) 49 } 50 defer os.RemoveAll(dir) 51 52 src := filepath.Join(dir, "main.go") 53 f, err := os.Create(src) 54 if err != nil { 55 t.Fatalf("failed to create file: %v", err) 56 } 57 err = st.Execute(f, data) 58 if err != nil { 59 f.Close() 60 t.Fatalf("failed to execute template: %v", err) 61 } 62 if err := f.Close(); err != nil { 63 t.Fatalf("failed to close file: %v", err) 64 } 65 66 for i := 0; i < len(extra); i += 2 { 67 if err := ioutil.WriteFile(filepath.Join(dir, extra[i]), []byte(extra[i+1]), 0666); err != nil { 68 t.Fatal(err) 69 } 70 } 71 72 cmd := exec.Command("go", "build", "-o", "a.exe") 73 cmd.Dir = dir 74 out, err := testEnv(cmd).CombinedOutput() 75 if err != nil { 76 t.Fatalf("building source: %v\n%s", err, out) 77 } 78 79 got, _ := testEnv(exec.Command(filepath.Join(dir, "a.exe"))).CombinedOutput() 80 return string(got) 81 } 82 83 var ( 84 staleRuntimeOnce sync.Once // guards init of staleRuntimeErr 85 staleRuntimeErr error 86 ) 87 88 func checkStaleRuntime(t *testing.T) { 89 staleRuntimeOnce.Do(func() { 90 // 'go run' uses the installed copy of runtime.a, which may be out of date. 91 out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput() 92 if err != nil { 93 staleRuntimeErr = fmt.Errorf("failed to execute 'go list': %v\n%v", err, string(out)) 94 return 95 } 96 if string(out) != "false\n" { 97 staleRuntimeErr = fmt.Errorf("Stale runtime.a. Run 'go install runtime'.") 98 } 99 }) 100 if staleRuntimeErr != nil { 101 t.Fatal(staleRuntimeErr) 102 } 103 } 104 105 func testCrashHandler(t *testing.T, cgo bool) { 106 type crashTest struct { 107 Cgo bool 108 } 109 output := executeTest(t, crashSource, &crashTest{Cgo: cgo}) 110 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" 111 if output != want { 112 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) 113 } 114 } 115 116 func TestCrashHandler(t *testing.T) { 117 testCrashHandler(t, false) 118 } 119 120 func testDeadlock(t *testing.T, source string) { 121 output := executeTest(t, source, nil) 122 want := "fatal error: all goroutines are asleep - deadlock!\n" 123 if !strings.HasPrefix(output, want) { 124 t.Fatalf("output does not start with %q:\n%s", want, output) 125 } 126 } 127 128 func TestSimpleDeadlock(t *testing.T) { 129 testDeadlock(t, simpleDeadlockSource) 130 } 131 132 func TestInitDeadlock(t *testing.T) { 133 testDeadlock(t, initDeadlockSource) 134 } 135 136 func TestLockedDeadlock(t *testing.T) { 137 testDeadlock(t, lockedDeadlockSource) 138 } 139 140 func TestLockedDeadlock2(t *testing.T) { 141 testDeadlock(t, lockedDeadlockSource2) 142 } 143 144 func TestGoexitDeadlock(t *testing.T) { 145 output := executeTest(t, goexitDeadlockSource, nil) 146 want := "no goroutines (main called runtime.Goexit) - deadlock!" 147 if !strings.Contains(output, want) { 148 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 149 } 150 } 151 152 func TestStackOverflow(t *testing.T) { 153 output := executeTest(t, stackOverflowSource, nil) 154 want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow" 155 if !strings.HasPrefix(output, want) { 156 t.Fatalf("output does not start with %q:\n%s", want, output) 157 } 158 } 159 160 func TestThreadExhaustion(t *testing.T) { 161 output := executeTest(t, threadExhaustionSource, nil) 162 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion" 163 if !strings.HasPrefix(output, want) { 164 t.Fatalf("output does not start with %q:\n%s", want, output) 165 } 166 } 167 168 func TestRecursivePanic(t *testing.T) { 169 output := executeTest(t, recursivePanicSource, nil) 170 want := `wrap: bad 171 panic: again 172 173 ` 174 if !strings.HasPrefix(output, want) { 175 t.Fatalf("output does not start with %q:\n%s", want, output) 176 } 177 178 } 179 180 func TestGoexitCrash(t *testing.T) { 181 output := executeTest(t, goexitExitSource, nil) 182 want := "no goroutines (main called runtime.Goexit) - deadlock!" 183 if !strings.Contains(output, want) { 184 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 185 } 186 } 187 188 func TestGoexitDefer(t *testing.T) { 189 c := make(chan struct{}) 190 go func() { 191 defer func() { 192 r := recover() 193 if r != nil { 194 t.Errorf("non-nil recover during Goexit") 195 } 196 c <- struct{}{} 197 }() 198 runtime.Goexit() 199 }() 200 // Note: if the defer fails to run, we will get a deadlock here 201 <-c 202 } 203 204 func TestGoNil(t *testing.T) { 205 output := executeTest(t, goNilSource, nil) 206 want := "go of nil func value" 207 if !strings.Contains(output, want) { 208 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 209 } 210 } 211 212 func TestMainGoroutineId(t *testing.T) { 213 output := executeTest(t, mainGoroutineIdSource, nil) 214 want := "panic: test\n\ngoroutine 1 [running]:\n" 215 if !strings.HasPrefix(output, want) { 216 t.Fatalf("output does not start with %q:\n%s", want, output) 217 } 218 } 219 220 func TestBreakpoint(t *testing.T) { 221 output := executeTest(t, breakpointSource, nil) 222 want := "runtime.Breakpoint()" 223 if !strings.Contains(output, want) { 224 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) 225 } 226 } 227 228 const crashSource = ` 229 package main 230 231 import ( 232 "fmt" 233 "runtime" 234 ) 235 236 {{if .Cgo}} 237 import "C" 238 {{end}} 239 240 func test(name string) { 241 defer func() { 242 if x := recover(); x != nil { 243 fmt.Printf(" recovered") 244 } 245 fmt.Printf(" done\n") 246 }() 247 fmt.Printf("%s:", name) 248 var s *string 249 _ = *s 250 fmt.Print("SHOULD NOT BE HERE") 251 } 252 253 func testInNewThread(name string) { 254 c := make(chan bool) 255 go func() { 256 runtime.LockOSThread() 257 test(name) 258 c <- true 259 }() 260 <-c 261 } 262 263 func main() { 264 runtime.LockOSThread() 265 test("main") 266 testInNewThread("new-thread") 267 testInNewThread("second-new-thread") 268 test("main-again") 269 } 270 ` 271 272 const simpleDeadlockSource = ` 273 package main 274 func main() { 275 select {} 276 } 277 ` 278 279 const initDeadlockSource = ` 280 package main 281 func init() { 282 select {} 283 } 284 func main() { 285 } 286 ` 287 288 const lockedDeadlockSource = ` 289 package main 290 import "runtime" 291 func main() { 292 runtime.LockOSThread() 293 select {} 294 } 295 ` 296 297 const lockedDeadlockSource2 = ` 298 package main 299 import ( 300 "runtime" 301 "time" 302 ) 303 func main() { 304 go func() { 305 runtime.LockOSThread() 306 select {} 307 }() 308 time.Sleep(time.Millisecond) 309 select {} 310 } 311 ` 312 313 const goexitDeadlockSource = ` 314 package main 315 import ( 316 "runtime" 317 ) 318 319 func F() { 320 for i := 0; i < 10; i++ { 321 } 322 } 323 324 func main() { 325 go F() 326 go F() 327 runtime.Goexit() 328 } 329 ` 330 331 const stackOverflowSource = ` 332 package main 333 334 import "runtime/debug" 335 336 func main() { 337 debug.SetMaxStack(4<<20) 338 f(make([]byte, 10)) 339 } 340 341 func f(x []byte) byte { 342 var buf [64<<10]byte 343 return x[0] + f(buf[:]) 344 } 345 ` 346 347 const threadExhaustionSource = ` 348 package main 349 350 import ( 351 "runtime" 352 "runtime/debug" 353 ) 354 355 func main() { 356 debug.SetMaxThreads(10) 357 c := make(chan int) 358 for i := 0; i < 100; i++ { 359 go func() { 360 runtime.LockOSThread() 361 c <- 0 362 select{} 363 }() 364 <-c 365 } 366 } 367 ` 368 369 const recursivePanicSource = ` 370 package main 371 372 import ( 373 "fmt" 374 ) 375 376 func main() { 377 func() { 378 defer func() { 379 fmt.Println(recover()) 380 }() 381 var x [8192]byte 382 func(x [8192]byte) { 383 defer func() { 384 if err := recover(); err != nil { 385 panic("wrap: " + err.(string)) 386 } 387 }() 388 panic("bad") 389 }(x) 390 }() 391 panic("again") 392 } 393 ` 394 395 const goexitExitSource = ` 396 package main 397 398 import ( 399 "runtime" 400 "time" 401 ) 402 403 func main() { 404 go func() { 405 time.Sleep(time.Millisecond) 406 }() 407 i := 0 408 runtime.SetFinalizer(&i, func(p *int) {}) 409 runtime.GC() 410 runtime.Goexit() 411 } 412 ` 413 414 const goNilSource = ` 415 package main 416 417 func main() { 418 defer func() { 419 recover() 420 }() 421 var f func() 422 go f() 423 select{} 424 } 425 ` 426 427 const mainGoroutineIdSource = ` 428 package main 429 func main() { 430 panic("test") 431 } 432 ` 433 434 const breakpointSource = ` 435 package main 436 import "runtime" 437 func main() { 438 runtime.Breakpoint() 439 } 440 ` 441 442 func TestGoexitInPanic(t *testing.T) { 443 // see issue 8774: this code used to trigger an infinite recursion 444 output := executeTest(t, goexitInPanicSource, nil) 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 const goexitInPanicSource = ` 452 package main 453 import "runtime" 454 func main() { 455 go func() { 456 defer func() { 457 runtime.Goexit() 458 }() 459 panic("hello") 460 }() 461 runtime.Goexit() 462 } 463 ` 464 465 func TestPanicAfterGoexit(t *testing.T) { 466 // an uncaught panic should still work after goexit 467 output := executeTest(t, panicAfterGoexitSource, nil) 468 want := "panic: hello" 469 if !strings.HasPrefix(output, want) { 470 t.Fatalf("output does not start with %q:\n%s", want, output) 471 } 472 } 473 474 const panicAfterGoexitSource = ` 475 package main 476 import "runtime" 477 func main() { 478 defer func() { 479 panic("hello") 480 }() 481 runtime.Goexit() 482 } 483 ` 484 485 func TestRecoveredPanicAfterGoexit(t *testing.T) { 486 output := executeTest(t, recoveredPanicAfterGoexitSource, nil) 487 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" 488 if !strings.HasPrefix(output, want) { 489 t.Fatalf("output does not start with %q:\n%s", want, output) 490 } 491 } 492 493 const recoveredPanicAfterGoexitSource = ` 494 package main 495 import "runtime" 496 func main() { 497 defer func() { 498 defer func() { 499 r := recover() 500 if r == nil { 501 panic("bad recover") 502 } 503 }() 504 panic("hello") 505 }() 506 runtime.Goexit() 507 } 508 ` 509 510 func TestRecoverBeforePanicAfterGoexit(t *testing.T) { 511 // 1. defer a function that recovers 512 // 2. defer a function that panics 513 // 3. call goexit 514 // Goexit should run the #2 defer. Its panic 515 // should be caught by the #1 defer, and execution 516 // should resume in the caller. Like the Goexit 517 // never happened! 518 defer func() { 519 r := recover() 520 if r == nil { 521 panic("bad recover") 522 } 523 }() 524 defer func() { 525 panic("hello") 526 }() 527 runtime.Goexit() 528 }