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 	issue7978wait(3, 4)
     92 }
     93 
     94 func issue7978go() {
     95 	C.issue7978c((*C.uint32_t)(&issue7978sync))
     96 	issue7978wait(7, 8)
     97 }
     98 
     99 func test7978(t *testing.T) {
    100 	if runtime.Compiler == "gccgo" {
    101 		t.Skip("gccgo can not do stack traces of C code")
    102 	}
    103 	if C.HAS_SYNC_FETCH_AND_ADD == 0 {
    104 		t.Skip("clang required for __sync_fetch_and_add support on darwin/arm")
    105 	}
    106 	if os.Getenv("GOTRACEBACK") != "2" {
    107 		t.Fatalf("GOTRACEBACK must be 2")
    108 	}
    109 	issue7978sync = 0
    110 	go issue7978go()
    111 	// test in c code, before callback
    112 	issue7978wait(0, 1)
    113 	issue7978check(t, "runtime.cgocall(", "", 1)
    114 	// test in go code, during callback
    115 	issue7978wait(2, 3)
    116 	issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
    117 	// test in c code, after callback
    118 	issue7978wait(4, 5)
    119 	issue7978check(t, "runtime.cgocall(", "runtime.cgocallback", 1)
    120 	// test in go code, after return from cgo
    121 	issue7978wait(6, 7)
    122 	issue7978check(t, "test.issue7978go(", "", 3)
    123 	atomic.StoreUint32(&issue7978sync, 8)
    124 }
    125