使用gops轻松监控golang程序和性能分析

什么是gops

gops是google开发的一个在你系统上列出和分析golang程序的工具。它也是一个golang的库,当你集成这个库以后,分析golang程序会变得很简单。

安装

go get -u github.com/google/gops

在你的程序中启动gops的agent

package main

import (
	"log"
	"time"

	"github.com/google/gops/agent"
)

func main() {
	if err := agent.Listen(agent.Options{
        Addr: "listen tcp address",  // 你想监听的地址,不填的话系统会自动分配一个端口给它用。
    }); err != nil {
		log.Fatal(err)
	}
	time.Sleep(time.Hour)
}

gops使用

当你的程序嵌入了gops的agent,那么你就可以对你的golang程序进行诊断了。 除了诊断golang程序,gops程序也可以列出当前系统的golang程序和其系统,即时那些golang程序没有嵌入gops的agent。 注意的是只有程序嵌入了gops的agent才可以进行程序的分析,包括程序的堆、内存信息、cpu性能分析等

列出所有golang程序

$ gops

983   980    uplink-soecks  go1.9   /usr/local/bin/uplink-soecks
52697 52695  gops           go1.10  /Users/jbd/bin/gops
4132  4130   foops        * go1.9   /Users/jbd/bin/foops
51130 51128  gocode         go1.9.2 /Users/jbd/bin/gocode
$ gops tree

...
├── 1
│   └── 13962 [gocode] {go1.9}
├── 557
│   └── 635 [com.docker.supervisor] {go1.9.2}
│       └── 638 [com.docker.driver.amd64-linux] {go1.9.2}
└── 13744
    └── 67243 [gops] {go1.10}
$ gops <pid>

parent PID:	5985
threads:	27
memory usage:	0.199%
cpu usage:	0.139%
username:	jbd
cmd+args:	/Applications/Splice.app/Contents/Resources/Splice Helper.app/Contents/MacOS/Splice Helper -pid 5985
local/remote:	127.0.0.1:56765 <-> :0 (LISTEN)
local/remote:	127.0.0.1:56765 <-> 127.0.0.1:50955 (ESTABLISHED)
local/remote:	100.76.175.164:52353 <-> 54.241.191.232:443 (ESTABLISHED)

打印指定程序的堆栈信息

用于显示程序所有堆栈信息,包括每个 goroutine 的堆栈信息、运行状态等。

$ gops stack (<pid>|<addr>)

gops stack 85709
goroutine 20 [running]:
runtime/pprof.writeGoroutineStacks(0x1197600, 0xc0000f0000, 0x30, 0xc000076000)
	/Users/boya/golang/go/src/runtime/pprof/pprof.go:679 +0x9d
runtime/pprof.writeGoroutine(0x1197600, 0xc0000f0000, 0x2, 0x4ed28d60, 0xeaf54e38f8079f88)
	/Users/boya/golang/go/src/runtime/pprof/pprof.go:668 +0x44
runtime/pprof.(*Profile).WriteTo(0x1275c40, 0x1197600, 0xc0000f0000, 0x2, 0xc0000f0000, 0x0)
	/Users/boya/golang/go/src/runtime/pprof/pprof.go:329 +0x3da
github.com/google/gops/agent.handle(0x4211008, 0xc0000f0000, 0xc0000e0000, 0x1, 0x1, 0x0, 0x0)
	/Users/boya/gopath/pkg/mod/github.com/google/gops@v0.3.6/agent/agent.go:181 +0x1ab
github.com/google/gops/agent.listen()
	/Users/boya/gopath/pkg/mod/github.com/google/gops@v0.3.6/agent/agent.go:129 +0x275
created by github.com/google/gops/agent.Listen
	/Users/boya/gopath/pkg/mod/github.com/google/gops@v0.3.6/agent/agent.go:110 +0x364

goroutine 1 [sleep]:
runtime.goparkunlock(...)
	/Users/boya/golang/go/src/runtime/proc.go:310
time.Sleep(0x34630b8a000)
	/Users/boya/golang/go/src/runtime/time.go:105 +0x157
main.main()
	/Users/boydzheng/program/go/learn/Learning_go/gops/main.go:14 +0xa3

goroutine 19 [syscall]:
os/signal.signal_recv(0x0)
	/Users/boya/golang/go/src/runtime/sigqueue.go:144 +0x96
os/signal.loop()
	/Users/boya/golang/go/src/os/signal/signal_unix.go:23 +0x22
created by os/signal.init.0
	/Users/boya/golang/go/src/os/signal/signal_unix.go:29 +0x41
# ...

