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// +build cgo 6 7package runtime_test 8 9import ( 10 "bytes" 11 "fmt" 12 "internal/testenv" 13 "os" 14 "os/exec" 15 "runtime" 16 "strconv" 17 "strings" 18 "testing" 19 "time" 20) 21 22func TestCgoCrashHandler(t *testing.T) { 23 t.Parallel() 24 testCrashHandler(t, true) 25} 26 27func TestCgoSignalDeadlock(t *testing.T) { 28 // Don't call t.Parallel, since too much work going on at the 29 // same time can cause the testprogcgo code to overrun its 30 // timeouts (issue #18598). 31 32 if testing.Short() && runtime.GOOS == "windows" { 33 t.Skip("Skipping in short mode") // takes up to 64 seconds 34 } 35 got := runTestProg(t, "testprogcgo", "CgoSignalDeadlock") 36 want := "OK\n" 37 if got != want { 38 t.Fatalf("expected %q, but got:\n%s", want, got) 39 } 40} 41 42func TestCgoTraceback(t *testing.T) { 43 t.Parallel() 44 got := runTestProg(t, "testprogcgo", "CgoTraceback") 45 want := "OK\n" 46 if got != want { 47 t.Fatalf("expected %q, but got:\n%s", want, got) 48 } 49} 50 51func TestCgoCallbackGC(t *testing.T) { 52 t.Parallel() 53 switch runtime.GOOS { 54 case "plan9", "windows": 55 t.Skipf("no pthreads on %s", runtime.GOOS) 56 } 57 if testing.Short() { 58 switch { 59 case runtime.GOOS == "dragonfly": 60 t.Skip("see golang.org/issue/11990") 61 case runtime.GOOS == "linux" && runtime.GOARCH == "arm": 62 t.Skip("too slow for arm builders") 63 case runtime.GOOS == "linux" && (runtime.GOARCH == "mips64" || runtime.GOARCH == "mips64le"): 64 t.Skip("too slow for mips64x builders") 65 } 66 } 67 got := runTestProg(t, "testprogcgo", "CgoCallbackGC") 68 want := "OK\n" 69 if got != want { 70 t.Fatalf("expected %q, but got:\n%s", want, got) 71 } 72} 73 74func TestCgoExternalThreadPanic(t *testing.T) { 75 t.Parallel() 76 if runtime.GOOS == "plan9" { 77 t.Skipf("no pthreads on %s", runtime.GOOS) 78 } 79 got := runTestProg(t, "testprogcgo", "CgoExternalThreadPanic") 80 want := "panic: BOOM" 81 if !strings.Contains(got, want) { 82 t.Fatalf("want failure containing %q. output:\n%s\n", want, got) 83 } 84} 85 86func TestCgoExternalThreadSIGPROF(t *testing.T) { 87 t.Parallel() 88 // issue 9456. 89 switch runtime.GOOS { 90 case "plan9", "windows": 91 t.Skipf("no pthreads on %s", runtime.GOOS) 92 } 93 if runtime.GOARCH == "ppc64" && runtime.GOOS == "linux" { 94 // TODO(austin) External linking not implemented on 95 // linux/ppc64 (issue #8912) 96 t.Skipf("no external linking on ppc64") 97 } 98 99 exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof") 100 if err != nil { 101 t.Fatal(err) 102 } 103 104 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput() 105 if err != nil { 106 t.Fatalf("exit status: %v\n%s", err, got) 107 } 108 109 if want := "OK\n"; string(got) != want { 110 t.Fatalf("expected %q, but got:\n%s", want, got) 111 } 112} 113 114func TestCgoExternalThreadSignal(t *testing.T) { 115 t.Parallel() 116 // issue 10139 117 switch runtime.GOOS { 118 case "plan9", "windows": 119 t.Skipf("no pthreads on %s", runtime.GOOS) 120 } 121 122 exe, err := buildTestProg(t, "testprogcgo", "-tags=threadprof") 123 if err != nil { 124 t.Fatal(err) 125 } 126 127 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoExternalThreadSIGPROF")).CombinedOutput() 128 if err != nil { 129 t.Fatalf("exit status: %v\n%s", err, got) 130 } 131 132 want := []byte("OK\n") 133 if !bytes.Equal(got, want) { 134 t.Fatalf("expected %q, but got:\n%s", want, got) 135 } 136} 137 138func TestCgoDLLImports(t *testing.T) { 139 // test issue 9356 140 if runtime.GOOS != "windows" { 141 t.Skip("skipping windows specific test") 142 } 143 got := runTestProg(t, "testprogcgo", "CgoDLLImportsMain") 144 want := "OK\n" 145 if got != want { 146 t.Fatalf("expected %q, but got %v", want, got) 147 } 148} 149 150func TestCgoExecSignalMask(t *testing.T) { 151 t.Parallel() 152 // Test issue 13164. 153 switch runtime.GOOS { 154 case "windows", "plan9": 155 t.Skipf("skipping signal mask test on %s", runtime.GOOS) 156 } 157 got := runTestProg(t, "testprogcgo", "CgoExecSignalMask", "GOTRACEBACK=system") 158 want := "OK\n" 159 if got != want { 160 t.Errorf("expected %q, got %v", want, got) 161 } 162} 163 164func TestEnsureDropM(t *testing.T) { 165 t.Parallel() 166 // Test for issue 13881. 167 switch runtime.GOOS { 168 case "windows", "plan9": 169 t.Skipf("skipping dropm test on %s", runtime.GOOS) 170 } 171 got := runTestProg(t, "testprogcgo", "EnsureDropM") 172 want := "OK\n" 173 if got != want { 174 t.Errorf("expected %q, got %v", want, got) 175 } 176} 177 178// Test for issue 14387. 179// Test that the program that doesn't need any cgo pointer checking 180// takes about the same amount of time with it as without it. 181func TestCgoCheckBytes(t *testing.T) { 182 t.Parallel() 183 // Make sure we don't count the build time as part of the run time. 184 testenv.MustHaveGoBuild(t) 185 exe, err := buildTestProg(t, "testprogcgo") 186 if err != nil { 187 t.Fatal(err) 188 } 189 190 // Try it 10 times to avoid flakiness. 191 const tries = 10 192 var tot1, tot2 time.Duration 193 for i := 0; i < tries; i++ { 194 cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes")) 195 cmd.Env = append(cmd.Env, "GODEBUG=cgocheck=0", fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i)) 196 197 start := time.Now() 198 cmd.Run() 199 d1 := time.Since(start) 200 201 cmd = testenv.CleanCmdEnv(exec.Command(exe, "CgoCheckBytes")) 202 cmd.Env = append(cmd.Env, fmt.Sprintf("GO_CGOCHECKBYTES_TRY=%d", i)) 203 204 start = time.Now() 205 cmd.Run() 206 d2 := time.Since(start) 207 208 if d1*20 > d2 { 209 // The slow version (d2) was less than 20 times 210 // slower than the fast version (d1), so OK. 211 return 212 } 213 214 tot1 += d1 215 tot2 += d2 216 } 217 218 t.Errorf("cgo check too slow: got %v, expected at most %v", tot2/tries, (tot1/tries)*20) 219} 220 221func TestCgoPanicDeadlock(t *testing.T) { 222 t.Parallel() 223 // test issue 14432 224 got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock") 225 want := "panic: cgo error\n\n" 226 if !strings.HasPrefix(got, want) { 227 t.Fatalf("output does not start with %q:\n%s", want, got) 228 } 229} 230 231func TestCgoCCodeSIGPROF(t *testing.T) { 232 t.Parallel() 233 got := runTestProg(t, "testprogcgo", "CgoCCodeSIGPROF") 234 want := "OK\n" 235 if got != want { 236 t.Errorf("expected %q got %v", want, got) 237 } 238} 239 240func TestCgoCrashTraceback(t *testing.T) { 241 t.Parallel() 242 switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { 243 case "darwin/amd64": 244 case "linux/amd64": 245 case "linux/ppc64le": 246 default: 247 t.Skipf("not yet supported on %s", platform) 248 } 249 if runtime.Compiler == "gccgo" { 250 t.Skip("gccgo does not have SetCgoTraceback") 251 } 252 got := runTestProg(t, "testprogcgo", "CrashTraceback") 253 for i := 1; i <= 3; i++ { 254 if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) { 255 t.Errorf("missing cgo symbolizer:%d", i) 256 } 257 } 258} 259 260func TestCgoCrashTracebackGo(t *testing.T) { 261 t.Parallel() 262 switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { 263 case "darwin/amd64": 264 case "linux/amd64": 265 case "linux/ppc64le": 266 default: 267 t.Skipf("not yet supported on %s", platform) 268 } 269 if runtime.Compiler == "gccgo" { 270 t.Skip("gccgo does not have SetCgoTraceback") 271 } 272 got := runTestProg(t, "testprogcgo", "CrashTracebackGo") 273 for i := 1; i <= 3; i++ { 274 want := fmt.Sprintf("main.h%d", i) 275 if !strings.Contains(got, want) { 276 t.Errorf("missing %s", want) 277 } 278 } 279} 280 281func TestCgoTracebackContext(t *testing.T) { 282 t.Parallel() 283 if runtime.Compiler == "gccgo" { 284 t.Skip("gccgo does not have SetCgoTraceback") 285 } 286 got := runTestProg(t, "testprogcgo", "TracebackContext") 287 want := "OK\n" 288 if got != want { 289 t.Errorf("expected %q got %v", want, got) 290 } 291} 292 293func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) { 294 t.Parallel() 295 if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") { 296 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) 297 } 298 if runtime.Compiler == "gccgo" { 299 t.Skip("gccgo does not have SetCgoTraceback") 300 } 301 testenv.MustHaveGoRun(t) 302 303 exe, err := buildTestProg(t, "testprogcgo", buildArg) 304 if err != nil { 305 t.Fatal(err) 306 } 307 308 // pprofCgoTraceback is called whenever CGO code is executing and a signal 309 // is received. Disable signal preemption to increase the likelihood at 310 // least one SIGPROF signal fired to capture a sample. See issue #37201. 311 cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg)) 312 cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1") 313 314 got, err := cmd.CombinedOutput() 315 if err != nil { 316 if testenv.Builder() == "linux-amd64-alpine" { 317 // See Issue 18243 and Issue 19938. 318 t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err) 319 } 320 t.Fatalf("%s\n\n%v", got, err) 321 } 322 fn := strings.TrimSpace(string(got)) 323 defer os.Remove(fn) 324 325 for try := 0; try < 2; try++ { 326 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-traces")) 327 // Check that pprof works both with and without explicit executable on command line. 328 if try == 0 { 329 cmd.Args = append(cmd.Args, exe, fn) 330 } else { 331 cmd.Args = append(cmd.Args, fn) 332 } 333 334 found := false 335 for i, e := range cmd.Env { 336 if strings.HasPrefix(e, "PPROF_TMPDIR=") { 337 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir() 338 found = true 339 break 340 } 341 } 342 if !found { 343 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) 344 } 345 346 out, err := cmd.CombinedOutput() 347 t.Logf("%s:\n%s", cmd.Args, out) 348 if err != nil { 349 t.Error(err) 350 continue 351 } 352 353 trace := findTrace(string(out), top) 354 if len(trace) == 0 { 355 t.Errorf("%s traceback missing.", top) 356 continue 357 } 358 if trace[len(trace)-1] != bottom { 359 t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom) 360 } 361 } 362} 363 364func TestCgoPprof(t *testing.T) { 365 testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main") 366} 367 368func TestCgoPprofPIE(t *testing.T) { 369 testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main") 370} 371 372func TestCgoPprofThread(t *testing.T) { 373 testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2") 374} 375 376func TestCgoPprofThreadNoTraceback(t *testing.T) { 377 testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode") 378} 379 380func TestRaceProf(t *testing.T) { 381 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" { 382 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) 383 } 384 if runtime.Compiler == "gccgo" { 385 t.Skip("gccgo does not have SetCgoTraceback") 386 } 387 388 testenv.MustHaveGoRun(t) 389 390 // This test requires building various packages with -race, so 391 // it's somewhat slow. 392 if testing.Short() { 393 t.Skip("skipping test in -short mode") 394 } 395 396 exe, err := buildTestProg(t, "testprogcgo", "-race") 397 if err != nil { 398 t.Fatal(err) 399 } 400 401 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput() 402 if err != nil { 403 t.Fatal(err) 404 } 405 want := "OK\n" 406 if string(got) != want { 407 t.Errorf("expected %q got %s", want, got) 408 } 409} 410 411func TestRaceSignal(t *testing.T) { 412 t.Parallel() 413 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" { 414 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) 415 } 416 417 testenv.MustHaveGoRun(t) 418 419 // This test requires building various packages with -race, so 420 // it's somewhat slow. 421 if testing.Short() { 422 t.Skip("skipping test in -short mode") 423 } 424 425 exe, err := buildTestProg(t, "testprogcgo", "-race") 426 if err != nil { 427 t.Fatal(err) 428 } 429 430 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput() 431 if err != nil { 432 t.Logf("%s\n", got) 433 t.Fatal(err) 434 } 435 want := "OK\n" 436 if string(got) != want { 437 t.Errorf("expected %q got %s", want, got) 438 } 439} 440 441func TestCgoNumGoroutine(t *testing.T) { 442 switch runtime.GOOS { 443 case "windows", "plan9": 444 t.Skipf("skipping numgoroutine test on %s", runtime.GOOS) 445 } 446 t.Parallel() 447 got := runTestProg(t, "testprogcgo", "NumGoroutine") 448 want := "OK\n" 449 if got != want { 450 t.Errorf("expected %q got %v", want, got) 451 } 452} 453 454func TestCatchPanic(t *testing.T) { 455 t.Parallel() 456 switch runtime.GOOS { 457 case "plan9", "windows": 458 t.Skipf("no signals on %s", runtime.GOOS) 459 case "darwin": 460 if runtime.GOARCH == "amd64" { 461 t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT") 462 } 463 } 464 465 testenv.MustHaveGoRun(t) 466 467 exe, err := buildTestProg(t, "testprogcgo") 468 if err != nil { 469 t.Fatal(err) 470 } 471 472 for _, early := range []bool{true, false} { 473 cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic")) 474 // Make sure a panic results in a crash. 475 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") 476 if early { 477 // Tell testprogcgo to install an early signal handler for SIGABRT 478 cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1") 479 } 480 if out, err := cmd.CombinedOutput(); err != nil { 481 t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out) 482 } 483 } 484} 485 486func TestCgoLockOSThreadExit(t *testing.T) { 487 switch runtime.GOOS { 488 case "plan9", "windows": 489 t.Skipf("no pthreads on %s", runtime.GOOS) 490 } 491 t.Parallel() 492 testLockOSThreadExit(t, "testprogcgo") 493} 494 495func TestWindowsStackMemoryCgo(t *testing.T) { 496 if runtime.GOOS != "windows" { 497 t.Skip("skipping windows specific test") 498 } 499 testenv.SkipFlaky(t, 22575) 500 o := runTestProg(t, "testprogcgo", "StackMemory") 501 stackUsage, err := strconv.Atoi(o) 502 if err != nil { 503 t.Fatalf("Failed to read stack usage: %v", err) 504 } 505 if expected, got := 100<<10, stackUsage; got > expected { 506 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got) 507 } 508} 509 510func TestSigStackSwapping(t *testing.T) { 511 switch runtime.GOOS { 512 case "plan9", "windows": 513 t.Skipf("no sigaltstack on %s", runtime.GOOS) 514 } 515 t.Parallel() 516 got := runTestProg(t, "testprogcgo", "SigStack") 517 want := "OK\n" 518 if got != want { 519 t.Errorf("expected %q got %v", want, got) 520 } 521} 522 523func TestCgoTracebackSigpanic(t *testing.T) { 524 // Test unwinding over a sigpanic in C code without a C 525 // symbolizer. See issue #23576. 526 if runtime.GOOS == "windows" { 527 // On Windows if we get an exception in C code, we let 528 // the Windows exception handler unwind it, rather 529 // than injecting a sigpanic. 530 t.Skip("no sigpanic in C on windows") 531 } 532 t.Parallel() 533 got := runTestProg(t, "testprogcgo", "TracebackSigpanic") 534 want := "runtime.sigpanic" 535 if !strings.Contains(got, want) { 536 t.Fatalf("want failure containing %q. output:\n%s\n", want, got) 537 } 538 nowant := "unexpected return pc" 539 if strings.Contains(got, nowant) { 540 t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got) 541 } 542} 543 544// Test that C code called via cgo can use large Windows thread stacks 545// and call back in to Go without crashing. See issue #20975. 546// 547// See also TestBigStackCallbackSyscall. 548func TestBigStackCallbackCgo(t *testing.T) { 549 if runtime.GOOS != "windows" { 550 t.Skip("skipping windows specific test") 551 } 552 t.Parallel() 553 got := runTestProg(t, "testprogcgo", "BigStack") 554 want := "OK\n" 555 if got != want { 556 t.Errorf("expected %q got %v", want, got) 557 } 558} 559 560func nextTrace(lines []string) ([]string, []string) { 561 var trace []string 562 for n, line := range lines { 563 if strings.HasPrefix(line, "---") { 564 return trace, lines[n+1:] 565 } 566 fields := strings.Fields(strings.TrimSpace(line)) 567 if len(fields) == 0 { 568 continue 569 } 570 // Last field contains the function name. 571 trace = append(trace, fields[len(fields)-1]) 572 } 573 return nil, nil 574} 575 576func findTrace(text, top string) []string { 577 lines := strings.Split(text, "\n") 578 _, lines = nextTrace(lines) // Skip the header. 579 for len(lines) > 0 { 580 var t []string 581 t, lines = nextTrace(lines) 582 if len(t) == 0 { 583 continue 584 } 585 if t[0] == top { 586 return t 587 } 588 } 589 return nil 590} 591 592func TestSegv(t *testing.T) { 593 switch runtime.GOOS { 594 case "plan9", "windows": 595 t.Skipf("no signals on %s", runtime.GOOS) 596 } 597 598 for _, test := range []string{"Segv", "SegvInCgo"} { 599 t.Run(test, func(t *testing.T) { 600 t.Parallel() 601 got := runTestProg(t, "testprogcgo", test) 602 t.Log(got) 603 if !strings.Contains(got, "SIGSEGV") { 604 t.Errorf("expected crash from signal") 605 } 606 }) 607 } 608} 609 610// TestEINTR tests that we handle EINTR correctly. 611// See issue #20400 and friends. 612func TestEINTR(t *testing.T) { 613 switch runtime.GOOS { 614 case "plan9", "windows": 615 t.Skipf("no EINTR on %s", runtime.GOOS) 616 case "linux": 617 if runtime.GOARCH == "386" { 618 // On linux-386 the Go signal handler sets 619 // a restorer function that is not preserved 620 // by the C sigaction call in the test, 621 // causing the signal handler to crash when 622 // returning the normal code. The test is not 623 // architecture-specific, so just skip on 386 624 // rather than doing a complicated workaround. 625 t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer") 626 } 627 } 628 629 t.Parallel() 630 output := runTestProg(t, "testprogcgo", "EINTR") 631 want := "OK\n" 632 if output != want { 633 t.Fatalf("want %s, got %s\n", want, output) 634 } 635} 636 637// Issue #42207. 638func TestNeedmDeadlock(t *testing.T) { 639 switch runtime.GOOS { 640 case "plan9", "windows": 641 t.Skipf("no signals on %s", runtime.GOOS) 642 } 643 output := runTestProg(t, "testprogcgo", "NeedmDeadlock") 644 want := "OK\n" 645 if output != want { 646 t.Fatalf("want %s, got %s\n", want, output) 647 } 648} 649