Go语言的堆栈分析

本文为理解翻译,原文地址:http://www.goinggo.net/2015/01/stack-traces-in-go.html

Introduction

在Go语言中有一些调试技巧能帮助我们快速找到问题,有时候你想尽可能多的记录异常但仍觉得不够,搞清楚堆栈的意义有助于定位Bug或者记录更完整的信息。

本文将讨论堆栈跟踪信息以及如何在堆栈中识别函数所传递的参数。

Functions
先从这段代码开始:

Listing 1

01 package main
02
03 func main() {
04     slice := make([]string, 2, 4)
05     Example(slice, "hello", 10)
06 }
07
08 func Example(slice []string, str string, i int) {
09     panic("Want stack trace")
10 }

Example函数定义了3个参数,1个string类型的slice, 1个string和1个integer, 并且抛出了panic,运行这段代码可以看到这样的结果:

Listing 2

Panic: Want stack trace

goroutine 1 [running]:
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
        /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
        temp/main.go:9 +0x64
main.main()
        /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
        temp/main.go:5 +0x85

goroutine 2 [runnable]:
runtime.forcegchelper()
        /Users/bill/go/src/runtime/proc.go:90
runtime.goexit()
        /Users/bill/go/src/runtime/asm_amd64.s:2232 +0x1

goroutine 3 [runnable]:
runtime.bgsweep()
        /Users/bill/go/src/runtime/mgc0.go:82
runtime.goexit()
        /Users/bill/go/src/runtime/asm_amd64.s:2232 +0x1

堆栈信息中显示了在panic抛出这个时间所有的goroutines状态,发生的panic的goroutine会显示在最上面。

Listing 3

01 goroutine 1 [running]:
02 main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)
           /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
           temp/main.go:9 +0x64
03 main.main()
           /Users/bill/Spaces/Go/Projects/src/github.com/goinaction/code/
           temp/main.go:5 +0x85

第1行显示最先发出panic的是goroutine 1, 第二行显示panic位于main.Example中, 并能定位到该行代码,在本例中第9行引发了panic。

下面我们关注参数是如何传递的:

Listing 4

// Declaration
main.Example(slice []string, str string, i int)

// Call to Example by main.
slice := make([]string, 2, 4)
Example(slice, "hello", 10)

// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

这里展示了在main中带参数调用Example函数时的堆栈信息,比较就能发现两者的参数数量并不相同,Example定义了3个参数,堆栈中显示了6个参数。现在的关键问题是我们要弄清楚它们是如何匹配的。

第1个参数是string类型的slice,我们知道在Go语言中slice是引用类型,即slice变量结构会包含三个部分:指针、长度(Lengthe)、容量(Capacity)

Listing 5

// Slice parameter value
slice := make([]string, 2, 4)

// Slice header values
Pointer:  0x2080c3f50
Length:   0x2
Capacity: 0x4

// Declaration
main.Example(slice []string, str string, i int)

// Stack trace
main.Example(0x2080c3f50, 0x2, 0x4, 0x425c0, 0x5, 0xa)

因此,前面3个参数会匹配slice, 如下图所示:

Figure 1

figure provided by Georgi Knox

我们现在来看第二个参数,它是string类型,string类型也是引用类型,它包括两部分:指针、长度。

Listing 6

// 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)

可以确定,堆栈信息中第4、5两个参数对应代码中的string参数,如下图所示:

Figure 2

figure provided by Georgi Knox

最后一个参数integer是single word值。

Listing 7

// 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)

现在我们可以匹配代码中的参数到堆栈信息了。

Figure 3

figure provided by Georgi Knox

Methods

如果我们将Example作为结构体的方法会怎么样呢?

Listing 8

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 }

如上所示修改代码,将Example定义为trace的方法,并通过trace的实例t来调用Example。

再次运行程序,会发现堆栈信息有一点不同:

Listing 9

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

首先注意第2行的方法调用使用了pointer receiver,在package名字和方法名之间多出了"*trace"字样。另外,参数列表的第1个参数标明了结构体(t)地址。我们从堆栈信息中看到了内部实现细节。

Packing

如果有多个参数可以填充到一个single word, 则这些参数值会合并打包:

Listing 10

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函数为4个参数:3个bool型和1个八位无符号整型。bool值也是用8个bit表示,所以在32位和64位架构下,4个参数可以合并为一个single word。

Listing 11

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

这是本例的堆栈信息,看下图的具体分析:

Listing 12

// 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)

以上展示了参数值是如何匹配到4个参数的。当我们看到堆栈信息中包括十六进制值,需要知道这些值是如何传递的。

Conclusion
The Go runtime provides a great deal of information to help us debug our programs. In this post we concentrated on stack traces. The ability to decode the values that were passed into each function throughout the call stack is huge. It has helped me more than once to identify my bug very quickly. Now that you know how to read stack traces, hopefully you can leverage this knowledge the next time a stack trace happens to you.

