363 lines
6.5 KiB
Go
363 lines
6.5 KiB
Go
// 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{})
|
|
}
|