golang pprof 监控系列 go trace统计原理与使用解析

目录
  • 引言
  • go trace 使用
  • 统计原理介绍
    • Goroutine analysis
    • Execution
    • Network wait
    • Sync block,Blocking syscall,Scheduler wait
    • 各种profile 图

引言

服务监控系列文章

服务监控系列视频

关于go tool trace的使用,网上有相当多的资料,但拿我之前初学golang的经验来讲,很多资料都没有把go tool trace中的相关指标究竟是统计的哪些方法,统计了哪段区间讲解清楚。所以这篇文章不仅仅会介绍go tool trace的使用,也会对其统计的原理进行剖析。

golang版本 go1.17.12

先简单说下go tool trace 的使用场景,在分析延迟性问题的时候,go tool trace能起到很重要的作用,因为它会记录各种延迟事件并且对其进行时长分析,连关键代码位置也能找出来。

关于trace的实战案例,之前有出过一个视频(一次系统延迟分析案例),欢迎浏览

接着我们简单看下golang 里如何使用trace 功能。

go trace 使用

package main
import (
	_ "net/http/pprof"
	"os"
	"runtime/trace"
)
func main() {
	f, _ := os.Create("trace.out")
	defer f.Close()
	trace.Start(f)
	defer trace.Stop()
  ......
}

使用trace的功能其实比较容易,用trace.Start 便可以开启trace的事件采样功能,trace.Stop 则停止采用,采样的数据会记录到trace.Start 传来的输出流参数里,这里我是将一个名为trace.out 的文件作为输出流参数传入。

采样结束后便可以通过 go tool trace trace.out命令对采样文件进行分析了。

go tool trace 命令默认会使用本地随机一个端口来启动一个http服务,页面显示如下:

接着我会分析下各个链接对应的统计信息以及背后进行统计的原理,好的,接下来,正戏开始。

统计原理介绍

平时在使用prometheus对应用服务进行监控时,我们主要还是采用埋点的方式,同样,go runtime内部也是采用这样的方式对代码运行过程中的各种事件进行埋点,最后读取 整理后的埋点数据,形成我们在网页上看的trace监控图。

// src/runtime/trace.go:517
func traceEvent(ev byte, skip int, args ...uint64) {
	mp, pid, bufp := traceAcquireBuffer()
    .....
}

每次要记录相应的事件时,都会调用traceEvent方法,ev代表的是事件枚举,skip 是看栈帧需要跳跃的层数,args 在某些事件需要传入特定参数时传入。

在traceEvent 内部同时也会获取到当前事件发生时的线程信息,协程信息,p运行队列信息,并把这些信息同事件一起记录到一个缓冲区里。

// src/runtime/trace/trace.go:120
func Start(w io.Writer) error {
	tracing.Lock()
	defer tracing.Unlock()
	if err := runtime.StartTrace(); err != nil {
		return err
	}
	go func() {
		for {
			data := runtime.ReadTrace()
			if data == nil {
				break
			}
			w.Write(data)
		}
	}()
	atomic.StoreInt32(&tracing.enabled, 1)
	return nil
}

在我们启动trace.Start方法的时候,同时会启动一个协程不断读取缓冲区中的内容到strace.Start 的参数中。

在示例代码里,trace.Start 方法传入名为trace.out文件的输出流参数,所以在采样过程中,runtime会将采集到的事件字节流输出到trace.out文件,trace.out文件在被读取出的时候 是用了一个叫做Event的结构体来表示这些监控事件。

// Event describes one event in the trace.
type Event struct {
	Off   int       // offset in input file (for debugging and error reporting)
	Type  byte      // one of Ev*
	seq   int64     // sequence number
	Ts    int64     // timestamp in nanoseconds
	P     int       // P on which the event happened (can be one of TimerP, NetpollP, SyscallP)
	G     uint64    // G on which the event happened
	StkID uint64    // unique stack ID
	Stk   []*Frame  // stack trace (can be empty)
	Args  [3]uint64 // event-type-specific arguments
	SArgs []string  // event-type-specific string args
	// linked event (can be nil), depends on event type:
	// for GCStart: the GCStop
	// for GCSTWStart: the GCSTWDone
	// for GCSweepStart: the GCSweepDone
	// for GoCreate: first GoStart of the created goroutine
	// for GoStart/GoStartLabel: the associated GoEnd, GoBlock or other blocking event
	// for GoSched/GoPreempt: the next GoStart
	// for GoBlock and other blocking events: the unblock event
	// for GoUnblock: the associated GoStart
	// for blocking GoSysCall: the associated GoSysExit
	// for GoSysExit: the next GoStart
	// for GCMarkAssistStart: the associated GCMarkAssistDone
	// for UserTaskCreate: the UserTaskEnd
	// for UserRegion: if the start region, the corresponding UserRegion end event
	Link *Event
}

