[golang]如何看懂调用堆栈

栏目: Go · 发布时间: 5年前

内容简介:之前也有方括号里的select表示阻塞原因,具体定义见后面的时间是阻塞时间。需要注意的是这只是一个大概时间,是在

之前也有 文章 讲过 go 调用堆栈的话题,但并没有完全讲清楚,这里补充里面缺漏的几个点。

阻塞

goroutine 1 [select, 2 minutes]:

方括号里的select表示阻塞原因,具体定义见 runtime.waitReason

后面的时间是阻塞时间。需要注意的是这只是一个大概时间,是在 gc的过程中标记的 ,所以如果这个goroutine不需要gc,那么永远也不会有值。

PC偏移

github.com/robfig/cron.(*Cron).run(0xc0000c44b0)
    cron.go:191 +0x28d

行号后面的16进制数是什么?pc偏移。是指函数入口(cron.(*Cron).run)到调用处(也就是行号指位置)的指令位置偏差。一般很少用到,除非下面这种特殊情况:

func main() {
    rand.Seed(time.Now().UnixNano())
    _, _, _ = foo(), foo(), foo()
}

func foo() int {
    if rand.NormFloat64() > 0 {
        panic("")
    }
    return 0
}

//main.main()
//  main.go:10 +0x7b 或 +0x80 或 +0x85

函数参数

函数参数是最复杂的部分,牵涉到go的很多底层实现。

输入参数,输出参数

为什么经常只有一个参数的函数堆栈里却跟着两个数呢?另一个是输出。

func main() {
    rand.Seed(time.Now().UnixNano())
    r := rand.Int() //2e78e7b163438cc2
    fmt.Printf("%x\n", r)
    foo(r)
}

func foo(i int) (o int) {
    o = rand.Int() //36dd26e720cac1fe
    fmt.Printf("%x\n", o)
    defer panic("want to panic")
    return
}

//main.foo(2e78e7b163438cc2, 36dd26e720cac1fe)
//  main.go:21 +0xdd

结构体展开

结构体会被展开,然后比较短的字段会被打包成一个uint。

type S struct {
    a int
    b int
    c int
    d int
    a1 bool
    b1 byte
    c1 bool
    d1 byte
}

func main() {
    foo(S{})
}

func foo(s S) {
    panic("want to panic")
    noInline()
}

func noInline() {
    fmt.Sprint()
}

//main.foo(0x0, 0x0, 0x0, 0x0, 0x0)
//  main.go:67 +0x39

但是这种打包是依赖字段顺序的。

type S struct {
    a int
    a1 bool
    b int
    b1 bool
    c int
    c1 bool
    d int
    d1 bool
}

func main() {
    foo(S{})
}

func foo(s S) {
    panic("want to panic")
    noInline()
}

func noInline() {
    fmt.Sprint()
}

//main.foo(0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
//  main.go:67 +0x39

函数参数对照表

下面的表涵盖了大部分情况

函数签名 输入 堆栈输出 说明
foo(float64) 1 (0x3ff0000000000000)
foo(complex64) 1+3i (0x404000003f800000) 两个32位浮点打包为一个64位数
foo(complex128) 1+3i (0x3ff0000000000000, 0x4008000000000000)
foo(string) "中文" (0x10adc82, 0x6) (指针,字节数)
foo(interface{}) "" (0x108e520, 0x10bff30) (类型指针,值指针)
foo(interface{}) (*string)(nil) (0x108b8e0, 0x0) 值为nil,类型不为nil
foo(interface{}) nil (0x0, 0x0) 值,类型都为nil
foo([]byte) make([]byte,3,6) (0xc000070f82, 0x3, 0x6) (指针,len,cap)
foo(map[string]string) make(map[string]string) (0xc000070e48)
foo(chan struct{}) make(chan struct{}) (0xc00005e060)
foo([200]int) [200]int{} (0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...) 数组展开
foo(func()) nil (0x0)
foo(int16, uint16) 1,2 (0xc000020001) 两个16位数打包为32位数,仅低32位有效
foo(bool)bool false (0xc000070f00, 0xc00005c058) 前一个数为输入,仅低8位有效,后一个数为输出,未初始化
foo(struct {a,b int}) struct {a,b int}{} (0x0, 0x0) 结构体展开
foo(struct {a int;b bool;c int;d bool;e byte}) struct {a int;b bool;c byte;d bool;e byte}{1,true,2,false,3} (0x1, 0x1, 0x2, 0x300) d,e合并,b不合并
foo(struct {a int;b bool;c string;d bool;e byte}) struct {a int;b bool;c string;d bool;e byte}{1,true,"a",false,3} (0x1, 0x1, 0x10adb18, 0x1, 0x300) d,e合并
foo(struct {a struct{a,b byte};b bool}) struct {a struct{a,b byte};b bool}{struct{a,b byte}{3,4},true} (0xc000010403) 内嵌结构体合并
foo(struct {a struct{a int;b byte};b bool}) struct {a struct{a int;b byte};b bool}{struct {a int;b byte}{5 , 7 },false} (0x5, 0xc000072f07, 0xc00005e000) 内嵌结构体不合并

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Writing Windows VxDs and Device Drivers, Second Edition

Writing Windows VxDs and Device Drivers, Second Edition

Karen Hazzah / CMP / 1996-01-12 / USD 54.95

Software developer and author Karen Hazzah expands her original treatise on device drivers in the second edition of "Writing Windows VxDs and Device Drivers." The book and companion disk include the a......一起来看看 《Writing Windows VxDs and Device Drivers, Second Edition》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具