github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/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 "io/ioutil" 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). 37 ogomaxprocs := runtime.GOMAXPROCS(2) 38 ogcpercent := debug.SetGCPercent(-1) 39 40 ready := make(chan *runtime.G) 41 var stop uint32 42 done := make(chan error) 43 go debugCallWorker(ready, &stop, done) 44 g = <-ready 45 return g, func() { 46 atomic.StoreUint32(&stop, 1) 47 err := <-done 48 if err != nil { 49 t.Fatal(err) 50 } 51 runtime.GOMAXPROCS(ogomaxprocs) 52 debug.SetGCPercent(ogcpercent) 53 } 54 } 55 56 func debugCallWorker(ready chan<- *runtime.G, stop *uint32, done chan<- error) { 57 runtime.LockOSThread() 58 defer runtime.UnlockOSThread() 59 60 ready <- runtime.Getg() 61 62 x := 2 63 debugCallWorker2(stop, &x) 64 if x != 1 { 65 done <- fmt.Errorf("want x = 2, got %d; register pointer not adjusted?", x) 66 } 67 close(done) 68 } 69 70 func debugCallWorker2(stop *uint32, x *int) { 71 for atomic.LoadUint32(stop) == 0 { 72 // Strongly encourage x to live in a register so we 73 // can test pointer register adjustment. 74 *x++ 75 } 76 *x = 1 77 } 78 79 func debugCallTKill(tid int) error { 80 return syscall.Tgkill(syscall.Getpid(), tid, syscall.SIGTRAP) 81 } 82 83 // skipUnderDebugger skips the current test when running under a 84 // debugger (specifically if this process has a tracer). This is 85 // Linux-specific. 86 func skipUnderDebugger(t *testing.T) { 87 pid := syscall.Getpid() 88 status, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/status", pid)) 89 if err != nil { 90 t.Logf("couldn't get proc tracer: %s", err) 91 return 92 } 93 re := regexp.MustCompile(`TracerPid:\s+([0-9]+)`) 94 sub := re.FindSubmatch(status) 95 if sub == nil { 96 t.Logf("couldn't find proc tracer PID") 97 return 98 } 99 if string(sub[1]) == "0" { 100 return 101 } 102 t.Skip("test will deadlock under a debugger") 103 } 104 105 func TestDebugCall(t *testing.T) { 106 g, after := startDebugCallWorker(t) 107 defer after() 108 109 // Inject a call into the debugCallWorker goroutine and test 110 // basic argument and result passing. 111 var args struct { 112 x int 113 yRet int 114 } 115 fn := func(x int) (yRet int) { 116 return x + 1 117 } 118 args.x = 42 119 if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill); err != nil { 120 t.Fatal(err) 121 } 122 if args.yRet != 43 { 123 t.Fatalf("want 43, got %d", args.yRet) 124 } 125 } 126 127 func TestDebugCallLarge(t *testing.T) { 128 g, after := startDebugCallWorker(t) 129 defer after() 130 131 // Inject a call with a large call frame. 132 const N = 128 133 var args struct { 134 in [N]int 135 out [N]int 136 } 137 fn := func(in [N]int) (out [N]int) { 138 for i := range in { 139 out[i] = in[i] + 1 140 } 141 return 142 } 143 var want [N]int 144 for i := range args.in { 145 args.in[i] = i 146 want[i] = i + 1 147 } 148 if _, err := runtime.InjectDebugCall(g, fn, &args, debugCallTKill); err != nil { 149 t.Fatal(err) 150 } 151 if want != args.out { 152 t.Fatalf("want %v, got %v", want, args.out) 153 } 154 } 155 156 func TestDebugCallGC(t *testing.T) { 157 g, after := startDebugCallWorker(t) 158 defer after() 159 160 // Inject a call that performs a GC. 161 if _, err := runtime.InjectDebugCall(g, runtime.GC, nil, debugCallTKill); err != nil { 162 t.Fatal(err) 163 } 164 } 165 166 func TestDebugCallGrowStack(t *testing.T) { 167 g, after := startDebugCallWorker(t) 168 defer after() 169 170 // Inject a call that grows the stack. debugCallWorker checks 171 // for stack pointer breakage. 172 if _, err := runtime.InjectDebugCall(g, func() { growStack(nil) }, nil, debugCallTKill); err != nil { 173 t.Fatal(err) 174 } 175 } 176 177 //go:nosplit 178 func debugCallUnsafePointWorker(gpp **runtime.G, ready, stop *uint32) { 179 // The nosplit causes this function to not contain safe-points 180 // except at calls. 181 runtime.LockOSThread() 182 defer runtime.UnlockOSThread() 183 184 *gpp = runtime.Getg() 185 186 for atomic.LoadUint32(stop) == 0 { 187 atomic.StoreUint32(ready, 1) 188 } 189 } 190 191 func TestDebugCallUnsafePoint(t *testing.T) { 192 skipUnderDebugger(t) 193 194 // This can deadlock if there aren't enough threads or if a GC 195 // tries to interrupt an atomic loop (see issue #10958). 196 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) 197 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 198 199 // Test that the runtime refuses call injection at unsafe points. 200 var g *runtime.G 201 var ready, stop uint32 202 defer atomic.StoreUint32(&stop, 1) 203 go debugCallUnsafePointWorker(&g, &ready, &stop) 204 for atomic.LoadUint32(&ready) == 0 { 205 runtime.Gosched() 206 } 207 208 _, err := runtime.InjectDebugCall(g, func() {}, nil, debugCallTKill) 209 if msg := "call not at safe point"; err == nil || err.Error() != msg { 210 t.Fatalf("want %q, got %s", msg, err) 211 } 212 } 213 214 func TestDebugCallPanic(t *testing.T) { 215 skipUnderDebugger(t) 216 217 // This can deadlock if there aren't enough threads. 218 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) 219 220 ready := make(chan *runtime.G) 221 var stop uint32 222 defer atomic.StoreUint32(&stop, 1) 223 go func() { 224 runtime.LockOSThread() 225 defer runtime.UnlockOSThread() 226 ready <- runtime.Getg() 227 for atomic.LoadUint32(&stop) == 0 { 228 } 229 }() 230 g := <-ready 231 232 p, err := runtime.InjectDebugCall(g, func() { panic("test") }, nil, debugCallTKill) 233 if err != nil { 234 t.Fatal(err) 235 } 236 if ps, ok := p.(string); !ok || ps != "test" { 237 t.Fatalf("wanted panic %v, got %v", "test", p) 238 } 239 }