来看下Event事件里包含哪些信息: P 是运行队列,go在运行协程时,是将协程调度到P上运行的,G 是协程id,还有栈id StkID ,栈帧 Stk,以及事件发生时可以携带的一些参数Args,SArgs。 Type 是事件的枚举字段,Ts是事件发生的时间戳信息,Link 是与事件关联的其他事件,用于计算关联事件的耗时。

拿计算系统调用耗时来说,系统调用相关的事件有GoSysExit,GoSysCall 分别是系统调用退出事件和系统调用开始事件,所以GoSysExit.Ts - GoSysCall.Ts 就是系统调用的耗时。

特别提示: runtime 内部用到的监控事件枚举在src/runtime/trace.go:39 位置 ,而 在读取文件中的监控事件用到的枚举是 在src/internal/trace/parser.go:1028 ,虽然是两套,但是值是一样的。

很明显Link 字段不是在runtime 记录监控事件时设置的,而是在读取trace.out里的监控事件时,将事件信息按照协程id分组后 进行设置的。同一个协程的 GoSysExit.Ts - GoSysCall.Ts 就是该协程的系统调用耗时。

接下来我们来挨个分析下trace页面的统计信息以及背后的统计原理。

View trace是每个事件的时间线构成的监控图,在生产环境下1s都会产生大量的事件,我认为直接看这张图还是会让人眼花缭乱。 所以还是跳过它吧,从Goroutine analysis开始分析。

Goroutine analysis

go tool trace最终引用的代码位置是在go/src/cmd/trace 包下,main函数会启动一个http服务,并且注册一些处理函数,我们点击Goroutine analysis 其实就是请求到了其中一个处理函数上。 下面这段代码是注册的goroutine的处理函数点击Goroutine analysis 就会映射到 /goroutines 路由上。

// src/cmd/trace/goroutines.go:22
func init() {
	http.HandleFunc("/goroutines", httpGoroutines)
	http.HandleFunc("/goroutine", httpGoroutine)
}

让我们点击下 Goroutine analysis

进入到了一个显示代码调用位置的列表,列表中的每个代码位置都是事件EvGoStart 协程开始运行时的位置,其中N代表 在采用期间 在这个位置上有N个协程开始过运行。

你可能会好奇,是怎样确定这10个协程是由同一段代码执行的?runtime在记录协程开始执行的事件EvGoStart 时,是会把栈帧也记录下来的,而栈帧中包含函数名和程序计数器(PC)的值,在Goroutine analysis 页面 中就是协程就是按PC的值进行分组的。 以下是PC赋值的代码片段。

// src/internal/trace/goroutines.go:176
case EvGoStart, EvGoStartLabel:
			g := gs[ev.G]
			if g.PC == 0 {
				g.PC = ev.Stk[0].PC
				g.Name = ev.Stk[0].Fn
			}

我们再点击第一行链接 nfw/nfw_base/fw/routine.(*Worker).TryRun.func1 N=10 ,这里点击第一行的链接将会映射到 /goroutine 的路由上(注意路由已经不是s结尾了),由它的处理函数进行处理。点击后如图所示:

现在看到的就是针对这10个协程分别的系统调用,阻塞,调度延迟,gc这些统计信息。

接着我们从上到下挨个分析: Execution Time 是指着10个协程的执行时间占所有协程执行的比例。 接着是用于分析网络等待时间,锁block时间,系统调用阻塞时间 ,调度等待时间的图片,这些都是分析系统延迟,阻塞问题的利器。 这里就不再分析图了,相信网上会有很多这种资料。

然后来看下 表格里的各项指标:

Execution

是协程在采样这段时间内的执行时间。

记录的方式也很简单,在读取event事件时,是按时间线从前往后读取,每次读取到协程开始执行的时间时,会记录下协程的开始执行的时间戳(时间戳是包含在Event结构里的),读取到协程停顿事件时,则会把停顿时刻的时间戳减去开始执行的时间戳 得到 一小段的执行时间,将这小段的时间 累加到该协程的总执行时间上。

停顿则是由锁block,系统调用阻塞,网络等待,抢占调度造成的。

Network wait

顾名思义,网络等待时长, 其实也是和Execution类似的记录方式,首先记录下协程在网络等待时刻的时间戳,由于event是按时间线读取的,当读取到unblock事件时,再去看协程上一次是不是网络等待事件,如果是的话,则将当前时刻时间戳减去 网络等待时刻的时间戳,得到的这一小段时间,累加到该协程的总网络等待时长上。