goroutine 20 [running] 这里的 20 表示goroutine的id,它是在每个go进程中是全局唯一的。running 表示正在运行状态。

打印指定程序的内存占有信息

查看程序当前的内存统计信息

$ gops memstats (<pid>|<addr>)

gops memstats 40515
alloc: 2.14MB (2247328 bytes)
total-alloc: 2.14MB (2247328 bytes)
sys: 68.31MB (71631096 bytes)
lookups: 0
mallocs: 417
frees: 6
heap-alloc: 2.14MB (2247328 bytes)
heap-sys: 63.66MB (66748416 bytes)
heap-idle: 61.05MB (64012288 bytes)
heap-in-use: 2.61MB (2736128 bytes)
heap-released: 60.98MB (63946752 bytes)
heap-objects: 411
stack-in-use: 352.00KB (360448 bytes)
stack-sys: 352.00KB (360448 bytes)
stack-mspan-inuse: 10.36KB (10608 bytes)
stack-mspan-sys: 16.00KB (16384 bytes)
stack-mcache-inuse: 13.56KB (13888 bytes)
stack-mcache-sys: 16.00KB (16384 bytes)
other-sys: 787.56KB (806457 bytes)
gc-sys: 2.14MB (2240512 bytes)
next-gc: when heap-alloc >= 4.27MB (4473924 bytes)
last-gc: -
gc-pause-total: 0s
gc-pause: 0
num-gc: 0
enable-gc: true
debug-gc: false

打印golang版本

$ gops version (<pid>|<addr>)

gops version 40515
go1.13.5

打印runtime的统计信息

包括goroutines个数、线程个数、GOMAXPROCS和cpu数。

$ gops stats (<pid>|<addr>)

gops stats 40515
goroutines: 3
OS threads: 8
GOMAXPROCS: 8
num CPU: 8

生成cpu性能文件

采集30s的CPU的性能分析数据并且保存在文件中,用go自带的性能分析工具进行分析,效果等价于go tool pprof
显示的信息中会告诉你文件保存的路径。

$ gops pprof-cpu (<pid>|<addr>)

gops pprof-cpu 40515
Profiling CPU now, will take 30 secs...
Profile dump saved to: /var/folders/r8/yjb25qtj0xng2vmkrtjfrwc913t5c8/T/profile919446242
Profiling dump saved to: /var/folders/r8/yjb25qtj0xng2vmkrtjfrwc913t5c8/T/profile919446242
Binary file saved to: /var/folders/r8/yjb25qtj0xng2vmkrtjfrwc913t5c8/T/binary227917017
File: binary227917017
Type: cpu
Time: Jan 14, 2020 at 10:02am (CST)
Duration: 30s, Total samples = 0
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 0, 0% of 0 total
      flat  flat%   sum%        cum   cum%
(pprof)

生成堆信息文件

采集当前heap的性能分析数据并且保存在文件中,用go自带的性能分析工具进行分析,效果等价于go tool pprof
显示的信息中会告诉你文件保存的路径。

$ gops pprof-heap (<pid>|<addr>)

gops pprof-heap 40515
Profile dump saved to: /var/folders/r8/yjb25qtj0xng2vmkrtjfrwc913t5c8/T/profile801546231
Profiling dump saved to: /var/folders/r8/yjb25qtj0xng2vmkrtjfrwc913t5c8/T/profile801546231
Binary file saved to: /var/folders/r8/yjb25qtj0xng2vmkrtjfrwc913t5c8/T/binary652763370
File: binary652763370
Type: inuse_space
Time: Jan 14, 2020 at 10:04am (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 902.59kB, 100% of 902.59kB total
      flat  flat%   sum%        cum   cum%
  902.59kB   100%   100%   902.59kB   100%  compress/flate.NewWriter
         0     0%   100%   902.59kB   100%  compress/gzip.(*Writer).Write
         0     0%   100%   902.59kB   100%  runtime/pprof.(*profileBuilder).build
         0     0%   100%   902.59kB   100%  runtime/pprof.profileWriter
(pprof)

采用http方式

http的方式也很简单,来自官方的文档https://golang.org/pkg/net/http/pprof/

package main

import _ "net/http/pprof"

func main() {
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()

	// do things
}

结合go tool pprof工具来使用,具体可查看https://golang.org/pkg/net/http/pprof/

总结

不管是采用哪种方式(gops或者http/pprof),都建议在程序中添加其中一种采集程序信息的方式,这对程序的优化和问题分析都很有帮助。

ref

https://github.com/google/gops
https://golang.org/pkg/net/http/pprof/

comments powered by Disqus