Home | History | Annotate | Download | only in test
      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 }
    139