Sync block,Blocking syscall,Scheduler wait

这三个时长 计算方式和前面两张也是类似的,不过注意下与之相关联的事件的触发条件是不同的。

Sync block 时长是由于 锁 sync.mutex ,通道channel,wait group,select case语句产生的阻塞都会记录到这里。 下面是相关代码片段。

// src/internal/trace/goroutines.go:192
case EvGoBlockSend, EvGoBlockRecv, EvGoBlockSelect,
			EvGoBlockSync, EvGoBlockCond:
			g := gs[ev.G]
			g.ExecTime += ev.Ts - g.lastStartTime
			g.lastStartTime = 0
			g.blockSyncTime = ev.Ts

Blocking syscall 就是系统调用造成的阻塞了。

Scheduler wait 是协程从可执行状态到执行状态的时间段,注意协程是可执行状态时 是会放到p 运行队列等待被调度的,只有被调度后,才会真正开始执行代码。 这里涉及到golang gpm模型的理解,这里就不再展开了。

后面两栏就是GC 占用total时间的一个百分比了,golang 的gc相关的知识也不继续展开了。

各种profile 图

还记得最开始分析trace.out生成的网页时,Goroutine analysis 下面是什么吗?是各种分析延迟相关的profile 图,数据的来源和我们讲Goroutine analysis 时分析单个Goroutine 的等待时间的指标是一样的,不过这里是针对所有goroutine而言。

Network blocking profile ()
Synchronization blocking profile ()
Syscall blocking profile ()
Scheduler latency profile ()

关于golang 的这个trace工具,还允许用户可以自定义监控事件 ,生成的trace网页里,User-defined tasks,User-defined regions 就是记录用户自定义的一些监控事件,这部分的应用等到以后再讲了。

Minimum mutator utilization 是一个看gc 对程序影响情况的曲线图,这个等以后有机会讲gc时再详细谈谈了。

以上就是golang pprof 监控系列 go trace统计原理与使用解析的详细内容,更多关于golang pprof监控go trace统计的资料请关注我们其它相关文章!

(0)

