github.com/rafaeltorres324/go/src@v0.0.0-20210519164414-9fdf653a9838/runtime/debug_test.go (about) 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. 4 5 // TODO: This test could be implemented on all (most?) UNIXes if we 6 // added syscall.Tgkill more widely. 7 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. 11 12 // +build amd64 13 // +build linux 14 // +build !race 15 16 package runtime_test 17 18 import ( 19 "fmt" 20 "os" 21 "regexp" 22 "runtime" 23 "runtime/debug" 24 "sync/atomic" 25 "syscall" 26 "testing" 27 ) 28 29 func startDebugCallWorker(t *testing.T) (g *runtime.G, after func()) { 30 // This can deadlock if run under a debugger because it 31 // depends on catching SIGTRAP, which is usually swallowed by 32 // a debugger. 33 skipUnderDebugger(t) 34 35 // This can deadlock if there aren't enough threads or if a GC 36 // tries to interrupt an atomic loop (see issue #10958). We 37 // use 8 Ps so there's room for the debug call worker, 38 // something that's trying to preempt the call worker, and the 39 // goroutine that's trying to stop the call worker. 40 ogomaxprocs := runtime.GOMAXPROCS(8) 41 ogcpercent := debug.SetGCPercent(-1) 42 43 // ready is a buffered channel so debugCallWorker won't block 44 // on sending to it. This makes it less likely we'll catch 45 // debugCallWorker while it's in the runtime. 46 ready := make(chan *runtime.G, 1) 47 var stop uint32 48 done := make(chan error) 49 go debugCallWorker(ready, &stop, done) 50 g = <-ready 51 return g, func() { 52 atomic.StoreUint32(&stop, 1) 53 err := <-done 54 if err != nil { 55 t.Fatal(err) 56 } 57 runtime.GOMAXPROCS(ogomaxprocs) 58 debug.SetGCPercent(ogcpercent) 59 } 60 } 61 62 func debugCallWorker(ready chan<- *runtime.G, stop *uint32, done chan<- error) { 63 runtime.LockOSThread() 64 defer runtime.UnlockOSThread() 65 66 ready <- runtime.Getg() 67 68 x := 2 69 debugCallWorker2(stop, &x) 70 if x != 1 { 71 done <- fmt.Errorf("want x = 2, got %d; register pointer not adjusted?", x) 72 } 73 close(done) 74 } 75 76 // Don't inline this function, since we want to test adjusting 77 // pointers in the arguments. 78 // 79 //go:noinline 80 func debugCallWorker2(stop *uint32, x *int) { 81 for atomic.LoadUint32(stop) == 0 { 82 // Strongly encourage x to live in a register so we 83 // can test pointer register adjustment. 84 *x++ 85 } 86 *x = 1 87 } 88 89 func debugCallTKill(tid int) error { 90 return syscall.Tgkill(syscall.Getpid(), tid, syscall.SIGTRAP) 91 } 92 93 // skipUnderDebugger skips the current test when running under a 94 // debugger (specifically if this process has a tracer). This is 95 // Linux-specific. 96 func skipUnderDebugger(t *testing.T) { 97 pid := syscall.Getpid() 98 status, err := os.ReadFile(fmt.Sprintf("/proc/%d/status", pid)) 99 if err != nil { 100 t.Logf("couldn't get proc tracer: %s", err) 101 return 102 } 103 re := regexp.MustCompile(`TracerPid:\s+([0-9]+)`) 104 sub := re.FindSubmatch(status) 105 if sub == nil { 106 t.Logf("couldn't find proc tracer PID") 107 return 108 } 109 if string(sub[1]) == "0" { 110 return 111 } 112 t.Skip("test will deadlock under a debugger") 113 } 114 115 func TestDebugCall(t *testing.T) { 116 g, after := startDebugCallWorker(t) 117 defer after() 118 119 // Inject a call into the debugCallWorker goroutine and test 120 // basic argument and result passing. 121 var args struct { 122 x int 123 yRet int 124 } 125 fn := func(x int) (yRet int) { 126 return x + 1 127 } 128 args.x = 42 129 if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill, false); err != nil { 130 t.Fatal(err) 131 } 132 if args.yRet != 43 { 133 t.Fatalf("want 43, got %d", args.yRet) 134 } 135 } 136 137 func TestDebugCallLarge(t *testing.T) { 138 g, after := startDebugCallWorker(t) 139 defer after() 140 141 // Inject a call with a large call frame. 142 const N = 128 143 var args struct { 144 in [N]int 145 out [N]int 146 } 147 fn := func(in [N]int) (out [N]int) { 148 for i := range in { 149 out[i] = in[i] + 1 150 } 151 return 152 } 153 var want [N]int 154 for i := range args.in { 155 args.in[i] = i 156 want[i] = i + 1 157 } 158 if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill, false); err != nil { 159 t.Fatal(err) 160 } 161 if want != args.out { 162 t.Fatalf("want %v, got %v", want, args.out) 163 } 164 } 165 166 func TestDebugCallGC(t *testing.T) { 167 g, after := startDebugCallWorker(t) 168 defer after() 169 170 // Inject a call that performs a GC. 171 if _, err := runtime.InjectDebugCall(g, runtime.GC, nil, debugCallTKill, false); err != nil { 172 t.Fatal(err) 173 } 174 } 175 176 func TestDebugCallGrowStack(t *testing.T) { 177 g, after := startDebugCallWorker(t) 178 defer after() 179 180 // Inject a call that grows the stack. debugCallWorker checks 181 // for stack pointer breakage. 182 if _, err := runtime.InjectDebugCall(g, func() { growStack(nil) }, nil, debugCallTKill, false); err != nil { 183 t.Fatal(err) 184 } 185 } 186 187 //go:nosplit 188 func debugCallUnsafePointWorker(gpp **runtime.G, ready, stop *uint32) { 189 // The nosplit causes this function to not contain safe-points 190 // except at calls. 191 runtime.LockOSThread() 192 defer runtime.UnlockOSThread() 193 194 *gpp = runtime.Getg() 195 196 for atomic.LoadUint32(stop) == 0 { 197 atomic.StoreUint32(ready, 1) 198 } 199 } 200 201 func TestDebugCallUnsafePoint(t *testing.T) { 202 skipUnderDebugger(t) 203 204 // This can deadlock if there aren't enough threads or if a GC 205 // tries to interrupt an atomic loop (see issue #10958). 206 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 207 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 208 209 // Test that the runtime refuses call injection at unsafe points. 210 var g *runtime.G 211 var ready, stop uint32 212 defer atomic.StoreUint32(&stop, 1) 213 go debugCallUnsafePointWorker(&g, &ready, &stop) 214 for atomic.LoadUint32(&ready) == 0 { 215 runtime.Gosched() 216 } 217 218 _, err := runtime.InjectDebugCall(g, func() {}, nil, debugCallTKill, true) 219 if msg := "call not at safe point"; err == nil || err.Error() != msg { 220 t.Fatalf("want %q, got %s", msg, err) 221 } 222 } 223 224 func TestDebugCallPanic(t *testing.T) { 225 skipUnderDebugger(t) 226 227 // This can deadlock if there aren't enough threads. 228 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 229 230 ready := make(chan *runtime.G) 231 var stop uint32 232 defer atomic.StoreUint32(&stop, 1) 233 go func() { 234 runtime.LockOSThread() 235 defer runtime.UnlockOSThread() 236 ready <- runtime.Getg() 237 for atomic.LoadUint32(&stop) == 0 { 238 } 239 }() 240 g := <-ready 241 242 p, err := runtime.InjectDebugCall(g, func() { panic("test") }, nil, debugCallTKill, false) 243 if err != nil { 244 t.Fatal(err) 245 } 246 if ps, ok := p.(string); !ok || ps != "test" { 247 t.Fatalf("wanted panic %v, got %v", "test", p) 248 } 249 }