在try 学习如何从死机中调试堆栈跟踪时,我遇到了一些令人困惑的事情.

package main

func F(a int) {
    panic(nil)
}

func main() {
    F(1)
}

当我在附加的播放链接上运行它时,输出以下内容:

panic: nil

goroutine 1 [running]:
main.F(0x1, 0x10436000)
    /tmp/sandbox090887108/main.go:4 +0x20
main.main()
    /tmp/sandbox090887108/main.go:8 +0x20

我无法破译第二个数字(main.F(0x1,0x10436000)中的0x10436000).如果有第二个int参数,或者如果作为第一个参数传入了任何其他参数(可以在第二个播放链接中看到),它就不会出现.

1参数:https://play.golang.org/p/3iV48xlNFR

两个参数:https://play.golang.org/p/4jA7ueI86K

推荐答案

堆栈跟踪中打印的数据是参数,但是值并不直接对应于传入的参数,而是以指针大小的值打印的原始数据(尽管这通常与本机字大小相同).playground 略显独特,因为它是一个64位字体系 struct ,带有32位指针(GOARCH=amd64p32).

traceback.go中,您可以看到通过基于指针大小单步执行参数来打印值;

for i := uintptr(0); i < frame.arglen/sys.PtrSize; i++ {

因此,因为单词大小是操场上指针大小的两倍,所以在框架参数中总是打印偶数个值.

通过在函数参数中使用较小的类型,可以在操场上看到数据表示方式的另一个示例:https://play.golang.org/p/vHZDEHQZLh

func F(a uint8) {
    panic(nil)
}

// F(1)
// main.F(0x97301, 0x10436000)

只使用64位字的前8位,即0x97301 & 0x0f,或简称为1.来自第一个值的额外0x97300和整个0x10436000只是该函数未使用的第一个64位字的剩余部分.

通过使用更多参数,您可以在amd64个系统上看到相同的行为.例如,该签名;

func F(a, b, c uint32)

调用VIA F(1, 1, 1)时,它将打印堆栈跟踪,如下所示:

main.F(0x100000001, 0xc400000001)

因为3个32位的值需要2个字

需要注意的最后一组值是返回值,这些返回值也是在堆栈上分配的.以下函数签名:

func F(a int64) (int, int)

在AMD64上,将显示堆栈帧参数,如下所示:

main.F(0xa, 0x1054d60, 0xc420078058)

其中一个单词代表a,另外两个单词代表(int, int)个返回值.然而,返回值并未初始化,因此除了理解这些值在那里的原因之外,我在这里没有获得什么收获.

Go相关问答推荐

有没有更简单的方法在Go中编写这个逻辑?

如何在另一个文件夹中使用Delve运行二进制文件?

CURL和Postman HTTP POST工作,但Golang请求失败,状态为400

使用!NOT运算符的Golang文本/模板多个条件

我怎样才能改进这个嵌套逻辑以使其正常工作并提高性能

Go 中将 int 切片转换为自定义 int 切片指针类型的函数

go-jwt 令牌验证错误 - 令牌签名无效:密钥类型无效

Go 中的 YAML 自定义标签

为什么docall在singleflight中使用go panic?

GitHub 中 REST API 的 aws 凭证问题

获取 nil 指针类型的 reflect.Value

将 struct 转换为 CSV 字符串

访问传递给可变参数函数的通用 struct 的特定字段

Golang - 客户 Unmarshaler/Marshaler 在指针上具有 nil/null 值

无法访问 Go 模块导入的远程存储库

为什么 0 big.Int 的 .Bytes() 值是空切片?

如何在Go中替换符号并使下一个字母大写

如何在 Unmarshal 中使用泛型(转到 1.18)

如何在 Gorm 中获得特定日期的最大值?

为什么在 unsafe.Sizeof() 中取消引用 nil 指针不会导致panic ?