이번에는 Google에서 만든 gops라는 도구를 소개하고자 합니다.
GitHub - google/gops: A tool to list and diagnose Go processes current...
https://github.com
GitHub: Let’s build from here
GitHub is where over 100 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and fea...
github.com
gops는 ps 명령과 유사하게 동작하지만, Go 언어로 작성된 프로세스에 한해 다음과 같은 기능을 제공합니다.
- 스택 트레이스 표시
- 메모리 상태 표시
- 런타임 상태 표시
- 메모리 프로파일링 수행 및 pprof 실행
- CPU 프로파일링 수행 및 pprof 실행
- 강제 GC 실행
하나씩 살펴보겠습니다.
Go 프로세스 목록
먼저 아무 인수도 지정하지 않고 실행합니다.
$ gops
10224 go (/opt/go/bin/go)
10244* main (/tmp/go-build638808727/command-line-arguments/_obj/exe/main)
10257 gops (/opt/godev/bin/gops)
139 processes left undetermined
여기서 *가 붙은 프로세스가 있는 것을 확인할 수 있습니다.
이는 아래에서 설명할 gops의 각종 기능을 실행할 수 있는 프로세스임을 나타냅니다.
사실 gops로 각종 기능을 실행하려면 대상 프로세스의 main 함수에 약간의 수정이 필요합니다.
수정 전 코드는 다음과 같습니다.
package main
import (
"log"
"time"
)
func main() {
m := make(map[int]int, 500000)
for i := 0; i < 500000; i++ {
m[i] = i
}
time.Sleep(time.Hour)
}
그리고 수정 후 코드는 다음과 같습니다.
package main
import (
"log"
"time"
"github.com/google/gops/agent"
)
func main() {
if err := agent.Listen(nil); err != nil {
log.Fatal(err)
}
m := make(map[int]int, 500000)
for i := 0; i < 500000; i++ {
m[i] = i
}
time.Sleep(time.Hour)
}
agent.Listen(nil)로 에이전트를 실행했습니다.
이 수정으로 인해 백그라운드에서 프로세스 모니터링과 제어가 가능해졌습니다.
gops 명령에서는 Go로 컴파일된 프로세스 목록이 표시되는데, 위 예시는 go run 명령을 실행한 결과입니다.
아시다시피 Go는 셀프 호스팅되므로 컴파일러(및 런처) 자체와 빌드되어 실행 중인 프로세스, 그리고 gops 프로세스 자신이 표시되는 것을 확인할 수 있습니다.
다음에 설명할 기능은 이렇게 수정한 프로세스에 한해서만 유효합니다.
스택 트레이스 표시
*가 붙은 프로세스 ID를 지정하여 다음과 같이 실행합니다.
$ gops stack 10244
goroutine 5 [running]:
github.com/google/gops/agent.handle(0x7f5da627a440, 0xc82002a000, 0xc82000a1f0, 0x1, 0x1, 0x0, 0x0)
/opt/godev/src/github.com/google/gops/agent/agent.go:86 +0xd9
...
현재 실행 중인 원시 스택 트레이스가 goroutine별로 모두 표시됩니다.
프로세스에서 응답이 없을 때 유용하게 사용할 수 있을 것 같습니다.
메모리 상태 표시
마찬가지로 프로세스 ID를 지정합니다.
$ gops memstats 10244
alloc: 19267968 bytes
total-alloc: 19393560 bytes
sys: 23939320 bytes
lookups: 11
mallocs: 2518
frees: 104
heap-alloc: 19267968 bytes
heap-sys: 20578304 bytes
heap-idle: 983040 bytes
heap-in-use: 19595264 bytes
heap-released: 0 bytes
heap-objects: 2414
stack-in-use: 393216 bytes
stack-sys: 393216 bytes
next-gc: when heap-alloc >= 28678953 bytes
last-gc: 1481194106621190296 ns
gc-pause: 455534 ns
num-gc: 2
enable-gc: true
debug-gc: false
각 메모리의 상태와 힙 사용량이 표시됩니다.
mallocs/frees는 기록 개수이므로 현재 확보된 개수는 아닙니다.
런타임 상태 표시
goroutines: 13
OS threads: 8
GOMAXPROCS: 2
num CPU: 2
실행하면 goroutine 수, OS 스레드 수, GOMAXPROCS 값, 실제 CPU 코어 수가 표시됩니다.
메모리 프로파일링 및 pprof 실행
순간적인 힙 프로파일을 가져와서 pprof가 실행됩니다.
아래에서는 pprof의 list 명령을 실행하여 각 행에서의 메모리 확보량을 표시했습니다.
(pprof) list
Total: 19MB
ROUTINE ======================== main.main in /home/spoteye/main.go
18.50MB 18.50MB (flat, cum) 97.37% of Total
. . 10:func main() {
. . 11: if err := agent.Start(); err != nil {
. . 12: log.Fatal(err)
. . 13: }
. . 14:
18MB 18MB 15: m := make(map[int]int, 500000)
. . 16: for i := 0; i < 500000; i++ {
512.07kB 512.07kB 17: m[i] = i
. . 18: }
. . 19:
. . 20: time.Sleep(time.Hour)
. . 21:}
...
pprof 명령은 기능이 매우 다양합니다. 이 글에서는 전부 설명할 수 없으므로 자세한 내용은 Go 공식 블로그 글을 참고하세요.
Profiling Go Programs - The Go Programming Language
https://blog.golang.org
The Go Blog - The Go Programming Language
More powerful Go execution traces, 14 March 2024 Michael Knyszek New features and improvements to execution traces from the last year. Robust generic functions on slices, 22 February 2024 Valentin Deleplace Avoiding memory leaks in the slices package. Rout
go.dev
CPU 프로파일링 및 pprof 실행
30초간 CPU 프로파일을 가져와서 pprof 명령을 실행합니다.
마찬가지로 위 링크를 참고하세요.
강제 GC 실행
gops 명령이 에이전트에 가비지 컬렉션 실행을 지시하고, 완료될 때까지 블록합니다.
GC의 효과를 더 잘 보여드리기 위해 소스 코드를 다음과 같이 수정했습니다.
package main
import (
"log"
"time"
"github.com/google/gops/agent"
)
func main() {
if err := agent.Start(); err != nil {
log.Fatal(err)
}
m := make(map[int]int, 500000)
for i := 0; i < 500000; i++ {
m[i] = i
}
m = nil
time.Sleep(time.Hour)
}
※ m에 nil을 할당했습니다.
gc 명령 실행 전 memstats는 다음과 같습니다.
alloc: 19376928 bytes
total-alloc: 19445104 bytes
sys: 24594680 bytes
lookups: 9
mallocs: 3473
frees: 662
heap-alloc: 19376928 bytes
heap-sys: 20840448 bytes
heap-idle: 942080 bytes
heap-in-use: 19898368 bytes
heap-released: 0 bytes
heap-objects: 2811
stack-in-use: 131072 bytes
stack-sys: 131072 bytes
next-gc: when heap-alloc >= 38035568 bytes
last-gc: 1481455449599960600 ns
gc-pause: 0 ns
num-gc: 1
enable-gc: true
debug-gc: false
이 상태에서 다음 명령을 실행합니다.
$ gops gc 10244
다시 memstats를 표시합니다.
alloc: 19335216 bytes
total-alloc: 19447008 bytes
sys: 24660216 bytes
lookups: 13
mallocs: 3510
frees: 734
heap-alloc: 19335216 bytes
heap-sys: 20840448 bytes
heap-idle: 1032192 bytes
heap-in-use: 19808256 bytes
heap-released: 0 bytes
heap-objects: 2776
stack-in-use: 131072 bytes
stack-sys: 131072 bytes
next-gc: when heap-alloc >= 38671024 bytes
last-gc: 1481455475620465600 ns
gc-pause: 0 ns
num-gc: 2
enable-gc: true
debug-gc: false
GC가 실행되어 힙 객체가 어느 정도 해제된 것을 확인할 수 있습니다.
그럼.
'Go' 카테고리의 다른 글
Go 언어로 간단한 수식 평가기 만들어보기 (0) | 2024.03.30 |
---|---|
Go언어의 const 식별자 iota 소개 (1) | 2024.03.30 |
Go 개발할 때 필요한 Makefile 작성해보기 (0) | 2024.03.25 |
Go 언어 개발 환경 설정 - go mod init과 그 필요성 (0) | 2024.03.03 |
Go 언어의 net/http 패키지의 http.HandleFunc이 실행되는 방식 이해하기 (0) | 2024.03.03 |