Go官方進程診斷工具gops詳解


gops簡介

gops 是Go團隊提供的命令行工具,它可以用來獲取go進程運行時信息。

可以查看:

  • 當前有哪些go語言進程,哪些使用gops的go進程
  • 進程的概要信息
  • 進程的調用棧
  • 進程的內存使用情況
  • 構建程序的Go版本
  • 運行時統計信息

可以獲取:

  • trace
  • cpu profile和memory profile

還可以:

  • 讓進程進行1次GC
  • 設置GC百分比

示例代碼

使用Options配置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
package main

import (
"log"
"runtime"
"time"

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

func main() {
if err := agent.Listen(agent.Options{
Addr: "0.0.0.0:8848",
// ConfigDir: "/home/centos/gopsconfig", // 最好使用默認
ShutdownCleanup: true}); err != nil {
log.Fatal(err)
}

// 測試代碼
_ = make([]int, 1000, 1000)
runtime.GC()

_ = make([]int, 1000, 2000)
runtime.GC()

time.Sleep(time.Hour)
}

agent Option選項

agent有3個配置:

  • Addr:agent要監聽的ip和端口,默認ip為環回地址,端口隨機分配。
  • ConfigDir:該目錄存放的不是agent的配置,而是每一個使用了agent的go進程信息,文件以pid命名,內容是該pid進程所監聽的端口號,所以其中文件的目的是形成pid到端口的映射。默認值為~/.config/gops
  • ShutdownCleanup:進程退出時,是否清理ConfigDir中的文件,默認值為false,不清理

通常可以把Addr設置為要監聽的IP,把ShutdownCleanup設置為ture,進程退出后,殘留在ConfigDir目錄的文件不再有用,最好清除掉。

ConfigDir示例:

1
2
3
4
5
// gopsconfig為設置的ConfigDir目錄,2051為pid,8848為端口號。
➜ ~ cat gopsconfig/2051
8848%
➜ ~ netstat -nap | grep `pgrep gopsexample`
tcp6 0 0 :::8848 :::* LISTEN 2051/./gopsexample

gops原理

gops的原理是,代碼中導入gops/agent,建立agent服務,gops命令連接agent讀取進程信息。

gops

agent的實現原理可以查看agent/handle函數

使用go標准庫中原生接口實現相關功能,如同你要在自己的程序中開啟pprof類似,只不過這部分功能由gops/agent實現了:

  • 使用runtime.MemStats獲取內存情況
  • 使用runtime/pprof獲取調用棧、cpu profile和memory profile
  • 使用runtime/trace獲取trace
  • 使用runtime獲取stats信息
  • 使用runtime/debugGC設置和啟動GC

再談ConfigDir。從源碼上看,ConfigDir對agent並沒有用途,對gops有用。當gops和ConfigDir在一台機器上時,即gops查看本機的go進程信息,gops可以通過其中的文件,快速找到agent服務的端口。能夠實現:gops <sub-cmd> pidgops <sub-cmd> 127.0.0.1:port的轉換。

如果代碼中通過ConfigDir指定了其他目錄,使用gops時,需要添加環境變量GOPS_CONFIG_DIR指向ConfigDir使用的目錄。

子命令介紹

gops后面可以跟子命令,然后是pid或者遠端地址。

也可以直接跟pid,查看本機進程信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  ~ gops help memstats
gops is a tool to list and diagnose Go processes.

Usage:
gops <cmd> <pid|addr> ...
gops <pid> # displays process info
gops help # displays this help message

Commands:
stack Prints the stack trace.
gc Runs the garbage collector and blocks until successful.
setgc Sets the garbage collection target percentage.
memstats Prints the allocation and garbage collection stats.
version Prints the Go version used to build the program.
stats Prints runtime stats.
trace Runs the runtime tracer for 5 secs and launches "go tool trace".
pprof-heap Reads the heap profile and launches "go tool pprof".
pprof-cpu Reads the CPU profile and launches "go tool pprof".

All commands require the agent running on the Go process.
"*" indicates the process is running the agent.

查看當前機器上go程序進程信息

查看當前機器上的go進程,可以列出pid、ppid、進程名、可執行程序所使用的go版本,以及可執行程序的路徑。

1
2
3
4
5
6
7
8
➜  ~ gops
67292 66333 gops * go1.13 /Users/shitaibin/Workspace/golang_step_by_step/gops/gops
67434 65931 gops go1.13 /Users/shitaibin/go/bin/gops
66551 1 gocode go1.11.2 /Users/shitaibin/go/bin/gocode
137 1 com.docker.vmnetd go1.12.7 /Library/PrivilegedHelperTools/com.docker.vmnetd
811 807 com.docker.backend go1.12.13 /Applications/Docker.app/Contents/MacOS/com.docker.backend
807 746 com.docker.supervisor go1.12.13 /Applications/Docker.app/Contents/MacOS/com.docker.supervisor
810 807 com.docker.driver.amd64-linux go1.12.13 /Applications/Docker.app/Contents/MacOS/com.docker.driver.amd64-linux

*的是程序中使用了gops/agent,不帶*的是普通的go程序。

go程序進程樹

查看進程樹:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  ~ gops tree
...
├── 66333
│   └── [*] 67292 (gops) {go1.13}
├── 1
│   ├── 66551 (gocode) {go1.11.2}
│   └── 137 (com.docker.vmnetd) {go1.12.7}
├── 65931
│   └── 67476 (gops) {go1.13}
└── 746
└── 807 (com.docker.supervisor) {go1.12.13}
├── 811 (com.docker.backend) {go1.12.13}
└── 810 (com.docker.driver.amd64-linux) {go1.12.13}

pid:進程概要信息

查看進程的概要信息,非gops進程也可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  ~ gops 67292
parent PID: 66333
threads: 7
memory usage: 0.018%
cpu usage: 0.000%
username: shitaibin
cmd+args: ./gops
elapsed time: 11:28
local/remote: 127.0.0.1:54753 <-> :0 (LISTEN)
➜ ~
➜ ~ gops 807
parent PID: 746
threads: 28
memory usage: 0.057%
cpu usage: 0.003%
username: shitaibin
cmd+args: /Applications/Docker.app/Contents/MacOS/com.docker.supervisor -watchdog fd:0
elapsed time: 27-23:36:35
local/remote: 127.0.0.1:54832 <-> :0 ()
local/remote: *:53849 <-> :0 ()
local/remote: 127.0.0.1:49473 <-> :0 (LISTEN)

stack:當前調用棧

查看使用gops的進程的調用棧:

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
➜  ~ gops stack 67292
goroutine 19 [running]:
runtime/pprof.writeGoroutineStacks(0x1197160, 0xc00009c028, 0x0, 0x0)
/Users/shitaibin/goroot/src/runtime/pprof/pprof.go:679 +0x9d
runtime/pprof.writeGoroutine(0x1197160, 0xc00009c028, 0x2, 0x0, 0x0)
/Users/shitaibin/goroot/src/runtime/pprof/pprof.go:668 +0x44
runtime/pprof.(*Profile).WriteTo(0x1275c60, 0x1197160, 0xc00009c028, 0x2, 0xc00009c028, 0x0)
/Users/shitaibin/goroot/src/runtime/pprof/pprof.go:329 +0x3da
github.com/google/gops/agent.handle(0x1665008, 0xc00009c028, 0xc000014068, 0x1, 0x1, 0x0, 0x0)
/Users/shitaibin/go/src/github.com/google/gops/agent/agent.go:185 +0x1ab
github.com/google/gops/agent.listen()
/Users/shitaibin/go/src/github.com/google/gops/agent/agent.go:133 +0x2bf
created by github.com/google/gops/agent.Listen
/Users/shitaibin/go/src/github.com/google/gops/agent/agent.go:111 +0x364

goroutine 1 [sleep]:
runtime.goparkunlock(...)
/Users/shitaibin/goroot/src/runtime/proc.go:310
time.Sleep(0x34630b8a000)
/Users/shitaibin/goroot/src/runtime/time.go:105 +0x157
main.main()
/Users/shitaibin/Workspace/golang_step_by_step/gops/example.go:15 +0xa3

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

memstats: 內存使用情況

查看gops進程內存使用情況:

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
➜  ~ gops memstats 67944
alloc: 136.80KB (140088 bytes) // 當前分配出去未收回的內存總量
total-alloc: 152.08KB (155728 bytes) // 已分配出去的內存總量
sys: 67.25MB (70518784 bytes) // 當前進程從OS獲取的內存總量
lookups: 0
mallocs: 418 // 分配的對象數量
frees: 82 // 釋放的對象數量
heap-alloc: 136.80KB (140088 bytes) // 當前分配出去未收回的堆內存總量
heap-sys: 63.56MB (66650112 bytes) // 當前堆從OS獲取的內存
heap-idle: 62.98MB (66035712 bytes) // 當前堆中空閑的內存量
heap-in-use: 600.00KB (614400 bytes) // 當前堆使用中的內存量
heap-released: 62.89MB (65945600 bytes)
heap-objects: 336 // 堆中對象數量
stack-in-use: 448.00KB (458752 bytes) // 棧使用中的內存量
stack-sys: 448.00KB (458752 bytes) // 棧從OS獲取的內存總量
stack-mspan-inuse: 10.89KB (11152 bytes)
stack-mspan-sys: 16.00KB (16384 bytes)
stack-mcache-inuse: 13.56KB (13888 bytes)
stack-mcache-sys: 16.00KB (16384 bytes)
other-sys: 1.01MB (1062682 bytes)
gc-sys: 2.21MB (2312192 bytes)
next-gc: when heap-alloc >= 4.00MB (4194304 bytes) // 下次GC的條件
last-gc: 2020-03-16 10:06:26.743193 +0800 CST // 上次GC的世界
gc-pause-total: 83.84µs // GC總暫停時間
gc-pause: 44891 // 上次GC暫停時間,單位納秒
num-gc: 2 // 已進行的GC次數
enable-gc: true // 是否開始GC
debug-gc: false

stats: 運行時信息

查看運行時統計信息:

1
2
3
4
5
➜  ~ gops stats 68125
goroutines: 3
OS threads: 12
GOMAXPROCS: 8
num CPU: 8

trace

獲取當前運行5s的trace信息,會打開網頁:

1
2
3
4
5
6
➜  ~ gops trace 68125
Tracing now, will take 5 secs...
Trace dump saved to: /var/folders/5g/rz16gqtx3nsdfs7k8sb80jth0000gn/T/trace116447431
2020/03/16 10:23:37 Parsing trace...
2020/03/16 10:23:37 Splitting trace...
2020/03/16 10:23:37 Opening browser. Trace viewer is listening on http://127.0.0.1:55480

cpu profile

獲取cpu profile,並進入交互模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
➜  ~ gops pprof-cpu 68125
Profiling CPU now, will take 30 secs...

Profile dump saved to: /var/folders/5g/rz16gqtx3nsdfs7k8sb80jth0000gn/T/profile431166544
Binary file saved to: /var/folders/5g/rz16gqtx3nsdfs7k8sb80jth0000gn/T/binary765361519
File: binary765361519
Type: cpu
Time: Mar 16, 2020 at 10:25am (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)
(pprof) top
Showing nodes accounting for 0, 0% of 0 total
flat flat% sum% cum cum%

memory profile

獲取memory profile,並進入交互模式:

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
➜  ~ gops pprof-heap 68125
Profile dump saved to: /var/folders/5g/rz16gqtx3nsdfs7k8sb80jth0000gn/T/profile292136242
Binary file saved to: /var/folders/5g/rz16gqtx3nsdfs7k8sb80jth0000gn/T/binary693335273
File: binary693335273
Type: inuse_space
Time: Mar 16, 2020 at 10:27am (CST)
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)
(pprof) traces
File: binary693335273
Type: inuse_space
Time: Mar 16, 2020 at 10:27am (CST)
-----------+-------------------------------------------------------
bytes: 256kB
0 compress/flate.(*compressor).init
compress/flate.NewWriter
compress/gzip.(*Writer).Write
runtime/pprof.(*profileBuilder).build
runtime/pprof.profileWriter
-----------+-------------------------------------------------------
bytes: 64kB
0 compress/flate.newDeflateFast
compress/flate.(*compressor).init
compress/flate.NewWriter
compress/gzip.(*Writer).Write
runtime/pprof.(*profileBuilder).build
runtime/pprof.profileWriter
-----------+-------------------------------------------------------

使用遠程連接

agent的默認配置Option{},監聽的是環回地址。

1
2
3
4
5
➜  ~ sudo netstat -nap | grep 414
➜ ~ netstat -nap | grep `pgrep gopsexample`
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 127.0.0.1:36812 0.0.0.0:* LISTEN 414/./gopsexample

修改程序,在Option中設置監聽的地址和端口:

1
agent.Listen(agent.Options{Addr:"0.0.0.0:8848"})

在遠程主機上重新編譯、重啟進程,確認進程監聽的端口:

1
2
3
4
➜  ~ netstat -nap | grep `pgrep gopsexample`
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp6 0 0 :::8848 :::* LISTEN 887/./gopsexample

在本地主機上使用gops連接遠端go進程,並查看數據:

1
2
3
4
5
➜  ~ gops stats 192.168.9.137:8848
goroutines: 3
OS threads: 9
GOMAXPROCS: 4
num CPU: 4

gops后面只能跟pid查看進程簡要信息,不能跟ip和port查看遠端go進程簡要信息,這些簡要信息可以通過子命令匯集起來。

1
2
3
4
5
➜  ~ gops 192.168.9.137:8848
gops: unknown subcommand
➜ ~
➜ ~ gops version 192.168.9.137:8848
go1.13


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM