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.
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.
13 void issue7978cb(void);
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
22 #define HAS_SYNC_FETCH_AND_ADD 0
25 #define HAS_SYNC_FETCH_AND_ADD 1
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)
34 __sync_fetch_and_add(sync, 1);
35 while(__sync_fetch_and_add(sync, 0) != 2)
38 __sync_fetch_and_add(sync, 1);
39 while(__sync_fetch_and_add(sync, 0) != 6)
54 var issue7978sync
uint32
56 func issue7978check(t
*testing
.T
, wantFunc
string, badFunc
string, depth
int) {
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
)
69 if strings
.Contains(trace
[1+2*i
], wantFunc
) {
73 t
.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc
, goroutine
)
77 t
.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace
)
80 func issue7978wait(store
uint32, wait
uint32) {
82 atomic
.StoreUint32(&issue7978sync
, store
)
84 for atomic
.LoadUint32(&issue7978sync
) != wait
{
91 // Force a stack growth from the callback to put extra
92 // pressure on the runtime. See issue #17785.
97 func growStack(n
int) int {
102 return buf
[growStack(n
-1)]
106 C
.issue7978c((*C
.uint32_t
)(&issue7978sync
))
110 func test7978(t
*testing
.T
) {
111 if runtime
.Compiler
== "gccgo" {
112 t
.Skip("gccgo can not do stack traces of C code")
114 if C
.HAS_SYNC_FETCH_AND_ADD
== 0 {
115 t
.Skip("clang required for __sync_fetch_and_add support on darwin/arm")
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")
120 if os
.Getenv("GOTRACEBACK") != "2" {
121 t
.Fatalf("GOTRACEBACK must be 2")
125 // test in c code, before callback
127 issue7978check(t
, "_Cfunc_issue7978c(", "", 1)
128 // test in go code, during callback
130 issue7978check(t
, "test.issue7978cb(", "test.issue7978go", 3)
131 // test in c code, after callback
133 issue7978check(t
, "_Cfunc_issue7978c(", "_cgoexpwrap", 1)
134 // test in go code, after return from cgo
136 issue7978check(t
, "test.issue7978go(", "", 3)
137 atomic
.StoreUint32(&issue7978sync
, 8)