译 | Stack Traces In Go(下)

简介: 译 | Stack Traces In Go(下)

现在让我们看看第二个参数,它是一个字符串。字符串也是引用类型,但此它的header value是不可变的。字符串的header value声明为三个word大小的结构,包含指向底层字节数组的指针和字符串的长度:

// String parameter value
"hello"
// String header values
Pointer: 0x425c0
Length:  0x5
// Declaration
main.Example(slice []string, `str string`, i int)
// Stack trace
main.Example(0x2080c3f50, 0x2, `0x4, 0x425c0`, 0x5, 0xa)

上述显示了stack trace中的第四、五个值如何匹配string参数。第四个值表示指向底层字节数组的指针,第五个值表示字符串的长度为5.字符串“hello”需要5个字节。这两个值表示字符串(即,第二个参数)header的每个值。

第三个参数是一个整数,它是一个word的值:

// Integer parameter value
10
// Integer value
Base 16: 0xa
// Declaration
main.Example(slice []string, str string, `i int`)
// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, `0xa`)

上述显示了stack trace中的最后一个值如何匹配整数参数。trace中的最后一个值是十六进制数0xa,它是值10.与该参数传递的值相同。该值代表第三个参数。

Methods

我们更改程序,将Example函数改为一个成员函数:

01 package main
02
03 import "fmt"
04
05 type trace struct{}
06
07 func main() {
08     slice := make([]string, 2, 4)
09
10     var t trace
11     t.Example(slice, "hello", 10)
12 }
13
14 func (t *trace) Example(slice []string, str string, i int) {
15     fmt.Printf("Receiver Address: %p\n", t)
16     panic("Want stack trace")
17 }

第5行通过声明一个新类型trace,改变原始程序,转换Example函数为的第14行的成员函数。转换是通过重新声明函数包含一个trace类型的指针接收器来实现。然后在第10行,声明一个trace类型的t变量,并在第11行使用该变量进行方法调用。

由于该方法是使用指针接收器声明的,因此Go将获取t变量的地址以支持接收器类型,即使方法调用使用的是值。这次运行程序时,stack trace略有不同

Receiver Address: `0x1553a8`
panic: Want stack trace
01 goroutine 1 [running]:
02 main.(`*trace`).Example(`0x1553a8`, 0x2081b7f50, 0x2, 0x4, 0xdc1d0, 0x5, 0xa)
           /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
           temp/main.go:16 +0x116
03 main.main()
           /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
           temp/main.go:11 +0xae

首先,应该注意的是第02行的stack trace清楚表明这是一个使用指针接收器的方法调用。现在,包含(* trace)的成员函数的名称显示在包名称和方法名称之间。其次,要注意的是值列表现在如何第一个显示接收器的值。成员函数调用实际上是第一个参数是接收器值的函数调用。我们从stack trace中看到了这个实现细节。

由于声明或调用Example方法没有其他任何更改,因此所有其他值保持不变。调用Example的行号和发生panic的位置发生了变化,并反映了新代码。

Packing

如果有多个参数适合放入单个word,那么stack trace中参数的值将打包在一起:

01 package main
02
03 func main() {
04     Example(true, false, true, 25)
05 }
06
07 func Example(b1, b2, b3 bool, i uint8) {
08     panic("Want stack trace")
09 }

上述显示了一个新的示例程序,将Example函数更改为接受四个参数。前三个是布尔值,最后一个是八位无符号整数。布尔值也是一个8位值,因此所有四个参数都适合放入32位和64位架构上的单个word。当程序运行时,它会产生一个有趣的stack trace:

01 goroutine 1 [running]:
02 main.Example(`0x19010001`)
           /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
           temp/main.go:8 +0x64
03 main.main()
           /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
           temp/main.go:4 +0x32

对Example的调用,stack trace中没有四个值,取而代之只有一个值。所有四个独立的8位值都拼凑成一个word:

// Parameter values
true, false, true, 25
// Word value
Bits    Binary      Hex   Value
00-07   0000 0001   `01`    true
08-15   0000 0000   `00`    false
16-23   0000 0001   `01`    true
24-31   0001 1001   `19`    25
// Declaration
main.Example(`b1, b2, b3 bool, i uint8`)
// Stack trace
main.Example(`0x19010001`)

上述显示出了在stack trace的值如何匹配传入的四个参数值。true的8位值对应于1的值,false的值对应0值。二进制25的值是11001,十六进制转换为19。现在,当我们查看堆stack trace中表示的十六进制值时,我们会看到它如何表示传入的值。

结论

Go runtime提供了大量信息来帮助我们调试程序。在这篇文章中,我们专注于stack trace。解码在整个调用堆栈中传递给每个函数的值的能力是巨大的。它不止一次帮助我快速识别我的bug。既然您已经知道如何读取stack trace,那么希望您可以在下次发生stack trace时可以利用这些知识。

原文:stack-traces-in-go

本文作者 : cyningsun

本文地址https://www.cyningsun.com/07-05-2019/live-stack-traces-in-go-cn.html

版权声明 :本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0 CN 许可协议。转载请注明出处!

# Golang

  1. 译|There Are No Reference Types in Go
  2. Go 语言没有引用类型,指针也与众不同
  3. 译|What “accept interfaces, return structs” means in Go
  4. 如何用好 Go interface
  5. 一个优雅的 LRU 缓存实现

目录
相关文章
|
程序员 Go
译 | Stack Traces In Go(上)
译 | Stack Traces In Go
54 0
|
13天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
54 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
1月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
41 7
|
1月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
1月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
103 71
|
1月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
106 67
|
1月前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
104 62
|
8天前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
40 12
|
1月前
|
存储 Go
go语言中映射
go语言中映射
38 11
|
1月前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
39 12