首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

golang panic堆栈日志解读

问题由来

以前出现panic问题,总是习惯通过日志中给出的代码行,去“猜测”是哪个变量出了问题,

如果推断不出来,就多加入一些日志,重现panic,再继续定位。

昨天又遇到了panic的问题,看到屏幕上打印了很多堆栈日志,转念一想:

如果现网出现了panic但日志信息不够怎么办,总不能先加日志等下次重现后再定位吧?

然后尝试仔细阅读堆栈日志时,却多出了一些疑惑:

日志解析

结论先行

panic日志中,打印出来的确实是输入参数的值;如果函数有返回值,则返回值也会打印;

但实际上是以字长word来打印的(word:操作系统处理信息的基本单位);

而每个参数占多少word,又和参数的类型有关(所以我们才会有参数个数对不上的疑惑)。

类型与字长

golang中基础类型如int、char、byte等的字长和C语言一致,不再展开,

下面列举常用的几个:

指针占一个word;

string占两个word (一个指向不可变字符数组的指针,一个string的长度);

切片占三个word (一个指向底层数组的指针,一个切片的长度,一个切片的容量)

接口占两个word(一个指向实际类型的指针,一个指向数据的指针)

更详细的可参考:https://research.swtch.com/godata

堆栈解析

有了上面的知识储备,本文开头提到的panic信息就能解释通了。

第一个参数slice []string,因为切片类型占3个word,所以:

slice := make([]string, 2, 4)

// 该切片的实际值

Pointer: 0xc000078f48

Length: 0x2

Capacity: 0x4

// 定义 func Fun1(slice []string, t *Test, i int)

// 堆栈 main.Fun1(0x2080c3f50, 0x2, 0x4, 0x0, 0x7)

第二个参数t *Test,因为指针占1个word,所以:

实际调用 Fun1(slice, nil, 7)

// 定义 func Fun1(slice []string,t *Test, i int)

// 堆栈 main.Fun1(0x2080c3f50, 0x2, 0x4,0x0, 0x7)

因此从这里也能看出传入的t是nil,这也是panic的所在;

第三个参数i int,因为int占1个word,所以:

//定义 func Fun1(slice []string, t *Test,i int)

//堆栈 main.Fun1(0x2080c3f50, 0x2, 0x4, 0x0,0x7)

函数有返回值

这里增加一个 有两个返回值的函数Fun2:

再看堆栈信息:

由上述第2行可以清晰看出,Fun2有两个返回值(int, error),

堆栈日志中main.Fun2就增加了0x1056c1d, 0xc000078f88, 0x1004c30三个值,其中:

int占一个word,对应0x1056c1d;

error是interface,占两个word,对应0xc000078f88, 0x1004c30;

函数到方法

将上述的Fun2改成如下方式并调用:

修改前后堆栈日志对比:

由上可知,两者唯一差别是,func (m *M) Fun2 堆栈的第一个是 m的地址,其他的和 func Func2 一致。

引申问题

参数个数限制

进一步发现堆栈中的Fun2最多只能有10个参数,当有更多时候,会用...省略掉:

参数packing

前面说了,堆栈参数是以word为单位来打印的,那如果参数不足word长度呢,如bool,char等?还是很有趣的,请看下文:

Fun1的堆栈日志:

很明显:b1、b2、b3、c占用了一个word,对应0x19010001,这就是参数packing;

因此可知:堆栈参数中的高位对应着右边的参数,地位对应着左边的参数。

喜欢的话,关注我的公众号哦

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200111A0OX8O00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券