github.com/comwrg/go/src@v0.0.0-20220319063731-c238d0440370/runtime/crash_cgo_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 //go:build cgo 6 // +build cgo 7 8 package runtime_test 9 10 import ( 11 "bytes" 12 "fmt" 13 "github.com/comwrg/go/src/internal/testenv" 14 "os" 15 "os/exec" 16 "runtime" 17 "strconv" 18 "strings" 19 "testing" 20 "time" 21 ) 22 23 func TestCgoCrashHandler(t *testing.T) { 24 t.Parallel() 25 testCrashHandler(t, true) 26 } 27 28 func 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 43 func 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 52 func 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 75 func 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 87 func 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 115 func 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 139 func 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 151 func 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 165 func 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. 182 func 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 222 func 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 232 func 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 241 func 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 got := runTestProg(t, "testprogcgo", "CrashTraceback") 251 for i := 1; i <= 3; i++ { 252 if !strings.Contains(got, fmt.Sprintf("cgo symbolizer:%d", i)) { 253 t.Errorf("missing cgo symbolizer:%d", i) 254 } 255 } 256 } 257 258 func TestCgoCrashTracebackGo(t *testing.T) { 259 t.Parallel() 260 switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform { 261 case "darwin/amd64": 262 case "linux/amd64": 263 case "linux/ppc64le": 264 default: 265 t.Skipf("not yet supported on %s", platform) 266 } 267 got := runTestProg(t, "testprogcgo", "CrashTracebackGo") 268 for i := 1; i <= 3; i++ { 269 want := fmt.Sprintf("main.h%d", i) 270 if !strings.Contains(got, want) { 271 t.Errorf("missing %s", want) 272 } 273 } 274 } 275 276 func TestCgoTracebackContext(t *testing.T) { 277 t.Parallel() 278 got := runTestProg(t, "testprogcgo", "TracebackContext") 279 want := "OK\n" 280 if got != want { 281 t.Errorf("expected %q got %v", want, got) 282 } 283 } 284 285 func TestCgoTracebackContextPreemption(t *testing.T) { 286 t.Parallel() 287 got := runTestProg(t, "testprogcgo", "TracebackContextPreemption") 288 want := "OK\n" 289 if got != want { 290 t.Errorf("expected %q got %v", want, got) 291 } 292 } 293 294 func testCgoPprof(t *testing.T, buildArg, runArg, top, bottom string) { 295 t.Parallel() 296 if runtime.GOOS != "linux" || (runtime.GOARCH != "amd64" && runtime.GOARCH != "ppc64le") { 297 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) 298 } 299 testenv.MustHaveGoRun(t) 300 301 exe, err := buildTestProg(t, "testprogcgo", buildArg) 302 if err != nil { 303 t.Fatal(err) 304 } 305 306 // pprofCgoTraceback is called whenever CGO code is executing and a signal 307 // is received. Disable signal preemption to increase the likelihood at 308 // least one SIGPROF signal fired to capture a sample. See issue #37201. 309 cmd := testenv.CleanCmdEnv(exec.Command(exe, runArg)) 310 cmd.Env = append(cmd.Env, "GODEBUG=asyncpreemptoff=1") 311 312 got, err := cmd.CombinedOutput() 313 if err != nil { 314 if testenv.Builder() == "linux-amd64-alpine" { 315 // See Issue 18243 and Issue 19938. 316 t.Skipf("Skipping failing test on Alpine (golang.org/issue/18243). Ignoring error: %v", err) 317 } 318 t.Fatalf("%s\n\n%v", got, err) 319 } 320 fn := strings.TrimSpace(string(got)) 321 defer os.Remove(fn) 322 323 for try := 0; try < 2; try++ { 324 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-traces")) 325 // Check that pprof works both with and without explicit executable on command line. 326 if try == 0 { 327 cmd.Args = append(cmd.Args, exe, fn) 328 } else { 329 cmd.Args = append(cmd.Args, fn) 330 } 331 332 found := false 333 for i, e := range cmd.Env { 334 if strings.HasPrefix(e, "PPROF_TMPDIR=") { 335 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir() 336 found = true 337 break 338 } 339 } 340 if !found { 341 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) 342 } 343 344 out, err := cmd.CombinedOutput() 345 t.Logf("%s:\n%s", cmd.Args, out) 346 if err != nil { 347 t.Error(err) 348 continue 349 } 350 351 trace := findTrace(string(out), top) 352 if len(trace) == 0 { 353 t.Errorf("%s traceback missing.", top) 354 continue 355 } 356 if trace[len(trace)-1] != bottom { 357 t.Errorf("invalid traceback origin: got=%v; want=[%s ... %s]", trace, top, bottom) 358 } 359 } 360 } 361 362 func TestCgoPprof(t *testing.T) { 363 testCgoPprof(t, "", "CgoPprof", "cpuHog", "runtime.main") 364 } 365 366 func TestCgoPprofPIE(t *testing.T) { 367 testCgoPprof(t, "-buildmode=pie", "CgoPprof", "cpuHog", "runtime.main") 368 } 369 370 func TestCgoPprofThread(t *testing.T) { 371 testCgoPprof(t, "", "CgoPprofThread", "cpuHogThread", "cpuHogThread2") 372 } 373 374 func TestCgoPprofThreadNoTraceback(t *testing.T) { 375 testCgoPprof(t, "", "CgoPprofThreadNoTraceback", "cpuHogThread", "runtime._ExternalCode") 376 } 377 378 func TestRaceProf(t *testing.T) { 379 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" { 380 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) 381 } 382 383 testenv.MustHaveGoRun(t) 384 385 // This test requires building various packages with -race, so 386 // it's somewhat slow. 387 if testing.Short() { 388 t.Skip("skipping test in -short mode") 389 } 390 391 exe, err := buildTestProg(t, "testprogcgo", "-race") 392 if err != nil { 393 t.Fatal(err) 394 } 395 396 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceprof")).CombinedOutput() 397 if err != nil { 398 t.Fatal(err) 399 } 400 want := "OK\n" 401 if string(got) != want { 402 t.Errorf("expected %q got %s", want, got) 403 } 404 } 405 406 func TestRaceSignal(t *testing.T) { 407 t.Parallel() 408 if (runtime.GOOS != "linux" && runtime.GOOS != "freebsd") || runtime.GOARCH != "amd64" { 409 t.Skipf("not yet supported on %s/%s", runtime.GOOS, runtime.GOARCH) 410 } 411 412 testenv.MustHaveGoRun(t) 413 414 // This test requires building various packages with -race, so 415 // it's somewhat slow. 416 if testing.Short() { 417 t.Skip("skipping test in -short mode") 418 } 419 420 exe, err := buildTestProg(t, "testprogcgo", "-race") 421 if err != nil { 422 t.Fatal(err) 423 } 424 425 got, err := testenv.CleanCmdEnv(exec.Command(exe, "CgoRaceSignal")).CombinedOutput() 426 if err != nil { 427 t.Logf("%s\n", got) 428 t.Fatal(err) 429 } 430 want := "OK\n" 431 if string(got) != want { 432 t.Errorf("expected %q got %s", want, got) 433 } 434 } 435 436 func TestCgoNumGoroutine(t *testing.T) { 437 switch runtime.GOOS { 438 case "windows", "plan9": 439 t.Skipf("skipping numgoroutine test on %s", runtime.GOOS) 440 } 441 t.Parallel() 442 got := runTestProg(t, "testprogcgo", "NumGoroutine") 443 want := "OK\n" 444 if got != want { 445 t.Errorf("expected %q got %v", want, got) 446 } 447 } 448 449 func TestCatchPanic(t *testing.T) { 450 t.Parallel() 451 switch runtime.GOOS { 452 case "plan9", "windows": 453 t.Skipf("no signals on %s", runtime.GOOS) 454 case "darwin": 455 if runtime.GOARCH == "amd64" { 456 t.Skipf("crash() on darwin/amd64 doesn't raise SIGABRT") 457 } 458 } 459 460 testenv.MustHaveGoRun(t) 461 462 exe, err := buildTestProg(t, "testprogcgo") 463 if err != nil { 464 t.Fatal(err) 465 } 466 467 for _, early := range []bool{true, false} { 468 cmd := testenv.CleanCmdEnv(exec.Command(exe, "CgoCatchPanic")) 469 // Make sure a panic results in a crash. 470 cmd.Env = append(cmd.Env, "GOTRACEBACK=crash") 471 if early { 472 // Tell testprogcgo to install an early signal handler for SIGABRT 473 cmd.Env = append(cmd.Env, "CGOCATCHPANIC_EARLY_HANDLER=1") 474 } 475 if out, err := cmd.CombinedOutput(); err != nil { 476 t.Errorf("testprogcgo CgoCatchPanic failed: %v\n%s", err, out) 477 } 478 } 479 } 480 481 func TestCgoLockOSThreadExit(t *testing.T) { 482 switch runtime.GOOS { 483 case "plan9", "windows": 484 t.Skipf("no pthreads on %s", runtime.GOOS) 485 } 486 t.Parallel() 487 testLockOSThreadExit(t, "testprogcgo") 488 } 489 490 func TestWindowsStackMemoryCgo(t *testing.T) { 491 if runtime.GOOS != "windows" { 492 t.Skip("skipping windows specific test") 493 } 494 testenv.SkipFlaky(t, 22575) 495 o := runTestProg(t, "testprogcgo", "StackMemory") 496 stackUsage, err := strconv.Atoi(o) 497 if err != nil { 498 t.Fatalf("Failed to read stack usage: %v", err) 499 } 500 if expected, got := 100<<10, stackUsage; got > expected { 501 t.Fatalf("expected < %d bytes of memory per thread, got %d", expected, got) 502 } 503 } 504 505 func TestSigStackSwapping(t *testing.T) { 506 switch runtime.GOOS { 507 case "plan9", "windows": 508 t.Skipf("no sigaltstack on %s", runtime.GOOS) 509 } 510 t.Parallel() 511 got := runTestProg(t, "testprogcgo", "SigStack") 512 want := "OK\n" 513 if got != want { 514 t.Errorf("expected %q got %v", want, got) 515 } 516 } 517 518 func TestCgoTracebackSigpanic(t *testing.T) { 519 // Test unwinding over a sigpanic in C code without a C 520 // symbolizer. See issue #23576. 521 if runtime.GOOS == "windows" { 522 // On Windows if we get an exception in C code, we let 523 // the Windows exception handler unwind it, rather 524 // than injecting a sigpanic. 525 t.Skip("no sigpanic in C on windows") 526 } 527 t.Parallel() 528 got := runTestProg(t, "testprogcgo", "TracebackSigpanic") 529 want := "runtime.sigpanic" 530 if !strings.Contains(got, want) { 531 t.Fatalf("want failure containing %q. output:\n%s\n", want, got) 532 } 533 nowant := "unexpected return pc" 534 if strings.Contains(got, nowant) { 535 t.Fatalf("failure incorrectly contains %q. output:\n%s\n", nowant, got) 536 } 537 } 538 539 // Test that C code called via cgo can use large Windows thread stacks 540 // and call back in to Go without crashing. See issue #20975. 541 // 542 // See also TestBigStackCallbackSyscall. 543 func TestBigStackCallbackCgo(t *testing.T) { 544 if runtime.GOOS != "windows" { 545 t.Skip("skipping windows specific test") 546 } 547 t.Parallel() 548 got := runTestProg(t, "testprogcgo", "BigStack") 549 want := "OK\n" 550 if got != want { 551 t.Errorf("expected %q got %v", want, got) 552 } 553 } 554 555 func nextTrace(lines []string) ([]string, []string) { 556 var trace []string 557 for n, line := range lines { 558 if strings.HasPrefix(line, "---") { 559 return trace, lines[n+1:] 560 } 561 fields := strings.Fields(strings.TrimSpace(line)) 562 if len(fields) == 0 { 563 continue 564 } 565 // Last field contains the function name. 566 trace = append(trace, fields[len(fields)-1]) 567 } 568 return nil, nil 569 } 570 571 func findTrace(text, top string) []string { 572 lines := strings.Split(text, "\n") 573 _, lines = nextTrace(lines) // Skip the header. 574 for len(lines) > 0 { 575 var t []string 576 t, lines = nextTrace(lines) 577 if len(t) == 0 { 578 continue 579 } 580 if t[0] == top { 581 return t 582 } 583 } 584 return nil 585 } 586 587 func TestSegv(t *testing.T) { 588 switch runtime.GOOS { 589 case "plan9", "windows": 590 t.Skipf("no signals on %s", runtime.GOOS) 591 } 592 593 for _, test := range []string{"Segv", "SegvInCgo"} { 594 t.Run(test, func(t *testing.T) { 595 t.Parallel() 596 got := runTestProg(t, "testprogcgo", test) 597 t.Log(got) 598 if !strings.Contains(got, "SIGSEGV") { 599 t.Errorf("expected crash from signal") 600 } 601 }) 602 } 603 } 604 605 // TestEINTR tests that we handle EINTR correctly. 606 // See issue #20400 and friends. 607 func TestEINTR(t *testing.T) { 608 switch runtime.GOOS { 609 case "plan9", "windows": 610 t.Skipf("no EINTR on %s", runtime.GOOS) 611 case "linux": 612 if runtime.GOARCH == "386" { 613 // On linux-386 the Go signal handler sets 614 // a restorer function that is not preserved 615 // by the C sigaction call in the test, 616 // causing the signal handler to crash when 617 // returning the normal code. The test is not 618 // architecture-specific, so just skip on 386 619 // rather than doing a complicated workaround. 620 t.Skip("skipping on linux-386; C sigaction does not preserve Go restorer") 621 } 622 } 623 624 t.Parallel() 625 output := runTestProg(t, "testprogcgo", "EINTR") 626 want := "OK\n" 627 if output != want { 628 t.Fatalf("want %s, got %s\n", want, output) 629 } 630 } 631 632 // Issue #42207. 633 func TestNeedmDeadlock(t *testing.T) { 634 switch runtime.GOOS { 635 case "plan9", "windows": 636 t.Skipf("no signals on %s", runtime.GOOS) 637 } 638 output := runTestProg(t, "testprogcgo", "NeedmDeadlock") 639 want := "OK\n" 640 if output != want { 641 t.Fatalf("want %s, got %s\n", want, output) 642 } 643 }