相关推荐

  • Golang pprof性能测试与分析讲解

    目录 一.性能分析类型 1.CPU性能分析 2.内存性能分析 3.阻塞性能分析 二.cpu性能分析 1.生成pporf 2.分析数据 三.内存性能分析 四.benchmark 生成 profile 一.性能分析类型 1.CPU性能分析 CPU性能分析是最常见的性能分析类型.启动CPU分析时,运行时每隔10ms中断一次,采集正在运行协程的堆栈信息. 程序运行结束后,可以根据收集的数据,找到最热代码路径. 一个函数在分析阶段出现的次数越多,则该函数的代码路径(code path)花费的时间占总运行时

  • golang利用pprof与go-torch如何做性能分析

    前言 软件开发过程中,项目上线并不是终点.上线后,还要对程序的取样分析运行情况,并重构现有的功能,让程序执行更高效更稳写. golang的工具包内自带pprof功能,使找出程序中占内存和CPU较多的部分功能方便了不少.加上uber的火焰图,可视化显示,让我们在分析程序时更简单明了. pprof有两个包用来分析程序一个是net/http/pprof另一个是runtime/pprof,net/http/pprof只是对runtime/pprof包进行封装并用http暴露出来,如下图源码所示: 使用n

  • golang pprof监控memory block mutex使用指南

    目录 profile trace 网页显示 如何使用 http 接口暴露的方式 allocs ,heap block mutex 代码生成profile文件的方式 总结 profile profile的中文被翻译轮廓,对于计算机程序而言,抛开业务逻辑不谈,它的轮廓是是啥呢?不就是cpu,内存,各种阻塞开销,线程,协程概况 这些运行指标或环境.golang语言自带了工具库来帮助我们描述,探测,分析这些指标或者环境信息,让我们来学习它. 在上一篇golang pprof 监控系列(1) —— go

  • golang pprof监控memory block mutex统计原理分析

    目录 引言 bucket结构体介绍 记录指标细节介绍 memory block mutex 总结 引言 在上一篇文章 golang pprof监控系列(2) —— memory,block,mutex 使用里我讲解了这3种性能指标如何在程序中暴露以及各自监控的范围.也有提到memory,block,mutex 把这3类数据放在一起讲,是因为他们统计的原理是很类似的.今天来看看它们究竟是如何统计的. 先说下结论,这3种类型在runtime内部都是通过一个叫做bucket的结构体做的统计,bucke

  • golang pprof 监控goroutine thread统计原理详解

    目录 引言 http 接口暴露的方式 goroutine profile 输出信息介绍 threadcreate 输出信息介绍 程序代码暴露指标信息 统计原理介绍 goroutine fetch 函数实现 threadcreate fetch 函数实现 总结 引言 在之前 golang pprof监控 系列文章里我分别介绍了go trace以及go pprof工具对memory,block,mutex这些维度的统计原理,今天我们接着来介绍golang pprof工具对于goroutine 和th

  • golang pprof 监控系列 go trace统计原理与使用解析

    目录 引言 go trace 使用 统计原理介绍 Goroutine analysis Execution Network wait Sync block,Blocking syscall,Scheduler wait 各种profile 图 引言 服务监控系列文章 服务监控系列视频 关于go tool trace的使用,网上有相当多的资料,但拿我之前初学golang的经验来讲,很多资料都没有把go tool trace中的相关指标究竟是统计的哪些方法,统计了哪段区间讲解清楚.所以这篇文章不仅仅

  • Golang pprof监控之cpu占用率统计原理详解

    目录 http 接口暴露的方式 程序代码生成profile cpu 统计原理分析 线程处理信号的时机 内核发送信号的方式 采样数据的公平性 总结 经过前面的几节对pprof的介绍,对pprof统计的原理算是掌握了七八十了,我们对memory,block,mutex,trace,goroutine,threadcreate这些维度的统计原理都进行了分析,但唯独还没有分析pprof 工具是如何统计cpu使用情况的,今天我们来分析下这部分. http 接口暴露的方式 还记得 golang pprof监

  • web项目中golang性能监控解析

    目录 性能监控 一.web项目(如gin中) 二.单个的go文件如果查看gc 性能监控 一.web项目(如gin中) 1.使用ginpprof import "github.com/DeanThompson/ginpprof" router := gin.Default() ginpprof.Wrap(router) 2.使用pprof 只需要在main.go中引入:_ “net/http/pprof” 访问:127.0.0.1:8080/debug/pprof /debug/ppro

  • Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    本文的完整代码在github.com/hdt3213/godis/redis/client 通常 TCP 客户端的通信模式都是阻塞式的: 客户端发送请求 -> 等待服务端响应 -> 发送下一个请求.因为需要等待网络传输数据,完成一次请求循环需要等待较多时间. 我们能否不等待服务端响应直接发送下一条请求呢?答案是肯定的. TCP 作为全双工协议可以同时进行上行和下行通信,不必担心客户端和服务端同时发包会导致冲突. p.s. 打电话的时候两个人同时讲话就会冲突听不清,只能轮流讲.这种通信方式称为半

  • GoLang日志监控系统实现

    目录 日志监控系统 项目简答介绍 系统架构 读取模块具体实现 日志解析模块 日志监控系统 Nginx(日志文件) -> log_process (实时读取解析写入) -> influxdb(存储) ->grafana(前端日志展示器) influxdb 属于GO语言编写的开源的时序型数据,着力于高性能 查询与存储时序型数据,influxdb 广泛的应用于存储系统的监控数据,IOT行业的实时数据. 目前市面上流行 TSDB(时序型处理数据库):influxDB, TimescaleDB,

  • JVM系列之String.intern的性能解析

    String对象有个特殊的StringTable字符串常量池,为了减少Heap中生成的字符串的数量,推荐尽量直接使用String Table中的字符串常量池中的元素. 那么String.intern的性能怎么样呢?我们一起来看一下. String.intern和G1字符串去重的区别 之前我们提到了,String.intern方法会返回字符串常量池中的字符串对象的引用. 而G1垃圾回收器的字符串去重的功能其实和String.intern有点不一样,G1是让两个字符串的底层指向同一个byte[]数组

  • vue-video-player实现实时视频播放方式(监控设备-rtmp流)

    监控设备播放效果如下 1.vue项目安装vue-video-player npm install vue-video-player --save 2.编写视频播放组件(放上完整的组件例子,父组件调用时给videoSrc和playerOptions.sources[0].src赋值就可以播放了,具体操作有注释) 注:style样式部分用了lang=scss,如果自己的项目没用他请用自己的方式改一下样式部分避免报错 <template> <div class="video-js&q

  • Spring boot admin 服务监控利器详解

    目录 一.简介 二.搭建 1.服务端 2.客户端 3.启动项目 4.客户端配置 3.微服务 3.1.服务端 3.2.客户端 4.我的微服务预警发送其他服务状态信息思路 一.简介 用于对 Spring Boot 应用的管理和监控.可以用来监控服务是否健康.是否在线.以及一些jvm数据等等.Spring Boot Admin 分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client),服务端和客户端之间采用 http 通讯方式实现数据交

随机推荐