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 7 package runtime_test 8 9 import ( 10 "os/exec" 11 "runtime" 12 "strings" 13 "testing" 14 ) 15 16 func TestCgoCrashHandler(t *testing.T) { 17 testCrashHandler(t, true) 18 } 19 20 func TestCgoSignalDeadlock(t *testing.T) { 21 if testing.Short() && runtime.GOOS == "windows" { 22 t.Skip("Skipping in short mode") // takes up to 64 seconds 23 } 24 got := executeTest(t, cgoSignalDeadlockSource, nil) 25 want := "OK\n" 26 if got != want { 27 t.Fatalf("expected %q, but got %q", want, got) 28 } 29 } 30 31 func TestCgoTraceback(t *testing.T) { 32 got := executeTest(t, cgoTracebackSource, nil) 33 want := "OK\n" 34 if got != want { 35 t.Fatalf("expected %q, but got %q", want, got) 36 } 37 } 38 39 func TestCgoCallbackGC(t *testing.T) { 40 if runtime.GOOS == "plan9" || runtime.GOOS == "windows" { 41 t.Skipf("no pthreads on %s", runtime.GOOS) 42 } 43 if testing.Short() && runtime.GOOS == "dragonfly" { 44 t.Skip("see golang.org/issue/11990") 45 } 46 got := executeTest(t, cgoCallbackGCSource, nil) 47 want := "OK\n" 48 if got != want { 49 t.Fatalf("expected %q, but got %q", want, got) 50 } 51 } 52 53 func TestCgoExternalThreadPanic(t *testing.T) { 54 if runtime.GOOS == "plan9" { 55 t.Skipf("no pthreads on %s", runtime.GOOS) 56 } 57 csrc := cgoExternalThreadPanicC 58 if runtime.GOOS == "windows" { 59 csrc = cgoExternalThreadPanicC_windows 60 } 61 got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", csrc) 62 want := "panic: BOOM" 63 if !strings.Contains(got, want) { 64 t.Fatalf("want failure containing %q. output:\n%s\n", want, got) 65 } 66 } 67 68 func TestCgoExternalThreadSIGPROF(t *testing.T) { 69 // issue 9456. 70 switch runtime.GOOS { 71 case "plan9", "windows": 72 t.Skipf("no pthreads on %s", runtime.GOOS) 73 case "darwin": 74 if runtime.GOARCH != "arm" && runtime.GOARCH != "arm64" { 75 // static constructor needs external linking, but we don't support 76 // external linking on OS X 10.6. 77 out, err := exec.Command("uname", "-r").Output() 78 if err != nil { 79 t.Fatalf("uname -r failed: %v", err) 80 } 81 // OS X 10.6 == Darwin 10.x 82 if strings.HasPrefix(string(out), "10.") { 83 t.Skipf("no external linking on OS X 10.6") 84 } 85 } 86 } 87 if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" { 88 // TODO(austin) External linking not implemented on 89 // ppc64 (issue #8912) 90 t.Skipf("no external linking on ppc64") 91 } 92 got := executeTest(t, cgoExternalThreadSIGPROFSource, nil) 93 want := "OK\n" 94 if got != want { 95 t.Fatalf("expected %q, but got %q", want, got) 96 } 97 } 98 99 func TestCgoExternalThreadSignal(t *testing.T) { 100 // issue 10139 101 switch runtime.GOOS { 102 case "plan9", "windows": 103 t.Skipf("no pthreads on %s", runtime.GOOS) 104 } 105 got := executeTest(t, cgoExternalThreadSignalSource, nil) 106 want := "OK\n" 107 if got != want { 108 t.Fatalf("expected %q, but got %q", want, got) 109 } 110 } 111 112 func TestCgoDLLImports(t *testing.T) { 113 // test issue 9356 114 if runtime.GOOS != "windows" { 115 t.Skip("skipping windows specific test") 116 } 117 got := executeTest(t, cgoDLLImportsMainSource, nil, "a/a.go", cgoDLLImportsPkgSource) 118 want := "OK\n" 119 if got != want { 120 t.Fatalf("expected %q, but got %v", want, got) 121 } 122 } 123 124 const cgoSignalDeadlockSource = ` 125 package main 126 127 import "C" 128 129 import ( 130 "fmt" 131 "runtime" 132 "time" 133 ) 134 135 func main() { 136 runtime.GOMAXPROCS(100) 137 ping := make(chan bool) 138 go func() { 139 for i := 0; ; i++ { 140 runtime.Gosched() 141 select { 142 case done := <-ping: 143 if done { 144 ping <- true 145 return 146 } 147 ping <- true 148 default: 149 } 150 func() { 151 defer func() { 152 recover() 153 }() 154 var s *string 155 *s = "" 156 }() 157 } 158 }() 159 time.Sleep(time.Millisecond) 160 for i := 0; i < 64; i++ { 161 go func() { 162 runtime.LockOSThread() 163 select {} 164 }() 165 go func() { 166 runtime.LockOSThread() 167 select {} 168 }() 169 time.Sleep(time.Millisecond) 170 ping <- false 171 select { 172 case <-ping: 173 case <-time.After(time.Second): 174 fmt.Printf("HANG\n") 175 return 176 } 177 } 178 ping <- true 179 select { 180 case <-ping: 181 case <-time.After(time.Second): 182 fmt.Printf("HANG\n") 183 return 184 } 185 fmt.Printf("OK\n") 186 } 187 ` 188 189 const cgoTracebackSource = ` 190 package main 191 192 /* void foo(void) {} */ 193 import "C" 194 195 import ( 196 "fmt" 197 "runtime" 198 ) 199 200 func main() { 201 C.foo() 202 buf := make([]byte, 1) 203 runtime.Stack(buf, true) 204 fmt.Printf("OK\n") 205 } 206 ` 207 208 const cgoCallbackGCSource = ` 209 package main 210 211 import "runtime" 212 213 /* 214 #include <pthread.h> 215 216 void go_callback(); 217 218 static void *thr(void *arg) { 219 go_callback(); 220 return 0; 221 } 222 223 static void foo() { 224 pthread_t th; 225 pthread_create(&th, 0, thr, 0); 226 pthread_join(th, 0); 227 } 228 */ 229 import "C" 230 import "fmt" 231 232 //export go_callback 233 func go_callback() { 234 runtime.GC() 235 grow() 236 runtime.GC() 237 } 238 239 var cnt int 240 241 func grow() { 242 x := 10000 243 sum := 0 244 if grow1(&x, &sum) == 0 { 245 panic("bad") 246 } 247 } 248 249 func grow1(x, sum *int) int { 250 if *x == 0 { 251 return *sum + 1 252 } 253 *x-- 254 sum1 := *sum + *x 255 return grow1(x, &sum1) 256 } 257 258 func main() { 259 const P = 100 260 done := make(chan bool) 261 // allocate a bunch of stack frames and spray them with pointers 262 for i := 0; i < P; i++ { 263 go func() { 264 grow() 265 done <- true 266 }() 267 } 268 for i := 0; i < P; i++ { 269 <-done 270 } 271 // now give these stack frames to cgo callbacks 272 for i := 0; i < P; i++ { 273 go func() { 274 C.foo() 275 done <- true 276 }() 277 } 278 for i := 0; i < P; i++ { 279 <-done 280 } 281 fmt.Printf("OK\n") 282 } 283 ` 284 285 const cgoExternalThreadPanicSource = ` 286 package main 287 288 // void start(void); 289 import "C" 290 291 func main() { 292 C.start() 293 select {} 294 } 295 296 //export gopanic 297 func gopanic() { 298 panic("BOOM") 299 } 300 ` 301 302 const cgoExternalThreadPanicC = ` 303 #include <stdlib.h> 304 #include <stdio.h> 305 #include <pthread.h> 306 307 void gopanic(void); 308 309 static void* 310 die(void* x) 311 { 312 gopanic(); 313 return 0; 314 } 315 316 void 317 start(void) 318 { 319 pthread_t t; 320 if(pthread_create(&t, 0, die, 0) != 0) 321 printf("pthread_create failed\n"); 322 } 323 ` 324 325 const cgoExternalThreadPanicC_windows = ` 326 #include <stdlib.h> 327 #include <stdio.h> 328 329 void gopanic(void); 330 331 static void* 332 die(void* x) 333 { 334 gopanic(); 335 return 0; 336 } 337 338 void 339 start(void) 340 { 341 if(_beginthreadex(0, 0, die, 0, 0, 0) != 0) 342 printf("_beginthreadex failed\n"); 343 } 344 ` 345 346 const cgoExternalThreadSIGPROFSource = ` 347 package main 348 349 /* 350 #include <stdint.h> 351 #include <signal.h> 352 #include <pthread.h> 353 354 volatile int32_t spinlock; 355 356 static void *thread1(void *p) { 357 (void)p; 358 while (spinlock == 0) 359 ; 360 pthread_kill(pthread_self(), SIGPROF); 361 spinlock = 0; 362 return NULL; 363 } 364 __attribute__((constructor)) void issue9456() { 365 pthread_t tid; 366 pthread_create(&tid, 0, thread1, NULL); 367 } 368 */ 369 import "C" 370 371 import ( 372 "runtime" 373 "sync/atomic" 374 "unsafe" 375 ) 376 377 func main() { 378 // This test intends to test that sending SIGPROF to foreign threads 379 // before we make any cgo call will not abort the whole process, so 380 // we cannot make any cgo call here. See https://golang.org/issue/9456. 381 atomic.StoreInt32((*int32)(unsafe.Pointer(&C.spinlock)), 1) 382 for atomic.LoadInt32((*int32)(unsafe.Pointer(&C.spinlock))) == 1 { 383 runtime.Gosched() 384 } 385 println("OK") 386 } 387 ` 388 389 const cgoExternalThreadSignalSource = ` 390 package main 391 392 /* 393 #include <pthread.h> 394 395 void **nullptr; 396 397 void *crash(void *p) { 398 *nullptr = p; 399 return 0; 400 } 401 402 int start_crashing_thread(void) { 403 pthread_t tid; 404 return pthread_create(&tid, 0, crash, 0); 405 } 406 */ 407 import "C" 408 409 import ( 410 "fmt" 411 "os" 412 "os/exec" 413 "time" 414 ) 415 416 func main() { 417 if len(os.Args) > 1 && os.Args[1] == "crash" { 418 i := C.start_crashing_thread() 419 if i != 0 { 420 fmt.Println("pthread_create failed:", i) 421 // Exit with 0 because parent expects us to crash. 422 return 423 } 424 425 // We should crash immediately, but give it plenty of 426 // time before failing (by exiting 0) in case we are 427 // running on a slow system. 428 time.Sleep(5 * time.Second) 429 return 430 } 431 432 out, err := exec.Command(os.Args[0], "crash").CombinedOutput() 433 if err == nil { 434 fmt.Println("C signal did not crash as expected\n") 435 fmt.Printf("%s\n", out) 436 os.Exit(1) 437 } 438 439 fmt.Println("OK") 440 } 441 ` 442 443 const cgoDLLImportsMainSource = ` 444 package main 445 446 /* 447 #include <windows.h> 448 449 DWORD getthread() { 450 return GetCurrentThreadId(); 451 } 452 */ 453 import "C" 454 455 import "./a" 456 457 func main() { 458 C.getthread() 459 a.GetThread() 460 println("OK") 461 } 462 ` 463 464 const cgoDLLImportsPkgSource = ` 465 package a 466 467 /* 468 #cgo CFLAGS: -mnop-fun-dllimport 469 470 #include <windows.h> 471 472 DWORD agetthread() { 473 return GetCurrentThreadId(); 474 } 475 */ 476 import "C" 477 478 func GetThread() uint32 { 479 return uint32(C.agetthread()) 480 } 481 ` 482