约定¶
示例使用的 Go 版本: go version go1.12.3 darwin/amd64 。
因为默认 go build 命令会在编译时进行一些优化导致举简单的示例程序的输出不符合举例的要求,所以除特殊说明外,所有示例程序都使用 go build -gcflags=all="-N -l" <name>.go 的方式进行编译。
基本结构¶
1 2 3 4 5 6 7 8 | package main import "fmt" func main() { fmt.Println("hello") panic("error") } |
运行结果:
$ ./hello
hello
panic: error
goroutine 1 [running]:
main.main()
/Users/xxx/hello.go:7 +0x9f
从这个最基础的 stack trace 信息中可以看到是 /Users/xxx/hello.go 这个文件的第 7 行出错了,并且这个错误发生在 main package 的 main 这个函数中。
方法¶
上面是在函数中 panic 了,下面看一下方法中 panic 的 strace 信息。
非指针实例的方法:
1 2 3 4 5 6 7 8 9 10 11 12 | package main type User struct{} func main() { var u User u.Name() } func (u User) Name() int { panic("error") } |
运行结果:
$ ./method
panic: error
goroutine 1 [running]:
main.User.Name(0x0)
/Users/xxx/method.go:11 +0x42
main.main()
/Users/xxx/method.go:7 +0x22
上方的 main.User.Name(0x0) 中的 0x0 表示的是方法的返回值,这里是 0 。
指针实例的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | $ cat method.go package main type User struct{} func main() { var u User u.Name() } func (u *User) Name() int { panic("error") } |
运行结果:
$ ./method
panic: error
goroutine 1 [running]:
main.(*User).Name(0xc000044788, 0x0)
/Users/xxx/method.go:11 +0x42
main.main()
/Users/xxx/method.go:7 +0x2b
上方的 main.(*User).Name(0xc000044788, 0x0) 中
- 0xc000044788 表示的是 u 的指针地址( &u )。
- 0x0 表示的是方法的返回值,这里是 0 。
参数¶
下面举例说明一下函数或方法的参数在 trace 中是如何展示的。
int/float/bool/array¶
int/float/bool/array 这些基本数据类型就是按本来的只显示的:
1 2 3 4 5 6 7 8 9 | package main func main() { test(true, 2, 2.2, [3]int{1, 2, 3}) } func test(b bool, n int, f float32, arr [3]int) { panic("test") } |
结果:
$ ./base
panic: test
goroutine 1 [running]:
main.test(0xc000044701, 0x2, 0x400ccccd, 0x1, 0x2, 0x3)
/Users/xxx/base.go:8 +0x39
main.main()
/Users/xxx/base.go:4 +0x55
结果解析:
main.test( 0xc000044701, // 一个常量,表示 true, false 是 0xc000044700 (至少在我的机器上是两个值) 0x2, // 2 0x400ccccd, // 2.2 的 16 进制表示 0x1, 0x2, 0x3 // [3]int{1, 2, 3} )
string¶
1 2 3 4 5 6 7 8 9 | package main func main() { test("test") } func test(s string) { panic("string") } |
结果:
$ ./string
panic: string
goroutine 1 [running]:
main.test(0x1074265, 0x4)
/Users/mg/tmp/test_go/strace/string.go:8 +0x39
main.main()
/Users/mg/tmp/test_go/strace/string.go:4 +0x36
为啥这里 main.test(0x1074265, 0x4) 显示的是两个参数呢,因为字符串是用两个参数来表示的: 第一个参数是指向字符串底层数组的指针地址(0x1074265),第二个参数是字符串的长度(0x4 表示长度为 4 )。
slice¶
slice 的数据用三个参数来表示:第一个是指向底层数组的指针,第二个是长度,第三个是容量:
goroutine 1 [running]:
main.test(0xc000044758, 0x3, 0x3)
/Users/xxx/slice.go:8 +0x39
main.main()
/Users/xxx/slice.go:4 +0x8c
map¶
map 的话就是一个指针地址表示:
goroutine 1 [running]:
main.test(0xc000044648)
/Users/xxx/map.go:10 +0x39
main.main()
/Users/xxx/map.go:4 +0x139
struct¶
会显示 struct 中所有字段的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package main func main() { u := User{ name: "Tom", age: 233, } test(u) } type User struct { name string age int } func test(u User) { panic("test") } |
结果:
$ ./struct
panic: test
goroutine 1 [running]:
main.test(0x1074474, 0x3, 0xe9)
/Users/xxx/struct.go:17 +0x39
main.main()
/Users/xxx/struct.go:8 +0x67
解析:
main.test( 0x1074474, 0x3, // name 字段的值 "Tom",0x1074474: 底层数组,0x3: 字符串长度 0xe9 // age 字段的值 233 的 16 进制表示 )
point¶
指针类型的话就显示一个指针地址:
$ ./point
panic: test
goroutine 1 [running]:
main.test(0xc000044770)
/Users/xxx/point.go:17 +0x39
main.main()
/Users/xxx/point.go:8 +0x5a
interface¶
interface 由两个参数表示,第一个是指向 interface 中存储类型信息的指针,第二个是指向真实数据的指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package main type namer interface { Name() string } func main() { var u namer = &User{ name: "Tom", age: 233, } test(u) } type User struct { name string age int } func (u *User) Name() string { return u.name } func test(n namer) { panic("test") } |
结果:
$ ./interface
panic: test
goroutine 1 [running]:
main.test(0x107c5c0, 0xc000044770)
/Users/xxx/interface.go:25 +0x39
main.main()
/Users/xxx/interface.go:12 +0x8c
// 0x107c5c0 包含类型信息的指针地址
// 0xc000044770 实际数据的指针地址
多个参数用一个值表示¶
当多个连续的参数可以用一个值表示的时候,trace 中就会用一个值来表示多个参数的值,比如
1 2 3 4 5 6 7 8 9 | package main func main() { test(1, 2, 3, 20, 30, 60) } func test(a, b, c, d, e, f uint8) { panic("test") } |
结果:
$ ./pack
panic: test
goroutine 1 [running]:
main.test(0x3c1e14030201)
/Users/mg/tmp/test_go/strace/pack.go:8 +0x39
main.main()
/Users/mg/tmp/test_go/strace/pack.go:4 +0x30
0x3c1e14030201 是由下面6个值组成的:
Bits Binary Hex Value 00-07 0000 0001 01 1 08-15 0000 0002 02 2 16-23 0000 0003 03 3 24-31 0001 0100 14 20 32-39 0001 1110 1e 30 40-47 0011 1100 3c 60
P.S. 等将来找到对应的源码后再补充更详细的合并规则 :joy
traceback 中显示的值个数¶
最多只能显示 10 个值 ,超过 10 个时会用 ... 代替:
1 2 3 4 5 6 7 8 9 | package main func main() { test([13]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}) } func test(arr [13]int) { panic("test") } |
$ ./max
panic: test
goroutine 1 [running]:
main.test(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...)
/Users/xxx/max.go:8 +0x39
main.main()
/Users/xxx/max.go:4 +0x4c
Comments