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 8package cgotest 9 10/* 11#include <stdint.h> 12 13void issue7978cb(void); 14 15// use ugly atomic variable sync since that doesn't require calling back into 16// Go code or OS dependencies 17static void issue7978c(uint32_t *sync) { 18 while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 0) 19 ; 20 __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST); 21 while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 2) 22 ; 23 issue7978cb(); 24 __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST); 25 while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 6) 26 ; 27} 28*/ 29import "C" 30 31import ( 32 "runtime" 33 "runtime/debug" 34 "strings" 35 "sync/atomic" 36 "testing" 37) 38 39var issue7978sync uint32 40 41func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) { 42 runtime.GC() 43 buf := make([]byte, 65536) 44 trace := string(buf[:runtime.Stack(buf, true)]) 45 for _, goroutine := range strings.Split(trace, "\n\n") { 46 if strings.Contains(goroutine, "test.issue7978go") { 47 trace := strings.Split(goroutine, "\n") 48 // look for the expected function in the stack 49 for i := 0; i < depth; i++ { 50 if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) { 51 t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine) 52 return 53 } 54 if strings.Contains(trace[1+2*i], wantFunc) { 55 return 56 } 57 } 58 t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine) 59 return 60 } 61 } 62 t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace) 63} 64 65func issue7978wait(store uint32, wait uint32) { 66 if store != 0 { 67 atomic.StoreUint32(&issue7978sync, store) 68 } 69 for atomic.LoadUint32(&issue7978sync) != wait { 70 runtime.Gosched() 71 } 72} 73 74//export issue7978cb 75func issue7978cb() { 76 // Force a stack growth from the callback to put extra 77 // pressure on the runtime. See issue #17785. 78 growStack(64) 79 issue7978wait(3, 4) 80} 81 82func growStack(n int) int { 83 var buf [128]int 84 if n == 0 { 85 return 0 86 } 87 return buf[growStack(n-1)] 88} 89 90func issue7978go() { 91 C.issue7978c((*C.uint32_t)(&issue7978sync)) 92 issue7978wait(7, 8) 93} 94 95func test7978(t *testing.T) { 96 if runtime.Compiler == "gccgo" { 97 t.Skip("gccgo can not do stack traces of C code") 98 } 99 debug.SetTraceback("2") 100 issue7978sync = 0 101 go issue7978go() 102 // test in c code, before callback 103 issue7978wait(0, 1) 104 issue7978check(t, "_Cfunc_issue7978c(", "", 1) 105 // test in go code, during callback 106 issue7978wait(2, 3) 107 issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3) 108 // test in c code, after callback 109 issue7978wait(4, 5) 110 issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1) 111 // test in go code, after return from cgo 112 issue7978wait(6, 7) 113 issue7978check(t, "test.issue7978go(", "", 3) 114 atomic.StoreUint32(&issue7978sync, 8) 115} 116