서론
디버깅은 소프트웨어 개발 과정에서 중요한 부분을 차지한다. 코드가 예상대로 동작하지 않거나 오류가 발생했을 때, 문제의 원인을 추적하고 해결하는 데 많은 시간이 소요된다. 특히 함수 호출 순서나 실행 흐름을 파악하는 것이 어려울 때, 콜 스택을 출력하는 방법은 매우 유용한 도구가 된다. Go 언어는 runtime과 debug 패키지를 제공하여, 런타임 중 함수 호출 정보를 쉽게 출력하고 코드 흐름을 추적할 수 있게 한다. 이러한 콜 스택 출력 기능은 디버깅 과정에서 매우 유용하며, 예기치 않은 오류를 해결하는 데 큰 도움이 된다.
STDERR에 콜 스택 출력
package main
import "runtime/debug"
func subModule() {
debug.PrintStack()
}
func Module() {
subModule()
}
func Manager() {
Module()
}
func main() {
Manager()
}
출력 결과
goroutine 1 [running]:
runtime/debug.Stack()
/usr/local/go/src/runtime/debug/stack.go:26 +0x5e
runtime/debug.PrintStack()
/usr/local/go/src/runtime/debug/stack.go:18 +0x13
main.subModule(...)
/tmp/sandbox421337237/prog.go:6
main.Module(...)
/tmp/sandbox421337237/prog.go:10
main.Manager(...)
/tmp/sandbox421337237/prog.go:14
main.main()
/tmp/sandbox421337237/prog.go:18 +0x12
debug.PrintStack을 사용하면 간단하게 STDERR에 전체 콜 스택을 출력할 수 있다.
콜 스택 전체 문자열 가져오기
func subModule() {
stack := debug.Stack()
fmt.Println(string(stack))
}
출력 결과
goroutine 1 [running]:
runtime/debug.Stack()
/usr/local/go/src/runtime/debug/stack.go:26 +0x5e
main.subModule()
/tmp/sandbox1728758504/prog.go:9 +0x13
main.Module(...)
/tmp/sandbox1728758504/prog.go:14
main.Manager(...)
/tmp/sandbox1728758504/prog.go:18
main.main()
/tmp/sandbox1728758504/prog.go:22 +0x11
debug.Stack을 사용하면 []byte 타입의 전체 콜 스택 문자열을 가져올 수 있다.
특정 콜 스택 정보 가져오기
func subModule() {
for i := 0; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
fmt.Printf("%s, %s:%d\n", runtime.FuncForPC(pc).Name(), file, line)
}
}
출력 결과
main.subModule, /tmp/sandbox2240394853/prog.go:10
main.Module, /tmp/sandbox2240394853/prog.go:19
main.Manager, /tmp/sandbox2240394853/prog.go:23
main.main, /tmp/sandbox2240394853/prog.go:27
runtime.main, /usr/local/go/src/runtime/proc.go:272
runtime.goexit, /usr/local/go/src/runtime/asm_amd64.s:1700
runtime.Caller를 사용하면 특정 콜 스택의 정보를 가져올 수 있다.
일부 구간 콜스택 정보 가져오기
func subModule() {
pcs := make([]uintptr, 10) // 가져올 프레임 수
n := runtime.Callers(0, pcs) // 건너 뛸 프레임 수
frames := runtime.CallersFrames(pcs[:n])
for {
frame, more := frames.Next()
fmt.Printf("%s, %s:%d\n", frame.Function, frame.File, frame.Line)
if !more {
break
}
}
}
출력 결과
runtime.Callers, /usr/local/go/src/runtime/extern.go:331
main.subModule, /tmp/sandbox3232018375/prog.go:10
main.Module, /tmp/sandbox3232018375/prog.go:23
main.Manager, /tmp/sandbox3232018375/prog.go:27
main.main, /tmp/sandbox3232018375/prog.go:31
runtime.main, /usr/local/go/src/runtime/proc.go:272
runtime.goexit, /usr/local/go/src/runtime/asm_amd64.s:1700
runtime.Callers와 runtime.CallersFrames를 사용하면 특정 구간의 콜 스택을 추적할 수 있다. runtime.Callers는 프로그램 카운터(PC) 배열을 반환하며, 이를 runtime.CallersFrames로 변환하여 함수 이름, 파일, 줄 번호 등의 정보를 가져올 수 있다.
'Language > Go' 카테고리의 다른 글
[golang] panic 없이 interface{} 타입 변환하기 (0) | 2024.11.24 |
---|---|
[golang] Base64 인코딩과 디코딩 처리하기 (0) | 2024.11.23 |
[golang] panic과 recover로 try-catch 따라 하기 (1) | 2024.11.21 |
[golang] path/filepath 패키지로 멀티 플랫폼에서 파일 경로 처리 (1) | 2024.11.20 |
[golang] flag 패키지로 명령줄 옵션 처리하기 (0) | 2024.11.19 |