Golang程序的调试工具包括gdb调试、go pprof性能调试工具及go gc分析工具。熟练掌握这些工具的基本用法对golang的程序开发及调试分析拥有很大的帮助。
gdb可以用来作为golang的调试工具。
1
2
3
4
5
6
7
8
9
10
11
12
| #编译时,打开相关编译变量, -gcflags是给go编译器的参数,gc是go compile的意思。-N是不要优化代码,-l 是禁止内联代码。
$ go build -gcflags "-N -l" test.go
# 运行gdb
$ gdb test
(gdb) info files #查看文件
(gdb) l main.main # list
(gdb) b 10 # breakpoint 10,第10行设置断点
(gdb) r # run
(gdb) s # step, 单
(gdb) p *b # print *b
(gdb) n # next
|
gdb对golang的调试功能支持不完善,delve
pprof是go tool自带的性能调试工具,看用于对pprof采样数据进行分析。
要使用pprof,需要先生成采样数据,有两种使用方式可以产生pprof数据:
通过引入runtime/pprof
包,并手动调用rutime.StartCPUProfile, runtimeStopCPUProfile
等API来获取采样数据;
通过引入import _ "net/http/prprof"
方式在线使用;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| import (
// 引入net/http/pprof包,该包自动注册 handler到 http server
_ "net/http/pprof" //
)
func main() {
runtime.GOMAXPROCS(1) // 限制 CPU 使用数,避免过载
runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪
runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪
go func() {
// 启动一个 http server,以提供pprof http服务端口,服务默认在/debug/pprof下
if err := http.ListenAndServe(":6060", nil); err != nil {
log.Fatal(err)
}
os.Exit(0)
}()
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # 浏览器
$ curl 'http://127.0.0.1:6060/debug/pprof/goroutine' > /tmp/goroutine.dbg
$ go tool pprof -http=":8081" /tmp/goroutine.dbg
# 函数调用cpu耗时
$ go tool pprof http://localhost:6060/debug/pprof/profile
# 内存
$ go tool pprof -sample_index=alloc_space "http://localhost:6060/debug/pprof/heap?gc=1&seconds=60"
# 已分配的堆内存
$ go tool pprof http://localhost:6060/debug/pprof/allocs
# goroutine
$ go tool pprof http://localhost:6060/debug/pprof/goroutine
$ curl 'http://localhost:6060/debug/goroutine?debug=1' > ~/tmp/gopprof.txt
# 阻塞
$ go tool pprof http://localhost:6060/debug/pprof/block
# 锁
$ go tool pprof http://localhost:6060/debug/pprof/mutex
(pprof) top # 查看top 前的指标
(pprof) list <> # 查看指标对象所在源码,需设置源码目录为编译时目录
(pprof) web --nodefraction=0.1 [metanode.NewInode] # 生成svg,在浏览其中图形化展示指标
(pprof) traces #
|
1
2
3
| $ curl 'http://localhost:6060/debug/pprof/trace?seconds=5' > ~/tmp/gotrace.out
$ go tool trace -http=":8081" ~/tmp/gotrace.out
$ go tool trace ~/tmp/gotrace.out
|
GODEBUG 开启 debug 模式后,可做内存 trace 和调度器的 trace
GODEBUG 还支持设置以下变量:
- GOGC: 改变堆增长方式 —— 设置初始的 GC 目标百分比。当新分配内存,与上一次采集后剩余的实时数据的比例达到这个百分比时,才会触发一次 GC。默认值是 GOGC=100。设置
GOGC=off
则完全禁用垃圾收集器。 - schedtrace:设置
schedtrace=X
,每 X 毫秒打印一次调度器状态 —— 包括调度器、处理器、线程和 goroutine
1
2
3
4
5
6
7
8
9
10
11
12
| # 输出gc
$ GODEBUG=gctrace=1 go run example.go
$ GODEBUG=gctrace=1 ./go-pprof-practice | grep gc
# 手动触发gc
$ curl -X GET "http://localhost:6060/debug/pprof/heap?gc=1"
# 查看调度
$ GODEBUG=schedtrace=1000 ./awesomeProject
# 查看调度详情
$ GODEBUG=scheddetail=1,schedtrace=1000 ./awesomeProject
|
1
| gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
|
gc#
:GC 执行次数的编号,每次叠加。@#s
:自程序启动后到当前的具体秒数。#%
:自程序启动以来在GC中花费的时间百分比。#+...+#
:GC 的标记工作共使用的 CPU 时间占总 CPU 时间的百分比。#->#-># MB
:分别表示 GC 启动时, GC 结束时, GC 活动时的堆大小.#MB goal
:下一次触发 GC 的内存占用阈值。#P
:当前使用的处理器 P 的数量。
golang调度器追踪
1
2
3
4
5
6
7
| $ GOMAXPROCS=2 GODEBUG=schedtrace=1000 ./example
SCHED 0ms: gomaxprocs=2 idleprocs=1 threads=2 spinningthreads=0 idlethreads=0 runqueue=0 [0 0]
SCHED 1002ms: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=1 idlethreads=1 runqueue=0 [0 4]
SCHED 2002ms: gomaxprocs=2 idleprocs=0 threads=4 spinningthreads=0 idlethreads=1 runqueue=0 [4 4]
$ GOMAXPROCS=2 GODEBUG=schedtrace=1000,scheddetail=1 ./example
|
第2秒:
1
2
3
4
5
6
7
| 2002ms : This is the trace for the 2 second mark.
gomaxprocs=2 : 2 processors are configured for this program.
threads=4 : 4 threads exist. 2 for processors and 2 for the runtime.
idlethreads=1 : 1 idle thread (3 threads running).
idleprocs=0 : 0 processors are idle (2 processors busy).
runqueue=0 : All runnable goroutines have been moved to a local run queue.
[4 4] : 4 goroutines are waiting inside each local run queue.
|
输出项 | 意义 |
---|
1009ms | 自从程序开始的毫秒数 |
gomaxprocs=1 | 配置的处理器数(逻辑的processor,也就是Go模型中的P,会通过操作系统的线程绑定到一个物理处理器上) |
threads=3 | 运行期管理的线程数,目前三个线程 |
idlethreads=1 | 空闲的线程数,当前一个线程空闲,两个忙 |
idleprocs=0 | 空闲的处理器数,当前0个空闲 |
runqueue=0 | 在全局的run队列中的goroutine数,目前所有的goroutine都被移动到本地run队列 |
[9] | 本地run队列中的goroutine数,目前9个goroutine在本地run队列中等待 |
golang程序panic后,会打印出panic时的内存堆栈信息以便于问题 分析,输出如下:
1
2
3
4
5
6
7
8
9
| 1 panic: runtime error: invalid memory address or nil pointer dereference
2 [signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x751ba4]
3 goroutine 58 [running]:
4 github.com/joeshaw/example.UpdateResponse(0xad3c60, 0xc420257300, 0xc4201f4200, 0x16, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
5 /go/src/github.com/joeshaw/example/resp.go:108 +0x144
6 github.com/joeshaw/example.PrefetchLoop(0xacfd60, 0xc420395480, 0x13a52453c000, 0xad3c60, 0xc420257300)
7 /go/src/github.com/joeshaw/example/resp.go:82 +0xc00
8 created by main.runServer
9 /go/src/github.com/joeshaw/example/cmd/server/server.go:100 +0x7e0
|
stack track函数参数遵守如下规则:
每个参数 按函数原型参数列表从左到右按内存布局按word逐一展开,不是和原型参数个数一一对应;
如果是method,receiver为最左边开始 展开;
返回值在参数展开后展开,多返回值也按左到右顺序逐一展开;
内建类型(int, rune,byte)按word逐个输出,不足一个word的 ,将合并成一个word;
指针类型:输出指针地址;
string类型:输出两个:指针地址,string长度;
slice:输出三个 :地址, 长度,容量;
struct:按stuct字段顺序逐个展开;
interface: 2个: 类型,数据指针;
参数都未被使用或者只是在 fmt.Print()
中未作修改使用,用func(...)
代替,内联的函数也只显示... ;
golang stack track 中函数调用各种类型参数的对应的数量:
类型名称 | 参数域数量 | 参数域说明 |
---|
string | 2 | 指针 长度 |
slice | 3 | 指针 长度 容量 |
map | 1 | 指针 |
chan | 1 | 指针 |
interface | 2 | 类型指针 值指针 |
pointer | 1 | 指针 |
func | 1 | 指针 |
nil | 1 | 0x0 |
1
| $ sudo strace -Tfp <PID>
|
https://guidao.github.io/go_debug.html
https://cizixs.com/2017/09/11/profiling-golang-program/
golang pprof 实战 | Wolfogre's Blog
https://segmentfault.com/a/1190000020255157
https://mp.weixin.qq.com/s/Brby6D7d1szUIBjcD_8kfg
GitHub - go-delve/delve: Delve is a debugger for the Go programming language.
Debugging Go Code with GDB - The Go Programming Language
Go函数调用链跟踪的一种实现思路 | Tony Bai
记一次go panic问题的解决过程 | Tony Bai
https://www.orztu.com/post/golang-trace/
Go 语言的 Stack Trace - Go语言中文网 - Golang中文社区
https://segmentfault.com/a/1190000040612732
Go 调度器跟踪
https://segmentfault.com/a/1190000019736288
pprof/README.md at master · google/pprof · GitHub
golang中定时器cpu使用率高的现象详析_Golang_脚本之家
https://swsmile.info/post/golang-trace/
Golang 大杀器之跟踪剖析 trace - 掘金
https://zhuanlan.zhihu.com/p/95056679
Golang 性能测试 (3) 跟踪刨析 golang trace - 搬砖程序员带你飞 - 博客园
6. strace 跟踪进程中的系统调用 — Linux Tools Quick Tutorial
[译]strace的10个命令