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 "internal/testenv" 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "regexp" 15 "runtime" 16 "strings" 17 "sync" 18 "testing" 19 "text/template" 20 ) 21 22 func testEnv(cmd *exec.Cmd) *exec.Cmd { 23 if cmd.Env != nil { 24 panic("environment already set") 25 } 26 for _, env := range os.Environ() { 27 // Exclude GODEBUG from the environment to prevent its output 28 // from breaking tests that are trying to parse other command output. 29 if strings.HasPrefix(env, "GODEBUG=") { 30 continue 31 } 32 // Exclude GOTRACEBACK for the same reason. 33 if strings.HasPrefix(env, "GOTRACEBACK=") { 34 continue 35 } 36 cmd.Env = append(cmd.Env, env) 37 } 38 return cmd 39 } 40 41 func executeTest(t *testing.T, templ string, data interface{}, extra ...string) string { 42 testenv.MustHaveGoBuild(t) 43 44 checkStaleRuntime(t) 45 46 st := template.Must(template.New("crashSource").Parse(templ)) 47 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 { 57 t.Fatalf("failed to create file: %v", err) 58 } 59 err = st.Execute(f, data) 60 if err != nil { 61 f.Close() 62 t.Fatalf("failed to execute template: %v", err) 63 } 64 if err := f.Close(); err != nil { 65 t.Fatalf("failed to close file: %v", err) 66 } 67 68 for i := 0; i < len(extra); i += 2 { 69 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 { 77 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() 89 return string(got) 90 } 91 92 var ( 93 staleRuntimeOnce sync.Once // guards init of staleRuntimeErr 94 staleRuntimeErr error 95 ) 96 97 func checkStaleRuntime(t *testing.T) { 98 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) 111 } 112 } 113 114 func testCrashHandler(t *testing.T, cgo bool) { 115 type crashTest struct { 116 Cgo bool 117 } 118 output := executeTest(t, crashSource, &crashTest{Cgo: cgo}) 119 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" 120 if output != want { 121 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) 122 } 123 } 124 125 func TestCrashHandler(t *testing.T) { 126 testCrashHandler(t, false) 127 } 128 129 func testDeadlock(t *testing.T, source string) { 130 output := executeTest(t, source, nil) 131 want := "fatal error: all goroutines are asleep - deadlock!\n" 132 if !strings.HasPrefix(output, want) { 133 t.Fatalf("output does not start with %q:\n%s", want, output) 134 } 135 } 136 137 func TestSimpleDeadlock(t *testing.T) { 138 testDeadlock(t, simpleDeadlockSource) 139 } 140 141 func TestInitDeadlock(t *testing.T) { 142 testDeadlock(t, initDeadlockSource) 143 } 144 145 func TestLockedDeadlock(t *testing.T) { 146 testDeadlock(t, lockedDeadlockSource) 147 } 148 149 func TestLockedDeadlock2(t *testing.T) { 150 testDeadlock(t, lockedDeadlockSource2) 151 } 152 153 func TestGoexitDeadlock(t *testing.T) { 154 output := executeTest(t, goexitDeadlockSource, nil) 155 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) 158 } 159 } 160 161 func 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) 166 } 167 } 168 169 func 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 177 func TestRecursivePanic(t *testing.T) { 178 output := executeTest(t, recursivePanicSource, nil) 179 want := `wrap: bad 180 panic: 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 189 func TestGoexitCrash(t *testing.T) { 190 output := executeTest(t, goexitExitSource, nil) 191 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) 194 } 195 } 196 197 func 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 213 func 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 221 func 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 229 func 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 237 func 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 245 const crashSource = ` 246 package main 247 248 import ( 249 "fmt" 250 "runtime" 251 ) 252 253 {{if .Cgo}} 254 import "C" 255 {{end}} 256 257 func 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 270 func 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 280 func main() { 281 runtime.LockOSThread() 282 test("main") 283 testInNewThread("new-thread") 284 testInNewThread("second-new-thread") 285 test("main-again") 286 } 287 ` 288 289 const simpleDeadlockSource = ` 290 package main 291 func main() { 292 select {} 293 } 294 ` 295 296 const initDeadlockSource = ` 297 package main 298 func init() { 299 select {} 300 } 301 func main() { 302 } 303 ` 304 305 const lockedDeadlockSource = ` 306 package main 307 import "runtime" 308 func main() { 309 runtime.LockOSThread() 310 select {} 311 } 312 ` 313 314 const lockedDeadlockSource2 = ` 315 package main 316 import ( 317 "runtime" 318 "time" 319 ) 320 func main() { 321 go func() { 322 runtime.LockOSThread() 323 select {} 324 }() 325 time.Sleep(time.Millisecond) 326 select {} 327 } 328 ` 329 330 const goexitDeadlockSource = ` 331 package main 332 import ( 333 "runtime" 334 ) 335 336 func F() { 337 for i := 0; i < 10; i++ { 338 } 339 } 340 341 func main() { 342 go F() 343 go F() 344 runtime.Goexit() 345 } 346 ` 347 348 const stackOverflowSource = ` 349 package main 350 351 import "runtime/debug" 352 353 func main() { 354 debug.SetMaxStack(4<<20) 355 f(make([]byte, 10)) 356 } 357 358 func f(x []byte) byte { 359 var buf [64<<10]byte 360 return x[0] + f(buf[:]) 361 } 362 ` 363 364 const threadExhaustionSource = ` 365 package main 366 367 import ( 368 "runtime" 369 "runtime/debug" 370 ) 371 372 func 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 ` 385 386 const recursivePanicSource = ` 387 package main 388 389 import ( 390 "fmt" 391 ) 392 393 func 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 ` 411 412 const goexitExitSource = ` 413 package main 414 415 import ( 416 "runtime" 417 "time" 418 ) 419 420 func 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 ` 430 431 const goNilSource = ` 432 package main 433 434 func main() { 435 defer func() { 436 recover() 437 }() 438 var f func() 439 go f() 440 select{} 441 } 442 ` 443 444 const mainGoroutineIdSource = ` 445 package main 446 func main() { 447 panic("test") 448 } 449 ` 450 451 const noHelperGoroutinesSource = ` 452 package main 453 import ( 454 "runtime" 455 "time" 456 ) 457 func init() { 458 i := 0 459 runtime.SetFinalizer(&i, func(p *int) {}) 460 time.AfterFunc(time.Hour, func() {}) 461 panic("oops") 462 } 463 func main() { 464 } 465 ` 466 467 const breakpointSource = ` 468 package main 469 import "runtime" 470 func main() { 471 runtime.Breakpoint() 472 } 473 ` 474 475 func 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 484 const goexitInPanicSource = ` 485 package main 486 import "runtime" 487 func main() { 488 go func() { 489 defer func() { 490 runtime.Goexit() 491 }() 492 panic("hello") 493 }() 494 runtime.Goexit() 495 } 496 ` 497 498 func 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 507 const panicAfterGoexitSource = ` 508 package main 509 import "runtime" 510 func main() { 511 defer func() { 512 panic("hello") 513 }() 514 runtime.Goexit() 515 } 516 ` 517 518 func 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 526 const recoveredPanicAfterGoexitSource = ` 527 package main 528 import "runtime" 529 func 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 543 func 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 } 562 563 func 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 571 const netpollDeadlockSource = ` 572 package main 573 import ( 574 "fmt" 575 "net" 576 ) 577 func 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 } 586 func main() { 587 fmt.Println("done") 588 } 589 ` 590