1 // Copyright 2018 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 // TODO: This test could be implemented on all (most?) UNIXes if we
6 // added syscall.Tgkill more widely.
8 // We skip all of these tests under race mode because our test thread
9 // spends all of its time in the race runtime, which isn't a safe
12 // +build ignore_for_gccgo
28 func startDebugCallWorker(t
*testing
.T
) (g
*runtime
.G
, after
func()) {
29 // This can deadlock if there aren't enough threads or if a GC
30 // tries to interrupt an atomic loop (see issue #10958).
31 ogomaxprocs
:= runtime
.GOMAXPROCS(2)
32 ogcpercent
:= debug
.SetGCPercent(-1)
34 ready
:= make(chan *runtime
.G
)
36 done
:= make(chan error
)
37 go debugCallWorker(ready
, &stop
, done
)
40 atomic
.StoreUint32(&stop
, 1)
45 runtime
.GOMAXPROCS(ogomaxprocs
)
46 debug
.SetGCPercent(ogcpercent
)
50 func debugCallWorker(ready
chan<- *runtime
.G
, stop
*uint32, done
chan<- error
) {
51 runtime
.LockOSThread()
52 defer runtime
.UnlockOSThread()
54 ready
<- runtime
.Getg()
57 debugCallWorker2(stop
, &x
)
59 done
<- fmt
.Errorf("want x = 2, got %d; register pointer not adjusted?", x
)
64 func debugCallWorker2(stop
*uint32, x
*int) {
65 for atomic
.LoadUint32(stop
) == 0 {
66 // Strongly encourage x to live in a register so we
67 // can test pointer register adjustment.
73 func debugCallTKill(tid
int) error
{
74 return syscall
.Tgkill(syscall
.Getpid(), tid
, syscall
.SIGTRAP
)
77 func TestDebugCall(t
*testing
.T
) {
78 g
, after
:= startDebugCallWorker(t
)
81 // Inject a call into the debugCallWorker goroutine and test
82 // basic argument and result passing.
87 fn
:= func(x
int) (yRet
int) {
91 if _
, err
:= runtime
.InjectDebugCall(g
, fn
, &args
, debugCallTKill
); err
!= nil {
95 t
.Fatalf("want 43, got %d", args
.yRet
)
99 func TestDebugCallLarge(t
*testing
.T
) {
100 g
, after
:= startDebugCallWorker(t
)
103 // Inject a call with a large call frame.
109 fn
:= func(in
[N
]int) (out
[N
]int) {
116 for i
:= range args
.in
{
120 if _
, err
:= runtime
.InjectDebugCall(g
, fn
, &args
, debugCallTKill
); err
!= nil {
123 if want
!= args
.out
{
124 t
.Fatalf("want %v, got %v", want
, args
.out
)
128 func TestDebugCallGC(t
*testing
.T
) {
129 g
, after
:= startDebugCallWorker(t
)
132 // Inject a call that performs a GC.
133 if _
, err
:= runtime
.InjectDebugCall(g
, runtime
.GC
, nil, debugCallTKill
); err
!= nil {
138 func TestDebugCallGrowStack(t
*testing
.T
) {
139 g
, after
:= startDebugCallWorker(t
)
142 // Inject a call that grows the stack. debugCallWorker checks
143 // for stack pointer breakage.
144 if _
, err
:= runtime
.InjectDebugCall(g
, func() { growStack(nil) }, nil, debugCallTKill
); err
!= nil {
150 func debugCallUnsafePointWorker(gpp
**runtime
.G
, ready
, stop
*uint32) {
151 // The nosplit causes this function to not contain safe-points
153 runtime
.LockOSThread()
154 defer runtime
.UnlockOSThread()
156 *gpp
= runtime
.Getg()
158 for atomic
.LoadUint32(stop
) == 0 {
159 atomic
.StoreUint32(ready
, 1)
163 func TestDebugCallUnsafePoint(t
*testing
.T
) {
164 // This can deadlock if there aren't enough threads or if a GC
165 // tries to interrupt an atomic loop (see issue #10958).
166 defer runtime
.GOMAXPROCS(runtime
.GOMAXPROCS(2))
167 defer debug
.SetGCPercent(debug
.SetGCPercent(-1))
169 // Test that the runtime refuses call injection at unsafe points.
171 var ready
, stop
uint32
172 defer atomic
.StoreUint32(&stop
, 1)
173 go debugCallUnsafePointWorker(&g
, &ready
, &stop
)
174 for atomic
.LoadUint32(&ready
) == 0 {
178 _
, err
:= runtime
.InjectDebugCall(g
, func() {}, nil, debugCallTKill
)
179 if msg
:= "call not at safe point"; err
== nil || err
.Error() != msg
{
180 t
.Fatalf("want %q, got %s", msg
, err
)
184 func TestDebugCallPanic(t
*testing
.T
) {
185 // This can deadlock if there aren't enough threads.
186 defer runtime
.GOMAXPROCS(runtime
.GOMAXPROCS(2))
188 ready
:= make(chan *runtime
.G
)
190 defer atomic
.StoreUint32(&stop
, 1)
192 runtime
.LockOSThread()
193 defer runtime
.UnlockOSThread()
194 ready
<- runtime
.Getg()
195 for atomic
.LoadUint32(&stop
) == 0 {
200 p
, err
:= runtime
.InjectDebugCall(g
, func() { panic("test") }, nil, debugCallTKill
)
204 if ps
, ok
:= p
.(string); !ok || ps
!= "test" {
205 t
.Fatalf("wanted panic %v, got %v", "test", p
)