github.com/rohankumardubey/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/pkg/runtime/crash_test.go (about) 1 // Copyright 2012 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 package runtime_test 6 7 import ( 8 "io/ioutil" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "strings" 13 "testing" 14 "text/template" 15 ) 16 17 // testEnv excludes GODEBUG from the environment 18 // to prevent its output from breaking tests that 19 // are trying to parse other command output. 20 func testEnv(cmd *exec.Cmd) *exec.Cmd { 21 if cmd.Env != nil { 22 panic("environment already set") 23 } 24 for _, env := range os.Environ() { 25 if strings.HasPrefix(env, "GODEBUG=") { 26 continue 27 } 28 cmd.Env = append(cmd.Env, env) 29 } 30 return cmd 31 } 32 33 func executeTest(t *testing.T, templ string, data interface{}) string { 34 checkStaleRuntime(t) 35 36 st := template.Must(template.New("crashSource").Parse(templ)) 37 38 dir, err := ioutil.TempDir("", "go-build") 39 if err != nil { 40 t.Fatalf("failed to create temp directory: %v", err) 41 } 42 defer os.RemoveAll(dir) 43 44 src := filepath.Join(dir, "main.go") 45 f, err := os.Create(src) 46 if err != nil { 47 t.Fatalf("failed to create file: %v", err) 48 } 49 err = st.Execute(f, data) 50 if err != nil { 51 f.Close() 52 t.Fatalf("failed to execute template: %v", err) 53 } 54 if err := f.Close(); err != nil { 55 t.Fatalf("failed to close file: %v", err) 56 } 57 58 got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput() 59 return string(got) 60 } 61 62 func checkStaleRuntime(t *testing.T) { 63 // 'go run' uses the installed copy of runtime.a, which may be out of date. 64 out, err := testEnv(exec.Command("go", "list", "-f", "{{.Stale}}", "runtime")).CombinedOutput() 65 if err != nil { 66 t.Fatalf("failed to execute 'go list': %v\n%v", err, string(out)) 67 } 68 if string(out) != "false\n" { 69 t.Fatalf("Stale runtime.a. Run 'go install runtime'.") 70 } 71 } 72 73 func testCrashHandler(t *testing.T, cgo bool) { 74 type crashTest struct { 75 Cgo bool 76 } 77 output := executeTest(t, crashSource, &crashTest{Cgo: cgo}) 78 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" 79 if output != want { 80 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) 81 } 82 } 83 84 func TestCrashHandler(t *testing.T) { 85 testCrashHandler(t, false) 86 } 87 88 func testDeadlock(t *testing.T, source string) { 89 output := executeTest(t, source, nil) 90 want := "fatal error: all goroutines are asleep - deadlock!\n" 91 if !strings.HasPrefix(output, want) { 92 t.Fatalf("output does not start with %q:\n%s", want, output) 93 } 94 } 95 96 func TestSimpleDeadlock(t *testing.T) { 97 testDeadlock(t, simpleDeadlockSource) 98 } 99 100 func TestInitDeadlock(t *testing.T) { 101 testDeadlock(t, initDeadlockSource) 102 } 103 104 func TestLockedDeadlock(t *testing.T) { 105 testDeadlock(t, lockedDeadlockSource) 106 } 107 108 func TestLockedDeadlock2(t *testing.T) { 109 testDeadlock(t, lockedDeadlockSource2) 110 } 111 112 func TestGoexitDeadlock(t *testing.T) { 113 output := executeTest(t, goexitDeadlockSource, nil) 114 if output != "" { 115 t.Fatalf("expected no output, got:\n%s", output) 116 } 117 } 118 119 func TestStackOverflow(t *testing.T) { 120 output := executeTest(t, stackOverflowSource, nil) 121 want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow" 122 if !strings.HasPrefix(output, want) { 123 t.Fatalf("output does not start with %q:\n%s", want, output) 124 } 125 } 126 127 func TestThreadExhaustion(t *testing.T) { 128 output := executeTest(t, threadExhaustionSource, nil) 129 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion" 130 if !strings.HasPrefix(output, want) { 131 t.Fatalf("output does not start with %q:\n%s", want, output) 132 } 133 } 134 135 const crashSource = ` 136 package main 137 138 import ( 139 "fmt" 140 "runtime" 141 ) 142 143 {{if .Cgo}} 144 import "C" 145 {{end}} 146 147 func test(name string) { 148 defer func() { 149 if x := recover(); x != nil { 150 fmt.Printf(" recovered") 151 } 152 fmt.Printf(" done\n") 153 }() 154 fmt.Printf("%s:", name) 155 var s *string 156 _ = *s 157 fmt.Print("SHOULD NOT BE HERE") 158 } 159 160 func testInNewThread(name string) { 161 c := make(chan bool) 162 go func() { 163 runtime.LockOSThread() 164 test(name) 165 c <- true 166 }() 167 <-c 168 } 169 170 func main() { 171 runtime.LockOSThread() 172 test("main") 173 testInNewThread("new-thread") 174 testInNewThread("second-new-thread") 175 test("main-again") 176 } 177 ` 178 179 const simpleDeadlockSource = ` 180 package main 181 func main() { 182 select {} 183 } 184 ` 185 186 const initDeadlockSource = ` 187 package main 188 func init() { 189 select {} 190 } 191 func main() { 192 } 193 ` 194 195 const lockedDeadlockSource = ` 196 package main 197 import "runtime" 198 func main() { 199 runtime.LockOSThread() 200 select {} 201 } 202 ` 203 204 const lockedDeadlockSource2 = ` 205 package main 206 import ( 207 "runtime" 208 "time" 209 ) 210 func main() { 211 go func() { 212 runtime.LockOSThread() 213 select {} 214 }() 215 time.Sleep(time.Millisecond) 216 select {} 217 } 218 ` 219 220 const goexitDeadlockSource = ` 221 package main 222 import ( 223 "runtime" 224 ) 225 226 func F() { 227 for i := 0; i < 10; i++ { 228 } 229 } 230 231 func main() { 232 go F() 233 go F() 234 runtime.Goexit() 235 } 236 ` 237 238 const stackOverflowSource = ` 239 package main 240 241 import "runtime/debug" 242 243 func main() { 244 debug.SetMaxStack(4<<20) 245 f(make([]byte, 10)) 246 } 247 248 func f(x []byte) byte { 249 var buf [64<<10]byte 250 return x[0] + f(buf[:]) 251 } 252 ` 253 254 const threadExhaustionSource = ` 255 package main 256 257 import ( 258 "runtime" 259 "runtime/debug" 260 ) 261 262 func main() { 263 debug.SetMaxThreads(10) 264 c := make(chan int) 265 for i := 0; i < 100; i++ { 266 go func() { 267 runtime.LockOSThread() 268 c <- 0 269 select{} 270 }() 271 <-c 272 } 273 } 274 `