網頁

2023/6/29

Golang 使用pprof監控系統效能 system profiling

Go內建的net/http/pprof套件可用來監控應用程式的資源利用狀況。


範例環境:

  • macOS Ventura 13.4.1
  • Go 1.19


使用

Go的pprof必須搭配HTTP應用程式使用,在程式中以副作用方式匯入(side-effect import)pprof,即import _ "net/http/pprof"

package main

import (
    "net/http"
    _ "net/http/pprof"
)

func main() {
    http.ListenAndServe(":8080", nil)
}

啟動應用程式,pprof監控的資料可以在/debug/pprof查看。

例如這邊在瀏覽器開啟http://localhost:8080/debug/pprof/如下。



  • allocs - 所有的記憶體配置。
  • block - 追蹤goroutine的多執行緒阻塞(包括Timer channel)。
  • cmdline - 目前程式調用的命令。
  • goroutine - 追蹤目前的goroutine。
  • heap - 仍存活物件的記憶體配置。用來監控目前即過去的記憶體使用狀況及找出可能的記憶體洩漏。
  • mutex - 追蹤互斥鎖競爭。
  • profile - CPU用量分析。
  • threadcreate - 追蹤作業系統執行緒的建立。
  • trace - 目前程式的執行追蹤檔。

從上可看到目前系統中的allocs有4個、goroutine有4個、heap有4個、threadcreate有7個。allocs和heap內容相同。


取得profile

點選goroutine進入的頁面內容如下,最上面的goroutine profile: total 3顯示目前有3個goroutine在運作。下面可以看到每個goroutine的數量和stack trace。

goroutine profile: total 4
1 @ 0x102fff6 0x10647c5 0x125c415 0x125c22d 0x125918b 0x1268365 0x1268efe 0x1215fef 0x1217989 0x1218bac 0x1214d47 0x106a761
#	0x10647c4	runtime/pprof.runtime_goroutineProfileWithLabels+0x24	/usr/local/Cellar/go/1.19.3/libexec/src/runtime/mprof.go:846
#	0x125c414	runtime/pprof.writeRuntimeProfile+0xb4			/usr/local/Cellar/go/1.19.3/libexec/src/runtime/pprof/pprof.go:723
#	0x125c22c	runtime/pprof.writeGoroutine+0x4c			/usr/local/Cellar/go/1.19.3/libexec/src/runtime/pprof/pprof.go:683
#	0x125918a	runtime/pprof.(*Profile).WriteTo+0x14a			/usr/local/Cellar/go/1.19.3/libexec/src/runtime/pprof/pprof.go:330
#	0x1268364	net/http/pprof.handler.ServeHTTP+0x4a4			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/pprof/pprof.go:253
#	0x1268efd	net/http/pprof.Index+0x13d				/usr/local/Cellar/go/1.19.3/libexec/src/net/http/pprof/pprof.go:371
#	0x1215fee	net/http.HandlerFunc.ServeHTTP+0x2e			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:2109
#	0x1217988	net/http.(*ServeMux).ServeHTTP+0x148			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:2487
#	0x1218bab	net/http.serverHandler.ServeHTTP+0x30b			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:2947
#	0x1214d46	net/http.(*conn).serve+0x606				/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:1991

1 @ 0x103a136 0x10335d7 0x1064bc9 0x10b8b12 0x10b96ba 0x10b96a8 0x1161029 0x116bce5 0x120eb5f 0x106a761
#	0x1064bc8	internal/poll.runtime_pollWait+0x88		/usr/local/Cellar/go/1.19.3/libexec/src/runtime/netpoll.go:305
#	0x10b8b11	internal/poll.(*pollDesc).wait+0x31		/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_poll_runtime.go:84
#	0x10b96b9	internal/poll.(*pollDesc).waitRead+0x259	/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_poll_runtime.go:89
#	0x10b96a7	internal/poll.(*FD).Read+0x247			/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_unix.go:167
#	0x1161028	net.(*netFD).Read+0x28				/usr/local/Cellar/go/1.19.3/libexec/src/net/fd_posix.go:55
#	0x116bce4	net.(*conn).Read+0x44				/usr/local/Cellar/go/1.19.3/libexec/src/net/net.go:183
#	0x120eb5e	net/http.(*connReader).backgroundRead+0x3e	/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:678

