Home | History | Annotate | Download | only in runtime
      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