时间: 2024-12-23 01:03:52

Go语言的堆栈分析的相关文章

Java语言特点及分析

Java语言特点及分析 Java是一门面向对象编程语言. (Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程.) Java具有简单性.面向对象.分布性.健壮性.安全性.平台独立与可移植性.多线程.动态性等特点 .Java可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序等. Java语言的这些特点体现了java的如下特性: 能够快速入门 编写的程序代码较少 编写较好的程序代码 更快速的开发程序 利用百分之百纯Java

[转]易语言消息机制分析(消息拦截原理)

标 题: [原创]易语言消息机制分析(消息拦截原理)作 者: 红绡枫叶时 间: 2014-12-17,12:41:44链 接: http://bbs.pediy.com/showthread.php?t=195626 我自己做了个易语言的sig签名,方便分析的时候用.易语言例子是静态编译的.版本 5.11易语言其实是基于mfc的,它依然需要mfc的消息派发机制,只不过,自己当了系统与用户间的代理人.所有的消息都要经它转发而已.我在MFC的消息派发函数_AfxDispatchCmdMsg下断点,总

Go将统治下一个10年?Go语言发展现状分析

"本文是国内Go语言大中华区首席布道师--许式伟,在QCon2015上海站上的分享.他预测Go语言10年内一定会超过C和java,并且统治这一个10年. Go语言语法及标准库变化 Go从1.0版本到现在(2015年)已经有三年多的时间,大的版本发布了五个,下面大家一起看看每个大版本分别都改了什么,当然这里不可能把所有的细节都提到,但我认为重要的会提出来. 首先是Go1.1,Go1.0于2012年4月发布,此后基本维持了每半年发布一个新版本的时间间隔.Go1.5比较例外,在Go1.5的拖累下,Go

go语言,简单分析和了解

Go 本地化 该指南也有其他语言版本: Brazilian Portuguese — Português do Brasil Catalan — Català Spanish — Español French - Français Hebrew — ???????? Japanese — 日本語 Korean — ??? Romanian - Român? Chinese — 普通话 Go Playground 这个指南构建在 Go Playground 之上,这是一个运行在 golang.or

使用Tcmalloc进行堆栈分析

在前一篇译文<使用TCmalloc的堆栈检查>,介绍了Tcmalloc进行堆栈检查,今天翻译<heap-profiling using tcmalloc>,了解如何 TCmalloc进行堆栈分析. 一.堆栈分析的用法: 这篇技术文档描述了如何使用C++程序来分析堆栈.可以用来做一下三条事情: 在任何时间了解程序的堆栈情况 定位内存泄漏 找到大量内存分配的位置 1.链接堆栈分析器 你可以对任何链接了tcmalloc的程序进行堆栈分析,并且不需要重新编译. 把tcmalloc链接到你的

R语言重要数据集分析研究——需要整理分析阐明理念

1.R语言重要数据集分析研究需要整理分析阐明理念? 上一节讲了R语言作图,本节来讲讲当你拿到一个数据集的时候如何下手分析,数据分析的第一步,探索性数据分析. 统计量,即统计学里面关注的数据集的几个指标,常用的如下:最小值,最大值,四分位数,均值,中位数,众数,方差,标准差,极差,偏度,峰度 先来解释一下各个量得含义,浅显就不说了,这里主要说一下不常见的 众数:出现次数最多的 方差:每个样本值与均值的差得平方和的平均数 标准差:又称均方差,是方差的二次方根,用来衡量一个数据集的集中性 极差:最大值

03-第一个C语言程序的分析

一.代码分析 二.开发和运行C程序的步骤 三.总结 说明:这个C语言专题,是学习iOS开发的前奏.也为了让有面向对象语言开发经验的程序员,能够快速上手C语言.如果你还没有编程经验,或者对C语言.iOS开发不感兴趣,请忽略 在上一篇中我们已经创建了一个C程序,接下来分析一下里面的代码. 项目结构如下: 回到顶部 一.代码分析 打开项目中的main.c文件(C程序的源文件拓展名为.c),可以发现它是第一个C程序中的唯一一个源文件,代码如下: 1 #include <stdio.h> 2 3 int

java 堆栈分析

再次,研究了一个下午的jhat好jmap.从一开始惊呆.懵懂于那样大量而无聊乏味的数据,到现在有那么一点点收货.赶紧记录下来.没办法,悟性太低... C:\Users\Administrator>jps 11896 12528 Jps C:\Users\Administrator>jps 11896 19016 Jps 5060 HttpServer C:\Users\Administrator>jmap -dump:file=testJmapa 5060 Dumping heap to

java 堆栈分析4

jprofiler ,又是一款好工具... —— 不过显然,我觉得有了jvisualvm就足够了,难道它会比jvisualvm还强大很多!?? 什么时候需要它呢?它有什么特别好用的地方吗? 带来什么方便之处吗? http://www.cnblogs.com/langtianya/p/3172180.html java 堆栈分析4