github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/misc/cgo/test/issue7978.go (about)

     1  // Copyright 2014 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  // Issue 7978.  Stack tracing didn't work during cgo code after calling a Go
     6  // callback.  Make sure GC works and the stack trace is correct.
     7  
     8  package cgotest
     9  
    10  /*
    11  #include <stdint.h>
    12  
    13  void issue7978cb(void);
    14  
    15  #if defined(__APPLE__) && defined(__arm__)
    16  // on Darwin/ARM, libSystem doesn't provide implementation of the __sync_fetch_and_add
    17  // primitive, and although gcc supports it, it doesn't inline its definition.
    18  // Clang could inline its definition, so we require clang on Darwin/ARM.
    19  #if defined(__clang__)
    20  #define HAS_SYNC_FETCH_AND_ADD 1
    21  #else
    22  #define HAS_SYNC_FETCH_AND_ADD 0
    23  #endif
    24  #else
    25  #define HAS_SYNC_FETCH_AND_ADD 1
    26  #endif
    27  
    28  // use ugly atomic variable sync since that doesn't require calling back into
    29  // Go code or OS dependencies
    30  static void issue7978c(uint32_t *sync) {
    31  #if HAS_SYNC_FETCH_AND_ADD
    32  	while(__sync_fetch_and_add(sync, 0) != 0)
    33  		;
    34  	__sync_fetch_and_add(sync, 1);
    35  	while(__sync_fetch_and_add(sync, 0) != 2)
    36  		;
    37  	issue7978cb();
    38  	__sync_fetch_and_add(sync, 1);
    39  	while(__sync_fetch_and_add(sync, 0) != 6)
    40  		;
    41  #endif
    42  }
    43  */
    44  import "C"
    45  
    46  import (
    47  	"os"
    48  	"runtime"
    49  	"strings"
    50  	"sync/atomic"
    51  	"testing"
    52  )
    53  
    54  var issue7978sync uint32
    55  
    56  func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
    57  	runtime.GC()
    58  	buf := make([]byte, 65536)
    59  	trace := string(buf[:runtime.Stack(buf, true)])
    60  	for _, goroutine := range strings.Split(trace, "\n\n") {
    61  		if strings.Contains(goroutine, "test.issue7978go") {
    62  			trace := strings.Split(goroutine, "\n")
    63  			// look for the expected function in the stack
    64  			for i := 0; i < depth; i++ {
    65  				if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) {
    66  					t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine)
    67  					return
    68  				}
    69  				if strings.Contains(trace[1+2*i], wantFunc) {
    70  					return
    71  				}
    72  			}
    73  			t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine)
    74  			return
    75  		}
    76  	}
    77  	t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace)
    78  }
    79  
    80  func issue7978wait(store uint32, wait uint32) {
    81  	if store != 0 {
    82  		atomic.StoreUint32(&issue7978sync, store)
    83  	}
    84  	for atomic.LoadUint32(&issue7978sync) != wait {
    85  		runtime.Gosched()
    86  	}
    87  }
    88  
    89  //export issue7978cb
    90  func issue7978cb() {
    91  	// Force a stack growth from the callback to put extra
    92  	// pressure on the runtime. See issue #17785.
    93  	growStack(64)
    94  	issue7978wait(3, 4)
    95  }
    96  
    97  func growStack(n int) int {
    98  	var buf [128]int
    99  	if n == 0 {
   100  		return 0
   101  	}
   102  	return buf[growStack(n-1)]
   103  }
   104  
   105  func issue7978go() {
   106  	C.issue7978c((*C.uint32_t)(&issue7978sync))
   107  	issue7978wait(7, 8)
   108  }
   109  
   110  func test7978(t *testing.T) {
   111  	if runtime.Compiler == "gccgo" {
   112  		t.Skip("gccgo can not do stack traces of C code")
   113  	}
   114  	if C.HAS_SYNC_FETCH_AND_ADD == 0 {
   115  		t.Skip("clang required for __sync_fetch_and_add support on darwin/arm")
   116  	}
   117  	if runtime.GOOS == "android" || runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
   118  		t.Skip("GOTRACEBACK is not passed on to the exec wrapper")
   119  	}
   120  	if os.Getenv("GOTRACEBACK") != "2" {
   121  		t.Fatalf("GOTRACEBACK must be 2")
   122  	}
   123  	issue7978sync = 0
   124  	go issue7978go()
   125  	// test in c code, before callback
   126  	issue7978wait(0, 1)
   127  	issue7978check(t, "_Cfunc_issue7978c(", "", 1)
   128  	// test in go code, during callback
   129  	issue7978wait(2, 3)
   130  	issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
   131  	// test in c code, after callback
   132  	issue7978wait(4, 5)
   133  	issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1)
   134  	// test in go code, after return from cgo
   135  	issue7978wait(6, 7)
   136  	issue7978check(t, "test.issue7978go(", "", 3)
   137  	atomic.StoreUint32(&issue7978sync, 8)
   138  }