【Golang】Go之runtime.Caller/Callers/CallersFrames/FuncForPC使用

If the skip is 0, the function returns the name of the current call Caller function, file, program counter PC, 1 is a layer function, and so on:

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

import (
    "log"
    "runtime"
)

func main() {
    test()
}

func test() {
    test2()
}

func test2() {

    // 上2层函数信息
    pc, file, line, ok := runtime.Caller(2)
    // 是否获取成功
    log.Println(ok)
    // 函数指针
    log.Println(pc)
    // 所属文件
    log.Println(file)
    // 所属行
    log.Println(line)
    // 获取函数信息
    f := runtime.FuncForPC(pc)
    // 函数名
    log.Println(f.Name())

    // 当前函数信息
    pc, file, line, ok = runtime.Caller(0)
    log.Println(pc)
    log.Println(file)
    log.Println(line)
    log.Println(ok)
    f = runtime.FuncForPC(pc)
    log.Println(f.Name())

    // 上一层函数信息
    pc, file, line, ok = runtime.Caller(1)
    log.Println(pc)
    log.Println(file)
    log.Println(line)
    log.Println(ok)
    f = runtime.FuncForPC(pc)
    log.Println(f.Name())
}

The output is:

2020/04/04 03:54:48 true
2020/04/04 03:54:48 17410619
2020/04/04 03:54:48 /Users/lurongming/main.go
2020/04/04 03:54:48 9
2020/04/04 03:54:48 main.main
2020/04/04 03:54:48 17411178
2020/04/04 03:54:48 /Users/lurongming/main.go
2020/04/04 03:54:48 34
2020/04/04 03:54:48 true
2020/04/04 03:54:48 main.test2
2020/04/04 03:54:48 17410624
2020/04/04 03:54:48 /Users/lurongming/main.go
2020/04/04 03:54:48 13
2020/04/04 03:54:48 true
2020/04/04 03:54:48 main.test

Name of function to obtain files directly from the function pointer:

package main

import (
    "log"
    "reflect"
    "runtime"
)

type Demo struct {
}

func (this *Demo) Hello() {

}

func main() {
    demo := new(Demo)
    pc := reflect.ValueOf(demo.Hello).Pointer()
    f := runtime.FuncForPC(pc)
    log.Println(f.Name())
    log.Println(f.FileLine(pc))
}

The output is:

2020/04/04 03:55:47 main.(*Demo).Hello-fm
2020/04/04 03:55:47 /Users/lurongming/main.go 12
func Callers(skip int, pc []uintptr) int

Callers to the program counter returns to the calling station, placed in a uintptr.

Callers 0 for itself, and the significance of this parameter Caller above is not the same, due to historical reasons. This corresponds to only 1 0 above.

package main

import (
	"fmt"
	"runtime"
)

func printMyName() string {
	pc, _, _, _ := runtime.Caller(1)
	return runtime.FuncForPC(pc).Name()
}
func printCallerName() string {
	pc, _, _, _ := runtime.Caller(2)
	return runtime.FuncForPC(pc).Name()
}

func Foo() {
	fmt.Printf("我是 %s, %s 在调用我!\n", printMyName(), printCallerName())
	Bar()
}
func Bar() {
	fmt.Printf("我是 %s, %s 又在调用我!\n", printMyName(), printCallerName())
	trace()
}
func trace() {
	pc := make([]uintptr, 10) // at least 1 entry needed
	n := runtime.Callers(0, pc)
	for i := 0; i < n; i++ {
		f := runtime.FuncForPC(pc[i])
		file, line := f.FileLine(pc[i])
		fmt.Printf("%s:%d %s\n", file, line, f.Name())
	}
}

func main() {
	Foo()
}

The output is:

我是 main.Foo, main.main 在调用我!
我是 main.Bar, main.Foo 又在调用我!
/usr/local/go/src/runtime/extern.go:211 runtime.Callers
/Users/lurongming/main.go:27 main.trace
/Users/lurongming/main.go:24 main.Bar
/Users/lurongming/main.go:20 main.Foo
/Users/lurongming/main.go:37 main.main
/usr/local/go/src/runtime/proc.go:212 runtime.main
/usr/local/go/src/runtime/asm_amd64.s:1358 runtime.goexit
CallersFrames(callers []uintptr) *Frames

Callers above or just stack the program counter, if you want to get information about the entire stack, you can use CallersFrames function, eliminating the need to traverse call FuncForPC.

The above trace function can be changed in the following manner, the output result is the same:

func trace() {
	pc := make([]uintptr, 10) // at least 1 entry needed
	n := runtime.Callers(0, pc)
	frames := runtime.CallersFrames(pc[:n])
	for {
		frame, more := frames.Next()
		fmt.Printf("%s:%d %s\n", frame.File, frame.Line, frame.Function)
		if !more {
			break
		}
	}
}
Stack acquisition program

In the program panic, the general will take the fight out of the stack, the stack if you want to get the information in the program, you can debug.PrintStack () to print out. For example, do you run a program Error, but do not expect the program panic, just want to print out a stack trace debugging information, you can use debug.PrintStack ().

Or, you read the stack information, own processing and printing:

func DumpStacks() {
	buf := make([]byte, 16384)
	buf = buf[:runtime.Stack(buf, true)]
	fmt.Printf("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)
}

Reference debugging tool: dump goroutine of StackTrace .

Information may also be obtained using the stack of goroutine id, refer to: and then talk about the acquisition method goroutine id .

func GoID() int {
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	id, err := strconv.Atoi(idField)
	if err != nil {
		panic(fmt.Sprintf("cannot get goroutine id: %v", err))
	}
	return id
}
reference

golang log component using runtime.Caller Performance Analysis

Published 349 original articles · won praise 14 · views 90000 +

Guess you like

Origin blog.csdn.net/LU_ZHAO/article/details/105305141