[golang] 런타임에 콜 스택 출력하기

2024. 11. 22. 14:54·Language/Go

서론

디버깅은 소프트웨어 개발 과정에서 중요한 부분을 차지한다. 코드가 예상대로 동작하지 않거나 오류가 발생했을 때, 문제의 원인을 추적하고 해결하는 데 많은 시간이 소요된다. 특히 함수 호출 순서나 실행 흐름을 파악하는 것이 어려울 때, 콜 스택을 출력하는 방법은 매우 유용한 도구가 된다. 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
'Language/Go' 카테고리의 다른 글
  • [golang] panic 없이 interface{} 타입 변환하기
  • [golang] Base64 인코딩과 디코딩 처리하기
  • [golang] panic과 recover로 try-catch 따라 하기
  • [golang] path/filepath 패키지로 멀티 플랫폼에서 파일 경로 처리
在晟
在晟
  • 在晟
    Jae-Sung
    在晟
  • 전체
    오늘
    어제
    • 분류 전체보기 (25)
      • Language (23)
        • Go (23)
      • DBMS (1)
        • MariaDB (1)
      • Notes (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Jae-Sung
    • GitHub
  • 인기 글

  • 태그

    Runtime
    recover
    interface{}
    http
    오블완
    fasthttp
    go
    filepath
    unsafe
    티스토리챌린지
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
在晟
[golang] 런타임에 콜 스택 출력하기
상단으로

티스토리툴바