[1/7] Preprocessor cleanup
[official-gcc.git] / libgo / go / runtime / debug_test.go
blob38c764fadb3d86b4cc0411dcbf312fab12dfaa53
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
10 // point.
12 // +build ignore_for_gccgo
13 // +build amd64
14 // +build linux
15 // +build !race
17 package runtime_test
19 import (
20 "fmt"
21 "runtime"
22 "runtime/debug"
23 "sync/atomic"
24 "syscall"
25 "testing"
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)
35 var stop uint32
36 done := make(chan error)
37 go debugCallWorker(ready, &stop, done)
38 g = <-ready
39 return g, func() {
40 atomic.StoreUint32(&stop, 1)
41 err := <-done
42 if err != nil {
43 t.Fatal(err)
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()
56 x := 2
57 debugCallWorker2(stop, &x)
58 if x != 1 {
59 done <- fmt.Errorf("want x = 2, got %d; register pointer not adjusted?", x)
61 close(done)
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.
68 *x++
70 *x = 1
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)
79 defer after()
81 // Inject a call into the debugCallWorker goroutine and test
82 // basic argument and result passing.
83 var args struct {
84 x int
85 yRet int
87 fn := func(x int) (yRet int) {
88 return x + 1
90 args.x = 42
91 if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill); err != nil {
92 t.Fatal(err)
94 if args.yRet != 43 {
95 t.Fatalf("want 43, got %d", args.yRet)
99 func TestDebugCallLarge(t *testing.T) {
100 g, after := startDebugCallWorker(t)
101 defer after()
103 // Inject a call with a large call frame.
104 const N = 128
105 var args struct {
106 in [N]int
107 out [N]int
109 fn := func(in [N]int) (out [N]int) {
110 for i := range in {
111 out[i] = in[i] + 1
113 return
115 var want [N]int
116 for i := range args.in {
117 args.in[i] = i
118 want[i] = i + 1
120 if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill); err != nil {
121 t.Fatal(err)
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)
130 defer after()
132 // Inject a call that performs a GC.
133 if _, err := runtime.InjectDebugCall(g, runtime.GC, nil, debugCallTKill); err != nil {
134 t.Fatal(err)
138 func TestDebugCallGrowStack(t *testing.T) {
139 g, after := startDebugCallWorker(t)
140 defer after()
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 {
145 t.Fatal(err)
149 //go:nosplit
150 func debugCallUnsafePointWorker(gpp **runtime.G, ready, stop *uint32) {
151 // The nosplit causes this function to not contain safe-points
152 // except at calls.
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.
170 var g *runtime.G
171 var ready, stop uint32
172 defer atomic.StoreUint32(&stop, 1)
173 go debugCallUnsafePointWorker(&g, &ready, &stop)
174 for atomic.LoadUint32(&ready) == 0 {
175 runtime.Gosched()
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)
189 var stop uint32
190 defer atomic.StoreUint32(&stop, 1)
191 go func() {
192 runtime.LockOSThread()
193 defer runtime.UnlockOSThread()
194 ready <- runtime.Getg()
195 for atomic.LoadUint32(&stop) == 0 {
198 g := <-ready
200 p, err := runtime.InjectDebugCall(g, func() { panic("test") }, nil, debugCallTKill)
201 if err != nil {
202 t.Fatal(err)
204 if ps, ok := p.(string); !ok || ps != "test" {
205 t.Fatalf("wanted panic %v, got %v", "test", p)