github.com/AESNooper/go/src@v0.0.0-20220218095104-b56a4ab1bbbb/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 //go:build amd64 && linux && !race 13 14 package runtime_test 15 16 import ( 17 "fmt" 18 "internal/abi" 19 "internal/goexperiment" 20 "math" 21 "os" 22 "regexp" 23 "runtime" 24 "runtime/debug" 25 "sync/atomic" 26 "syscall" 27 "testing" 28 ) 29 30 func startDebugCallWorker(t *testing.T) (g *runtime.G, after func()) { 31 // This can deadlock if run under a debugger because it 32 // depends on catching SIGTRAP, which is usually swallowed by 33 // a debugger. 34 skipUnderDebugger(t) 35 36 // This can deadlock if there aren't enough threads or if a GC 37 // tries to interrupt an atomic loop (see issue #10958). Execute 38 // an extra GC to ensure even the sweep phase is done (out of 39 // caution to prevent #49370 from happening). 40 // TODO(mknyszek): This extra GC cycle is likely unnecessary 41 // because preemption (which may happen during the sweep phase) 42 // isn't much of an issue anymore thanks to asynchronous preemption. 43 // The biggest risk is having a write barrier in the debug call 44 // injection test code fire, because it runs in a signal handler 45 // and may not have a P. 46 // 47 // We use 8 Ps so there's room for the debug call worker, 48 // something that's trying to preempt the call worker, and the 49 // goroutine that's trying to stop the call worker. 50 ogomaxprocs := runtime.GOMAXPROCS(8) 51 ogcpercent := debug.SetGCPercent(-1) 52 runtime.GC() 53 54 // ready is a buffered channel so debugCallWorker won't block 55 // on sending to it. This makes it less likely we'll catch 56 // debugCallWorker while it's in the runtime. 57 ready := make(chan *runtime.G, 1) 58 var stop uint32 59 done := make(chan error) 60 go debugCallWorker(ready, &stop, done) 61 g = <-ready 62 return g, func() { 63 atomic.StoreUint32(&stop, 1) 64 err := <-done 65 if err != nil { 66 t.Fatal(err) 67 } 68 runtime.GOMAXPROCS(ogomaxprocs) 69 debug.SetGCPercent(ogcpercent) 70 } 71 } 72 73 func debugCallWorker(ready chan<- *runtime.G, stop *uint32, done chan<- error) { 74 runtime.LockOSThread() 75 defer runtime.UnlockOSThread() 76 77 ready <- runtime.Getg() 78 79 x := 2 80 debugCallWorker2(stop, &x) 81 if x != 1 { 82 done <- fmt.Errorf("want x = 2, got %d; register pointer not adjusted?", x) 83 } 84 close(done) 85 } 86 87 // Don't inline this function, since we want to test adjusting 88 // pointers in the arguments. 89 // 90 //go:noinline 91 func debugCallWorker2(stop *uint32, x *int) { 92 for atomic.LoadUint32(stop) == 0 { 93 // Strongly encourage x to live in a register so we 94 // can test pointer register adjustment. 95 *x++ 96 } 97 *x = 1 98 } 99 100 func debugCallTKill(tid int) error { 101 return syscall.Tgkill(syscall.Getpid(), tid, syscall.SIGTRAP) 102 } 103 104 // skipUnderDebugger skips the current test when running under a 105 // debugger (specifically if this process has a tracer). This is 106 // Linux-specific. 107 func skipUnderDebugger(t *testing.T) { 108 pid := syscall.Getpid() 109 status, err := os.ReadFile(fmt.Sprintf("/proc/%d/status", pid)) 110 if err != nil { 111 t.Logf("couldn't get proc tracer: %s", err) 112 return 113 } 114 re := regexp.MustCompile(`TracerPid:\s+([0-9]+)`) 115 sub := re.FindSubmatch(status) 116 if sub == nil { 117 t.Logf("couldn't find proc tracer PID") 118 return 119 } 120 if string(sub[1]) == "0" { 121 return 122 } 123 t.Skip("test will deadlock under a debugger") 124 } 125 126 func TestDebugCall(t *testing.T) { 127 g, after := startDebugCallWorker(t) 128 defer after() 129 130 type stackArgs struct { 131 x0 int 132 x1 float64 133 y0Ret int 134 y1Ret float64 135 } 136 137 // Inject a call into the debugCallWorker goroutine and test 138 // basic argument and result passing. 139 fn := func(x int, y float64) (y0Ret int, y1Ret float64) { 140 return x + 1, y + 1.0 141 } 142 var args *stackArgs 143 var regs abi.RegArgs 144 intRegs := regs.Ints[:] 145 floatRegs := regs.Floats[:] 146 fval := float64(42.0) 147 if goexperiment.RegabiArgs { 148 intRegs[0] = 42 149 floatRegs[0] = math.Float64bits(fval) 150 } else { 151 args = &stackArgs{ 152 x0: 42, 153 x1: 42.0, 154 } 155 } 156 157 if _, err := runtime.InjectDebugCall(g, fn, ®s, args, debugCallTKill, false); err != nil { 158 t.Fatal(err) 159 } 160 var result0 int 161 var result1 float64 162 if goexperiment.RegabiArgs { 163 result0 = int(intRegs[0]) 164 result1 = math.Float64frombits(floatRegs[0]) 165 } else { 166 result0 = args.y0Ret 167 result1 = args.y1Ret 168 } 169 if result0 != 43 { 170 t.Errorf("want 43, got %d", result0) 171 } 172 if result1 != fval+1 { 173 t.Errorf("want 43, got %f", result1) 174 } 175 } 176 177 func TestDebugCallLarge(t *testing.T) { 178 g, after := startDebugCallWorker(t) 179 defer after() 180 181 // Inject a call with a large call frame. 182 const N = 128 183 var args struct { 184 in [N]int 185 out [N]int 186 } 187 fn := func(in [N]int) (out [N]int) { 188 for i := range in { 189 out[i] = in[i] + 1 190 } 191 return 192 } 193 var want [N]int 194 for i := range args.in { 195 args.in[i] = i 196 want[i] = i + 1 197 } 198 if _, err := runtime.InjectDebugCall(g, fn, nil, &args, debugCallTKill, false); err != nil { 199 t.Fatal(err) 200 } 201 if want != args.out { 202 t.Fatalf("want %v, got %v", want, args.out) 203 } 204 } 205 206 func TestDebugCallGC(t *testing.T) { 207 g, after := startDebugCallWorker(t) 208 defer after() 209 210 // Inject a call that performs a GC. 211 if _, err := runtime.InjectDebugCall(g, runtime.GC, nil, nil, debugCallTKill, false); err != nil { 212 t.Fatal(err) 213 } 214 } 215 216 func TestDebugCallGrowStack(t *testing.T) { 217 g, after := startDebugCallWorker(t) 218 defer after() 219 220 // Inject a call that grows the stack. debugCallWorker checks 221 // for stack pointer breakage. 222 if _, err := runtime.InjectDebugCall(g, func() { growStack(nil) }, nil, nil, debugCallTKill, false); err != nil { 223 t.Fatal(err) 224 } 225 } 226 227 //go:nosplit 228 func debugCallUnsafePointWorker(gpp **runtime.G, ready, stop *uint32) { 229 // The nosplit causes this function to not contain safe-points 230 // except at calls. 231 runtime.LockOSThread() 232 defer runtime.UnlockOSThread() 233 234 *gpp = runtime.Getg() 235 236 for atomic.LoadUint32(stop) == 0 { 237 atomic.StoreUint32(ready, 1) 238 } 239 } 240 241 func TestDebugCallUnsafePoint(t *testing.T) { 242 skipUnderDebugger(t) 243 244 // This can deadlock if there aren't enough threads or if a GC 245 // tries to interrupt an atomic loop (see issue #10958). 246 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 247 248 // InjectDebugCall cannot be executed while a GC is actively in 249 // progress. Wait until the current GC is done, and turn it off. 250 // 251 // See #49370. 252 runtime.GC() 253 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 254 255 // Test that the runtime refuses call injection at unsafe points. 256 var g *runtime.G 257 var ready, stop uint32 258 defer atomic.StoreUint32(&stop, 1) 259 go debugCallUnsafePointWorker(&g, &ready, &stop) 260 for atomic.LoadUint32(&ready) == 0 { 261 runtime.Gosched() 262 } 263 264 _, err := runtime.InjectDebugCall(g, func() {}, nil, nil, debugCallTKill, true) 265 if msg := "call not at safe point"; err == nil || err.Error() != msg { 266 t.Fatalf("want %q, got %s", msg, err) 267 } 268 } 269 270 func TestDebugCallPanic(t *testing.T) { 271 skipUnderDebugger(t) 272 273 // This can deadlock if there aren't enough threads. 274 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 275 276 // InjectDebugCall cannot be executed while a GC is actively in 277 // progress. Wait until the current GC is done, and turn it off. 278 // 279 // See #10958 and #49370. 280 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 281 // TODO(mknyszek): This extra GC cycle is likely unnecessary 282 // because preemption (which may happen during the sweep phase) 283 // isn't much of an issue anymore thanks to asynchronous preemption. 284 // The biggest risk is having a write barrier in the debug call 285 // injection test code fire, because it runs in a signal handler 286 // and may not have a P. 287 runtime.GC() 288 289 ready := make(chan *runtime.G) 290 var stop uint32 291 defer atomic.StoreUint32(&stop, 1) 292 go func() { 293 runtime.LockOSThread() 294 defer runtime.UnlockOSThread() 295 ready <- runtime.Getg() 296 for atomic.LoadUint32(&stop) == 0 { 297 } 298 }() 299 g := <-ready 300 301 p, err := runtime.InjectDebugCall(g, func() { panic("test") }, nil, nil, debugCallTKill, false) 302 if err != nil { 303 t.Fatal(err) 304 } 305 if ps, ok := p.(string); !ok || ps != "test" { 306 t.Fatalf("wanted panic %v, got %v", "test", p) 307 } 308 }