// Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Issue 7978. Stack tracing didn't work during cgo code after calling a Go // callback. Make sure GC works and the stack trace is correct. package cgotest /* #include void issue7978cb(void); // use ugly atomic variable sync since that doesn't require calling back into // Go code or OS dependencies static void issue7978c(uint32_t *sync) { while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 0) ; __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST); while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 2) ; issue7978cb(); __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST); while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 6) ; } */ import "C" import ( "runtime" "runtime/debug" "strings" "sync/atomic" "testing" ) var issue7978sync uint32 func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) { runtime.GC() buf := make([]byte, 65536) trace := string(buf[:runtime.Stack(buf, true)]) for _, goroutine := range strings.Split(trace, "\n\n") { if strings.Contains(goroutine, "test.issue7978go") { trace := strings.Split(goroutine, "\n") // look for the expected function in the stack for i := 0; i < depth; i++ { if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) { t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine) return } if strings.Contains(trace[1+2*i], wantFunc) { return } } t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine) return } } t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace) } func issue7978wait(store uint32, wait uint32) { if store != 0 { atomic.StoreUint32(&issue7978sync, store) } for atomic.LoadUint32(&issue7978sync) != wait { runtime.Gosched() } } //export issue7978cb func issue7978cb() { // Force a stack growth from the callback to put extra // pressure on the runtime. See issue #17785. growStack(64) issue7978wait(3, 4) } func growStack(n int) int { var buf [128]int if n == 0 { return 0 } return buf[growStack(n-1)] } func issue7978go() { C.issue7978c((*C.uint32_t)(&issue7978sync)) issue7978wait(7, 8) } func test7978(t *testing.T) { if runtime.Compiler == "gccgo" { t.Skip("gccgo can not do stack traces of C code") } debug.SetTraceback("2") issue7978sync = 0 go issue7978go() // test in c code, before callback issue7978wait(0, 1) issue7978check(t, "_Cfunc_issue7978c(", "", 1) // test in go code, during callback issue7978wait(2, 3) issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3) // test in c code, after callback issue7978wait(4, 5) issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1) // test in go code, after return from cgo issue7978wait(6, 7) issue7978check(t, "test.issue7978go(", "", 3) atomic.StoreUint32(&issue7978sync, 8) }