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