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