| // Copyright 2015 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import ( | |
| "fmt" | |
| "runtime" | |
| "runtime/debug" | |
| "time" | |
| ) | |
| func init() { | |
| registerInit("InitDeadlock", InitDeadlock) | |
| registerInit("NoHelperGoroutines", NoHelperGoroutines) | |
| register("SimpleDeadlock", SimpleDeadlock) | |
| register("LockedDeadlock", LockedDeadlock) | |
| register("LockedDeadlock2", LockedDeadlock2) | |
| register("GoexitDeadlock", GoexitDeadlock) | |
| register("StackOverflow", StackOverflow) | |
| register("ThreadExhaustion", ThreadExhaustion) | |
| register("RecursivePanic", RecursivePanic) | |
| register("RecursivePanic2", RecursivePanic2) | |
| register("RecursivePanic3", RecursivePanic3) | |
| register("RecursivePanic4", RecursivePanic4) | |
| register("RecursivePanic5", RecursivePanic5) | |
| register("GoexitExit", GoexitExit) | |
| register("GoNil", GoNil) | |
| register("MainGoroutineID", MainGoroutineID) | |
| register("Breakpoint", Breakpoint) | |
| register("GoexitInPanic", GoexitInPanic) | |
| register("PanicAfterGoexit", PanicAfterGoexit) | |
| register("RecoveredPanicAfterGoexit", RecoveredPanicAfterGoexit) | |
| register("RecoverBeforePanicAfterGoexit", RecoverBeforePanicAfterGoexit) | |
| register("RecoverBeforePanicAfterGoexit2", RecoverBeforePanicAfterGoexit2) | |
| register("PanicTraceback", PanicTraceback) | |
| register("GoschedInPanic", GoschedInPanic) | |
| register("SyscallInPanic", SyscallInPanic) | |
| register("PanicLoop", PanicLoop) | |
| } | |
| func SimpleDeadlock() { | |
| select {} | |
| panic("not reached") | |
| } | |
| func InitDeadlock() { | |
| select {} | |
| panic("not reached") | |
| } | |
| func LockedDeadlock() { | |
| runtime.LockOSThread() | |
| select {} | |
| } | |
| func LockedDeadlock2() { | |
| go func() { | |
| runtime.LockOSThread() | |
| select {} | |
| }() | |
| time.Sleep(time.Millisecond) | |
| select {} | |
| } | |
| func GoexitDeadlock() { | |
| F := func() { | |
| for i := 0; i < 10; i++ { | |
| } | |
| } | |
| go F() | |
| go F() | |
| runtime.Goexit() | |
| } | |
| func StackOverflow() { | |
| var f func() byte | |
| f = func() byte { | |
| var buf [64 << 10]byte | |
| return buf[0] + f() | |
| } | |
| debug.SetMaxStack(1474560) | |
| f() | |
| } | |
| func ThreadExhaustion() { | |
| debug.SetMaxThreads(10) | |
| c := make(chan int) | |
| for i := 0; i < 100; i++ { | |
| go func() { | |
| runtime.LockOSThread() | |
| c <- 0 | |
| select {} | |
| }() | |
| <-c | |
| } | |
| } | |
| func RecursivePanic() { | |
| func() { | |
| defer func() { | |
| fmt.Println(recover()) | |
| }() | |
| var x [8192]byte | |
| func(x [8192]byte) { | |
| defer func() { | |
| if err := recover(); err != nil { | |
| panic("wrap: " + err.(string)) | |
| } | |
| }() | |
| panic("bad") | |
| }(x) | |
| }() | |
| panic("again") | |
| } | |
| // Same as RecursivePanic, but do the first recover and the second panic in | |
| // separate defers, and make sure they are executed in the correct order. | |
| func RecursivePanic2() { | |
| func() { | |
| defer func() { | |
| fmt.Println(recover()) | |
| }() | |
| var x [8192]byte | |
| func(x [8192]byte) { | |
| defer func() { | |
| panic("second panic") | |
| }() | |
| defer func() { | |
| fmt.Println(recover()) | |
| }() | |
| panic("first panic") | |
| }(x) | |
| }() | |
| panic("third panic") | |
| } | |
| // Make sure that the first panic finished as a panic, even though the second | |
| // panic was recovered | |
| func RecursivePanic3() { | |
| defer func() { | |
| defer func() { | |
| recover() | |
| }() | |
| panic("second panic") | |
| }() | |
| panic("first panic") | |
| } | |
| // Test case where a single defer recovers one panic but starts another panic. If | |
| // the second panic is never recovered, then the recovered first panic will still | |
| // appear on the panic stack (labeled '[recovered]') and the runtime stack. | |
| func RecursivePanic4() { | |
| defer func() { | |
| recover() | |
| panic("second panic") | |
| }() | |
| panic("first panic") | |
| } | |
| // Test case where we have an open-coded defer higher up the stack (in two), and | |
| // in the current function (three) we recover in a defer while we still have | |
| // another defer to be processed. | |
| func RecursivePanic5() { | |
| one() | |
| panic("third panic") | |
| } | |
| //go:noinline | |
| func one() { | |
| two() | |
| } | |
| //go:noinline | |
| func two() { | |
| defer func() { | |
| }() | |
| three() | |
| } | |
| //go:noinline | |
| func three() { | |
| defer func() { | |
| }() | |
| defer func() { | |
| fmt.Println(recover()) | |
| }() | |
| defer func() { | |
| fmt.Println(recover()) | |
| panic("second panic") | |
| }() | |
| panic("first panic") | |
| } | |
| func GoexitExit() { | |
| println("t1") | |
| go func() { | |
| time.Sleep(time.Millisecond) | |
| }() | |
| i := 0 | |
| println("t2") | |
| runtime.SetFinalizer(&i, func(p *int) {}) | |
| println("t3") | |
| runtime.GC() | |
| println("t4") | |
| runtime.Goexit() | |
| } | |
| func GoNil() { | |
| defer func() { | |
| recover() | |
| }() | |
| var f func() | |
| go f() | |
| select {} | |
| } | |
| func MainGoroutineID() { | |
| panic("test") | |
| } | |
| func NoHelperGoroutines() { | |
| i := 0 | |
| runtime.SetFinalizer(&i, func(p *int) {}) | |
| time.AfterFunc(time.Hour, func() {}) | |
| panic("oops") | |
| } | |
| func Breakpoint() { | |
| runtime.Breakpoint() | |
| } | |
| func GoexitInPanic() { | |
| go func() { | |
| defer func() { | |
| runtime.Goexit() | |
| }() | |
| panic("hello") | |
| }() | |
| runtime.Goexit() | |
| } | |
| type errorThatGosched struct{} | |
| func (errorThatGosched) Error() string { | |
| runtime.Gosched() | |
| return "errorThatGosched" | |
| } | |
| func GoschedInPanic() { | |
| panic(errorThatGosched{}) | |
| } | |
| type errorThatPrint struct{} | |
| func (errorThatPrint) Error() string { | |
| fmt.Println("1") | |
| fmt.Println("2") | |
| return "3" | |
| } | |
| func SyscallInPanic() { | |
| panic(errorThatPrint{}) | |
| } | |
| func PanicAfterGoexit() { | |
| defer func() { | |
| panic("hello") | |
| }() | |
| runtime.Goexit() | |
| } | |
| func RecoveredPanicAfterGoexit() { | |
| defer func() { | |
| defer func() { | |
| r := recover() | |
| if r == nil { | |
| panic("bad recover") | |
| } | |
| }() | |
| panic("hello") | |
| }() | |
| runtime.Goexit() | |
| } | |
| func RecoverBeforePanicAfterGoexit() { | |
| // 1. defer a function that recovers | |
| // 2. defer a function that panics | |
| // 3. call goexit | |
| // Goexit runs the #2 defer. Its panic | |
| // is caught by the #1 defer. For Goexit, we explicitly | |
| // resume execution in the Goexit loop, instead of resuming | |
| // execution in the caller (which would make the Goexit disappear!) | |
| defer func() { | |
| r := recover() | |
| if r == nil { | |
| panic("bad recover") | |
| } | |
| }() | |
| defer func() { | |
| panic("hello") | |
| }() | |
| runtime.Goexit() | |
| } | |
| func RecoverBeforePanicAfterGoexit2() { | |
| for i := 0; i < 2; i++ { | |
| defer func() { | |
| }() | |
| } | |
| // 1. defer a function that recovers | |
| // 2. defer a function that panics | |
| // 3. call goexit | |
| // Goexit runs the #2 defer. Its panic | |
| // is caught by the #1 defer. For Goexit, we explicitly | |
| // resume execution in the Goexit loop, instead of resuming | |
| // execution in the caller (which would make the Goexit disappear!) | |
| defer func() { | |
| r := recover() | |
| if r == nil { | |
| panic("bad recover") | |
| } | |
| }() | |
| defer func() { | |
| panic("hello") | |
| }() | |
| runtime.Goexit() | |
| } | |
| func PanicTraceback() { | |
| pt1() | |
| } | |
| func pt1() { | |
| defer func() { | |
| panic("panic pt1") | |
| }() | |
| pt2() | |
| } | |
| func pt2() { | |
| defer func() { | |
| panic("panic pt2") | |
| }() | |
| panic("hello") | |
| } | |
| type panicError struct{} | |
| func (*panicError) Error() string { | |
| panic("double error") | |
| } | |
| func PanicLoop() { | |
| panic(&panicError{}) | |
| } |