gops: A tool to list and diagnose Go processes currently running on your system
gops
能够列出当前系统中运行的go
进程,并且能够帮助对指定go
进程进行诊断,是gopher
必备居家良品。
安装
1 | $ go get -u github.com/google/gops |
简单使用
查看命令帮助
1 | $ gops |
查看当前系统的go进程
1 | $ gops |
上面各列的含义分别是:1
pid ppid 二进制文件名 go编译版本 二进制文件路径
而在go编译版本前面的 *
表明当前进程包含了agent
。
查看某个进程的信息
1 | $ gops 29573 |
进程诊断
为了能够使用诊断功能,我们需要在我们的程序代码中启动一个agent
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// import "github.com/google/gops/agent"
func main(){
err := agent.Listen(agent.Options{
Addr: ":5050", // agent监听地址
ShutdownCleanup: false, // 如果true,会监听SIGINT信号,自动关闭agent并退出进程
})
if err != nil {
log.Fatal(err)
}
defer agent.Close() // 手动关闭agent
.....
}
在启动agent
的时候,我们需要指定监听的端口。
启动了agent
之后,我们在gops
中就可以看到多了一个*
:1
2$ gops
29135 27413 gops-demo * go1.11.2 /home/...
开启了agent
之后,我们就可以使用gops
强大的诊断功能了1
$ gops <cmd> <pid|agent-addr>
Commands:
stack
:查看进程栈信息gc
:手动触发一次gc
,并阻塞等到gc
结束setgc
:相当于设置GOGC
值memstats
:查看进程的内存统计信息version
:查看构建二进制程序的go版本stats
:查看runtimes的统计信息,主要是goroutine数量和thread数量trace
:运行5s的 runtime tracer,并启动一个http server
用于查看trace信息pprof-heap
:读取heap的profile并启动go tool pprof
pprof-cpu
:读取cpu之后30s的profile并启动go tool pprof
查看内存统计
1 | $ gops memstats 29573 |
注意:读取内存统计信息的时候会触发STW
:1
2
3
4
5
6
7
8
9
10// runtime/mstas.go
func ReadMemStats(m *MemStats) {
stopTheWorld("read mem stats")
systemstack(func() {
readmemstats_m(m)
})
startTheWorld()
}
设置gc触发百分比
默认gc
的触发百分比是100%
查看当前gc
触发比例:1
2
3
4$ gops memstats 29573
...
next-gc: when heap-alloc >= 4.00MB (4194304 bytes)
...
当前的触发时机是,heap-alloc
达到4MB
更改gc
触发比例:1
2
3
4
5
6
7$ gops setgc 29573 200
New GC percent set to 200. Previous value was 100.
$ gops memstats 29573
...
next-gc: when heap-alloc >= 8.00MB (8388608 bytes)
...
next-gc
的计算公式:1
next_gc = heap_marked + heap_marked*uint64(gcpercent)/100
heap_marked
表示在一次gc
中,标记为存活的总对象大小,而gcpercent
就是我们设置的gc
触发比例,默认为100
。
注意:比如next_gc
为8MB
,并不是真的等到总共分配了8MB的对象才触发GC。这个next_gc
实际上是gc
后的goal heap size
。现在的gc
实现,对象标记与业务代码会并发执行,而在业务代码中还会申请分配新的对象。如果真的等到next_gc
才触发gc
,那么等到gc
结束之后,当前的heap size
可能会大于next_gc
,因此实际上gc
的触发会提前一点(有另一个字段gc_trigger
来决定)。
gops的实现
我们程序中的agent
,会在指定端口监听tcp
连接,通时默认在~/.config/gops
目录下,以进程号为文件名创建一个记录监听端口的文件。
当我们通过gops
执行命令时,首先会读取该文件,获取监听的端口,然后与agent
建立连接,发送事先定义好的命令:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31const (
// StackTrace represents a command to print stack trace.
StackTrace = byte(0x1)
// GC runs the garbage collector.
GC = byte(0x2)
// MemStats reports memory stats.
MemStats = byte(0x3)
// Version prints the Go version.
Version = byte(0x4)
// HeapProfile starts `go tool pprof` with the current memory profile.
HeapProfile = byte(0x5)
// CPUProfile starts `go tool pprof` with the current CPU profile
CPUProfile = byte(0x6)
// Stats returns Go runtime statistics such as number of goroutines, GOMAXPROCS, and NumCPU.
Stats = byte(0x7)
// Trace starts the Go execution tracer, waits 5 seconds and launches the trace tool.
Trace = byte(0x8)
// BinaryDump returns running binary file.
BinaryDump = byte(0x9)
// SetGCPercent sets the garbage collection target percentage.
SetGCPercent = byte(0x10)
)
agent
收到gops
的请求后,调用runtime
的接口,并将结果返回。