1 @ 0x103a136 0x10335d7 0x1064bc9 0x10b8b12 0x10ba1b4 0x10ba1a1 0x1162195 0x11719c8 0x1170d3d 0x1219105 0x1218d3d 0x126a0a5 0x126a075 0x1039d72 0x106a761
#	0x1064bc8	internal/poll.runtime_pollWait+0x88		/usr/local/Cellar/go/1.19.3/libexec/src/runtime/netpoll.go:305
#	0x10b8b11	internal/poll.(*pollDesc).wait+0x31		/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_poll_runtime.go:84
#	0x10ba1b3	internal/poll.(*pollDesc).waitRead+0x233	/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_poll_runtime.go:89
#	0x10ba1a0	internal/poll.(*FD).Accept+0x220		/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_unix.go:614
#	0x1162194	net.(*netFD).accept+0x34			/usr/local/Cellar/go/1.19.3/libexec/src/net/fd_unix.go:172
#	0x11719c7	net.(*TCPListener).accept+0x27			/usr/local/Cellar/go/1.19.3/libexec/src/net/tcpsock_posix.go:142
#	0x1170d3c	net.(*TCPListener).Accept+0x3c			/usr/local/Cellar/go/1.19.3/libexec/src/net/tcpsock.go:288
#	0x1219104	net/http.(*Server).Serve+0x384			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:3070
#	0x1218d3c	net/http.(*Server).ListenAndServe+0x7c		/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:2999
#	0x126a0a4	net/http.ListenAndServe+0x44			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:3255
#	0x126a074	main.main+0x14					/Users/<user>/Documents/projects/go-demo/main.go:9
#	0x1039d71	runtime.main+0x211				/usr/local/Cellar/go/1.19.3/libexec/src/runtime/proc.go:250

1 @ 0x1066f5f 0x10752c9 0x10b96e5 0x10b96cd 0x10b9485 0x1161029 0x116bce5 0x120f031 0x11c319f 0x11c3d2f 0x11c3f87 0x11c6439 0x120b9f9 0x120b9fa 0x12105ca 0x1214a85 0x106a761
#	0x1066f5e	syscall.syscall+0x7e				/usr/local/Cellar/go/1.19.3/libexec/src/runtime/sys_darwin.go:23
#	0x10752c8	syscall.read+0x48				/usr/local/Cellar/go/1.19.3/libexec/src/syscall/zsyscall_darwin_amd64.go:1189
#	0x10b96e4	syscall.Read+0x284				/usr/local/Cellar/go/1.19.3/libexec/src/syscall/syscall_unix.go:183
#	0x10b96cc	internal/poll.ignoringEINTRIO+0x26c		/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_unix.go:794
#	0x10b9484	internal/poll.(*FD).Read+0x24			/usr/local/Cellar/go/1.19.3/libexec/src/internal/poll/fd_unix.go:163
#	0x1161028	net.(*netFD).Read+0x28				/usr/local/Cellar/go/1.19.3/libexec/src/net/fd_posix.go:55
#	0x116bce4	net.(*conn).Read+0x44				/usr/local/Cellar/go/1.19.3/libexec/src/net/net.go:183
#	0x120f030	net/http.(*connReader).Read+0x170		/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:786
#	0x11c319e	bufio.(*Reader).fill+0xfe			/usr/local/Cellar/go/1.19.3/libexec/src/bufio/bufio.go:106
#	0x11c3d2e	bufio.(*Reader).ReadSlice+0x2e			/usr/local/Cellar/go/1.19.3/libexec/src/bufio/bufio.go:372
#	0x11c3f86	bufio.(*Reader).ReadLine+0x26			/usr/local/Cellar/go/1.19.3/libexec/src/bufio/bufio.go:401
#	0x11c6438	net/textproto.(*Reader).readLineSlice+0x98	/usr/local/Cellar/go/1.19.3/libexec/src/net/textproto/reader.go:56
#	0x120b9f8	net/textproto.(*Reader).ReadLine+0x78		/usr/local/Cellar/go/1.19.3/libexec/src/net/textproto/reader.go:37
#	0x120b9f9	net/http.readRequest+0x79			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/request.go:1036
#	0x12105c9	net/http.(*conn).readRequest+0x249		/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:994
#	0x1214a84	net/http.(*conn).serve+0x344			/usr/local/Cellar/go/1.19.3/libexec/src/net/http/server.go:1916


在命令列輸入go tool pprof <profile-path>會下載為一份pprof的goroutine profile檔並進入pprof命令互動模式。<profile-path>為profile的url路徑。例如這邊輸入go tool pprof http://localhost:8080/debug/pprof/goroutine會下載一份pprof.goroutine.001.pb.gz檔預設放在使用者目錄下的pprof目錄,即/Users/<user>/pprof

~% go tool pprof http://localhost:8080/debug/pprof/goroutine
Fetching profile over HTTP from http://localhost:8080/debug/pprof/goroutine
Saved profile in /Users/<user>/pprof/pprof.goroutine.001.pb.gz
Type: goroutine
Time: Jun 30, 2023 at 10:24pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)


轉為圖形檔

進入pprof命令互動模式後,可選擇將檔案轉為不同格式的圖形,例如這邊輸入png則轉為profile001.png檔在執行目錄下。

(pprof) png
Generating report in profile001.png

轉圖形檔需要graphviz工具,mac輸入brew install graphviz安裝。

或是先離開pprof互動模式回到命令列,在命令列輸入go tool pprof -png <pprof-profile-path>則是把profile檔轉為png。<pprof-profile-path>為profile檔的目錄位置。

~% go tool pprof -png ~/pprof/pprof.goroutine.001.pb.gz
Generating report in profile001.png

pprof profile轉換成的圖形檔如下。




沒有留言:

張貼留言