天天看點

go panic的那點“破“事

       go panic和c++ coredump差不多, 來看看go panic.

       看程式:

package main 
import "fmt" 

func main() {
	fmt.Println("main begin")
	var p *int
	*p = 0
	fmt.Println("main end")

	for {}
}
           

       結果:

main begin

panic: runtime error: invalid memory address or nil pointer dereference

[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1090f33]

goroutine 1 [running]:

main.main()

        /Users/xxx/test/a.go:7 +0x63

exit status 2

      可見,panic後,程序退出了。

      再看:

package main 
import "fmt" 
import "runtime/debug"

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("haha")
			debug.PrintStack()
		}
	}()


	fmt.Println("main begin")
	var p *int
	*p = 0
	fmt.Println("main end")

	for {}
}
           

      結果:

main begin

haha

goroutine 1 [running]:

runtime/debug.Stack(0xc00000c018, 0xc000076de8, 0x1)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7

runtime/debug.PrintStack()

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22

main.main.func1()

        /Users/xxx/test/a.go:9 +0x82

panic(0x10a9620, 0x115d520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9

main.main()

        /Users/xxx/test/a.go:16 +0x83

       程序還是退出了,可見在main中recover并沒有什麼卵用。

       再看:

package main 
import "fmt" 
import "runtime/debug"

func fun() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("haha")
			debug.PrintStack()
		}
	}()
	
	fmt.Println("fun begin")
	var p *int
	*p = 0
	fmt.Println("fun end")
}


func main() {
	fmt.Println("main begin")
	fun()
	fmt.Println("main end")
	for {}
}
           

        結果:

main begin

fun begin

haha

goroutine 1 [running]:

runtime/debug.Stack(0xc00008c008, 0xc000082d88, 0x1)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7

runtime/debug.PrintStack()

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22

main.fun.func1()

        /Users/xxx/test/a.go:9 +0x82

panic(0x10a96e0, 0x115d520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9

main.fun()

        /Users/xxx/test/a.go:15 +0x83

main.main()

        /Users/xxx/test/a.go:22 +0x66

main end

     可見,在fun層panic後退出了,沒有被for{}卡住,但在main層沒有退出,被for{}卡住了, 程序還是活着的。

     那好,現在來看看:

package main 
import "fmt" 
import "runtime/debug" 

func high() {
	fmt.Println("high begin")
	middle()
	fmt.Println("high end")
}

func middle() {
	fmt.Println("middle begin")
	low()
	var p *int
	*p = 0
	fmt.Println("middle end")
}

func low() {
	fmt.Println("low begin")
	fmt.Println("low end")
}

func fun() {
    fmt.Println("fun begin")

	defer func() {
		debug.PrintStack()
	}()

	fmt.Println("yyy")
	high()
	fmt.Println("zzz")

	fmt.Println("fun end")

	for {}
}

func main() {
	fmt.Println("main begin")
	fun()
	a := 2
	b := 3
	fmt.Println("main end", a * b)

	for {}
}
           

       結果:

main begin

fun begin

yyy

high begin

middle begin

low begin

low end

goroutine 1 [running]:

runtime/debug.Stack(0x0, 0x10, 0x8)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7

runtime/debug.PrintStack()

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22

main.fun.func1()

        /Users/xxx/test/a.go:28 +0x20

panic(0x10a9a00, 0x115e520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9

main.middle()

        /Users/xxx/test/a.go:15 +0x68

main.high()

        /Users/xxx/test/a.go:7 +0x66

main.fun()

        /Users/xxx/test/a.go:32 +0xc6

main.main()

        /Users/xxx/test/a.go:42 +0x66

panic: runtime error: invalid memory address or nil pointer dereference

[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1091528]

goroutine 1 [running]:

main.middle()

        /Users/xxx/test/a.go:15 +0x68

main.high()

        /Users/xxx/test/a.go:7 +0x66

main.fun()

        /Users/xxx/test/a.go:32 +0xc6

main.main()

        /Users/xxx/test/a.go:42 +0x66

exit status 2

      很顯然,沒有recover去處理panic資訊,是以整個程序崩潰, 如下:

      再來看看:

package main 
import "fmt" 
import "runtime/debug" 

func high() {
	fmt.Println("high begin")
	middle()
	fmt.Println("high end")
}

func middle() {
	fmt.Println("middle begin")
	low()
	var p *int
	*p = 0
	fmt.Println("middle end")
}

func low() {
	fmt.Println("low begin")
	fmt.Println("low end")
}

func fun() {
    fmt.Println("fun begin")

	defer func() {
		if err := recover(); err != nil {
			debug.PrintStack()
		}
	}()

	fmt.Println("yyy")
	high()
	fmt.Println("zzz")

	fmt.Println("fun end")

	for {}
}

func main() {
	fmt.Println("main begin")
	fun()
	a := 2
	b := 3
	fmt.Println("main end", a * b)

	for {}
}
           

        結果:

main begin

fun begin

yyy

high begin

middle begin

low begin

low end

goroutine 1 [running]:

runtime/debug.Stack(0x115e5c0, 0xc000042c00, 0x0)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7

runtime/debug.PrintStack()

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22

main.fun.func1()

        /Users/xxx/test/a.go:29 +0x42

panic(0x10a9a20, 0x115e520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9

main.middle()

        /Users/xxx/test/a.go:15 +0x68

main.high()

        /Users/xxx/test/a.go:7 +0x66

main.fun()

        /Users/xxx/test/a.go:34 +0xc6

main.main()

        /Users/xxx/test/a.go:44 +0x66

main end 6

      可以看到middle層有panic,  故提前異常退出middle, high和fun,  但在fun層被recover了,是以main層仍然正常,整個程序不會崩潰,仍然活得好好的。

     最後看看,如何擷取堆棧資訊的串呢? 且看:

package main 
import "fmt" 
import "runtime/debug" 

func fun() {
    fmt.Println("fun begin")

	defer func() {
		if err := recover(); err != nil {
			debug.PrintStack()
			fmt.Println("xxx", string(debug.Stack()))
		}
	}()

	var p *int
	*p = 0
	fmt.Println("fun end")

	for {}
}

func main() {
	fmt.Println("main begin")
	fun()
	fmt.Println("main end")

	for {}
}
           

        結果:

main begin

fun begin

goroutine 1 [running]:

runtime/debug.Stack(0xc000088060, 0xc00009a000, 0xa)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7

runtime/debug.PrintStack()

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:16 +0x22

main.fun.func1()

        /Users/xxx/test/a.go:10 +0x46

panic(0x10a9760, 0x115d520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9

main.fun()

        /Users/xxx/test/a.go:16 +0x7f

main.main()

        /Users/xxx/test/a.go:24 +0x66

xxx goroutine 1 [running]:

runtime/debug.Stack(0xc00007ada8, 0x10a9760, 0x115d520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/debug/stack.go:24 +0xa7

main.fun.func1()

        /Users/xxx/test/a.go:11 +0x4b

panic(0x10a9760, 0x115d520)

        /usr/local/Cellar/go/1.11.1/libexec/src/runtime/panic.go:513 +0x1b9

main.fun()

        /Users/xxx/test/a.go:16 +0x7f

main.main()

        /Users/xxx/test/a.go:24 +0x66

main end

     總之,panic觸發層到recover層都會異常退出。 但recover層之上不會異常退出。

     不多說。

繼續閱讀