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