Golang 大杀器之跟踪剖析 trace

image

在 Go 中有许许多多的分析工具,在以前我有写过一篇 《Golang 大杀器之性能剖析 PProf》 来介绍 PProf,若是有小伙伴感兴趣能够去我博客看看。html

但单单使用 PProf 有时候不必定足够完整,由于在真实的程序中还包含许多的隐藏动做,例如 Goroutine 在执行时会作哪些操做?执行/阻塞了多长时间?在何时阻止?在哪里被阻止的?谁又锁/解锁了它们?GC 是怎么影响到 Goroutine 的执行的?这些东西用 PProf 是很难分析出来的,但若是你又想知道上述的答案的话,你能够用本文的主角 go tool trace 来打开新世界的大门。目录以下:golang

image

初步了解

import (
	"os"
	"runtime/trace"
)

func main() {
	trace.Start(os.Stderr)
	defer trace.Stop()

	ch := make(chan string)
	go func() {
		ch <- "EDDYCJY"
	}()

	<-ch
}
复制代码

生成跟踪文件:bash

$ go run main.go 2> trace.out
复制代码

启动可视化界面:网络

$ go tool trace trace.out
2019/06/22 16:14:52 Parsing trace...
2019/06/22 16:14:52 Splitting trace...
2019/06/22 16:14:52 Opening browser. Trace viewer is listening on http://127.0.0.1:57321
复制代码

查看可视化界面:闭包

image

  • View trace:查看跟踪
  • Goroutine analysis:Goroutine 分析
  • Network blocking profile:网络阻塞概况
  • Synchronization blocking profile:同步阻塞概况
  • Syscall blocking profile:系统调用阻塞概况
  • Scheduler latency profile:调度延迟概况
  • User defined tasks:用户自定义任务
  • User defined regions:用户自定义区域
  • Minimum mutator utilization:最低 Mutator 利用率

Scheduler latency profile

在刚开始查看问题时,除非是很明显的现象,不然不该该一开始就陷入细节,所以咱们通常先查看 “Scheduler latency profile”,咱们能经过 Graph 看到总体的调用开销状况,以下:curl

image

演示程序比较简单,所以这里就两块,一个是 trace 自己,另一个是 channel 的收发。函数

Goroutine analysis

第二步看 “Goroutine analysis”,咱们能经过这个功能看到整个运行过程当中,每一个函数块有多少个有 Goroutine 在跑,而且观察每一个的 Goroutine 的运行开销都花费在哪一个阶段。以下:工具

image

经过上图咱们能够看到共有 3 个 goroutine,分别是 runtime.mainruntime/trace.Start.func1main.main.func1,那么它都作了些什么事呢,接下来咱们能够经过点击具体细项去观察。以下:性能

image

同时也能够看到当前 Goroutine 在整个调用耗时中的占比,以及 GC 清扫和 GC 暂停等待的一些开销。若是你以为还不够,能够把图表下载下来分析,至关于把整个 Goroutine 运行时掰开来看了,这块可以很好的帮助咱们对 Goroutine 运行阶段作一个的剖析,能够得知到底慢哪,而后再决定下一步的排查方向。以下:google

名称 含义 耗时
Execution Time 执行时间 3140ns
Network Wait Time 网络等待时间 0ns
Sync Block Time 同步阻塞时间 0ns
Blocking Syscall Time 调用阻塞时间 0ns
Scheduler Wait Time 调度等待时间 14ns
GC Sweeping GC 清扫 0ns
GC Pause GC 暂停 0ns

View trace

在对当前程序的 Goroutine 运行分布有了初步了解后,咱们再经过 “查看跟踪” 看看之间的关联性,以下:

image

这个跟踪图粗略一看,相信有的小伙伴会比较懵逼,咱们能够依据注解一块块查看,以下:

  1. 时间线:显示执行的时间单元,根据时间维度的不一样能够调整区间,具体可执行 shift + ? 查看帮助手册。
  2. 堆:显示执行期间的内存分配和释放状况。
  3. 协程:显示在执行期间的每一个 Goroutine 运行阶段有多少个协程在运行,其包含 GC 等待(GCWaiting)、可运行(Runnable)、运行中(Running)这三种状态。
  4. OS 线程:显示在执行期间有多少个线程在运行,其包含正在调用 Syscall(InSyscall)、运行中(Running)这两种状态。
  5. 虚拟处理器:每一个虚拟处理器显示一行,虚拟处理器的数量通常默认为系统内核数。
  6. 协程和事件:显示在每一个虚拟处理器上有什么 Goroutine 正在运行,而连线行为表明事件关联。

image

点击具体的 Goroutine 行为后能够看到其相关联的详细信息,这块很简单,你们实际操做一下就懂了。文字解释以下:

  • Start:开始时间
  • Wall Duration:持续时间
  • Self Time:执行时间
  • Start Stack Trace:开始时的堆栈信息
  • End Stack Trace:结束时的堆栈信息
  • Incoming flow:输入流
  • Outgoing flow:输出流
  • Preceding events:以前的事件
  • Following events:以后的事件
  • All connected:全部链接的事件

View Events

咱们能够经过点击 View Options-Flow events、Following events 等方式,查看咱们应用运行中的事件流状况。以下:

image

经过分析图上的事件流,咱们可得知这程序从 G1 runtime.main 开始运行,在运行时建立了 2 个 Goroutine,先是建立 G18 runtime/trace.Start.func1,而后再是 G19 main.main.func1 。而同时咱们能够经过其 Goroutine Name 去了解它的调用类型,如:runtime/trace.Start.func1 就是程序中在 main.main 调用了 runtime/trace.Start 方法,而后该方法又利用协程建立了一个闭包 func1 去进行调用。

image

在这里咱们结合开头的代码去看的话,很明显就是 ch 的输入输出的过程了。

结合实战

今天生产环境忽然出现了问题,机智的你早已埋好 _ "net/http/pprof" 这个神奇的工具,你麻利的执行了以下命令:

View trace

你很快的看到了熟悉的 List 界面,而后不信邪点开了 View trace 界面,以下:

image

彻底看懵的你,稳住,对着合适的区域执行快捷键 W 不断地放大时间线,以下:

image

通过初步排查,你发现上述绝大部分的 G 居然都和 google.golang.org/grpc.(*Server).Serve.func 有关,关联的一大串也是 Serve 所触发的相关动做。

image

这时候有经验的你内心已经有了初步结论,你能够继续追踪 View trace 深刻进去,不过我建议先鸟瞰全貌,所以咱们再往下看 “Network blocking profile” 和 “Syscall blocking profile” 所提供的信息,以下:

Network blocking profile

image

Syscall blocking profile

image

经过对以上三项的跟踪分析,加上这个泄露,这个阻塞的耗时,这个涉及的内部方法名,很明显就是哪位又忘记关闭客户端链接了,赶忙改改改。

总结

经过本文咱们习得了 go tool trace 的武林秘籍,它可以跟踪捕获各类执行中的事件,例如 Goroutine 的建立/阻塞/解除阻塞,Syscall 的进入/退出/阻止,GC 事件,Heap 的大小改变,Processor 启动/中止等等。

但愿你可以用好 Go 的两大杀器 pprof + trace 组合,此乃排查好搭档,谁用谁清楚,即便他并不万能。

参考

image
相关文章
相关标签/搜索