golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang) 実行ファイルのファイル名や行番号、関数名などを取得する

golang.png


runtime.Callerやruntime.FuncForPCを使って
実行ファイルのパスや実行している行番号、関数名などを取得する





目次



行番号やファイル名の取得(runtime.Caller)

func Caller(skip int) (pc uintptr, file string, line int, ok bool)

引数

  • skip: フレームをどこまで遡るか

戻り値

  • pc : メモリのアドレス
  • file: ファイル名
  • line: 関数が呼ばれた行番号。
  • ok : 取得できたかどうかの真偽値
package main

import "runtime"

func main() {
    pc, file, line, ok := runtime.Caller(0)
    if ok {
        println(pc, file, line)
    }
 
    _, name, _, _ := runtime.Caller(0)
    println(name) // /go/src/main.go

    _, name, _, _ = runtime.Caller(1)
    println(name) // /src/runtime/proc.go
}


引数skipについて

Bは関数一つ分ズレる

package main

import "runtime"

func A(skip int) {
    _, name, _, _ := runtime.Caller(skip)
    println(name)
}

func B(skip int) {
    A(skip)
}

func main() {
    A(0) // /path/to/dir/main.go
    A(1) // /path/to/dir/main.go
    A(2) // /src/runtime/proc.go
    A(3) // /src/runtime/asm_amd64.s
    A(4) // 空

    B(0) // /path/to/dir/main.go
    B(1) // /path/to/dir/main.go
    B(2) // /path/to/dir/main.go
    B(3) // /src/runtime/proc.go
    B(4) // /src/runtime/asm_amd64.s
}



関数名の取得(runtime.FuncForPC)

package main

import (
    "reflect"
    "runtime"
)

func function() {}

func main() {
    rv := reflect.ValueOf(function)
    name := runtime.FuncForPC(rv.Pointer()).Name()
    println(name) // main.function

    rv = reflect.ValueOf(runtime.FuncForPC)
    name = runtime.FuncForPC(rv.Pointer()).Name()
    println(name) // runtime.FuncForPC
}


関数が定義されている行番号の取得(FileLine)

package main

import (
    "fmt"
    "reflect"
    "runtime"
)

func function() {}

func main() {
    rv := reflect.ValueOf(function)
    ffpc := runtime.FuncForPC(rv.Pointer())

    println(rv.Pointer() == ffpc.Entry()) // true  FuncForPC に渡したポインタと同じ

    file, line := ffpc.FileLine(ffpc.Entry())
    fmt.Println(file, line) // /path/to/dir/main.go  9

    println(ffpc.Name()) // main.function
}