| // 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{}) |
| } |