【Golang | runtime】runtime.Caller和runtime.Callers的使用和区别

环境:
go version go1.18.2

1、runtime.Caller

函数func runtime.Caller(skip int) (pc uintptr, file string, line int, ok bool)

作用

获取函数Caller的调用信息

参数

  • skip: =0时,返回调用Caller函数A的pc(program counter)、所在文件名以及Caller函数A中的行数;=1时, 返回调用函数A函数B的pc、所在文件名以及函数A函数B中的行数;以此类推
  • pc: 程序计数器,指向下一条将要取指的指令地址
  • file: pc对应的函数所在文件
  • line: 被调用方在pc对应的函数中的行数

e.g.

项目结构如下:

【Golang | runtime】runtime.Caller和runtime.Callers的使用和区别_第1张图片

tool.tool.go

package tool

import (
	"bytes"
	"fmt"
	"runtime"
)

func CallerTest() {
	for i := 0; i <= 4; i++ {
		pc, file, line, ok := runtime.Caller(i)
		if ok {
			fmt.Printf("当i:=%d时:\n调用者的pc:%v\n调用者的函数名:%s\n"+
				"调用者所在file:%s\n被调用者在调用者中的line:%d\n", i, pc, runtime.FuncForPC(pc).Name(), file, line)
			fmt.Println(string(bytes.Repeat([]byte("*"), 10)))
		}
	}
}

main.go

package main

import "caller/tool"

func main() {
	tool.CallerTest()
}

运行结果如下:

PS E:\goland-workspace\GolangLearning\Common\caller> go run .\main.go
当i:=0时:
调用者的pc:15191590
调用者的函数名:caller/tool.CallerTest
调用者所在file:E:/goland-workspace/GolangLearning/Common/caller/tool/tool.go
被调用者在调用者中的line:11
**********
当i:=1时:
调用者的pc:15192214
调用者的函数名:main.main
调用者所在file:E:/goland-workspace/GolangLearning/Common/caller/main.go
被调用者在调用者中的line:6
**********
当i:=2时:
调用者的pc:14832765
调用者的函数名:runtime.main
调用者所在file:D:/golang/install/src/runtime/proc.go
被调用者在调用者中的line:250
********** 	
当i:=3时:
调用者的pc:14990688
调用者的函数名:runtime.goexit
调用者所在file:D:/golang/install/src/runtime/asm_amd64.s
被调用者在调用者中的line:1571
**********

2. runtime.Callers

函数func runtime.Callers(skip int, pc []uintptr) int

作用

类似函数Caller,可以在入参pc中填充Callers所有的调用信息(前提是pc切片空间足够)

参数

  • skip:区别于Caller,skip=0时,代表Callers函数本身,skip=1时,代表调用Callers的函数
  • pc:存储Callers所有的调用方pc

e.g.

项目结构如下:

【Golang | runtime】runtime.Caller和runtime.Callers的使用和区别_第2张图片

tool.tool.go

package tool

import (
	"fmt"
	"runtime"
)

func CallersTest() {
	pc := make([]uintptr, 32)
	n := runtime.Callers(0, pc)
	cf := runtime.CallersFrames(pc[:n])
	for {
		frame, more := cf.Next()
		if !more {
			break
		} else {
			fmt.Println(frame)
		}
	}

}

main.go

package main

import "callers/tool"

func main() {
	tool.CallersTest()
}

运行结果如下:

PS E:\goland-workspace\GolangLearning\Common\callers> go run .\main.go
{11711796 <nil> runtime.Callers D:/golang/install/src/runtime/extern.go 235 11711712 {0xbc2550 0xbc8a00}}
{11711778 0xbc2550 callers/tool.CallersTest E:/goland-workspace/GolangLearning/Common/callers/tool/tool.go 10 11711712 {0xbc2550 0xbc8a00}}
{11712246 0xbc2598 main.main E:/goland-workspace/GolangLearning/Common/callers/main.go 6 11712224 {0xbc2598 0xbc8a00}}
{11359357 0xbb2370 runtime.main D:/golang/install/src/runtime/proc.go 250 11358848 {0xbb2370 0xbc8a00}}

注:

  • 需要借助函数runtime.CallersFrames来获取Callers的调用关系,不能直接根据runtime.Callers(0, pc)中pc的值来获取,否则获取的调用顺序可能不正确
  • 可以看到当skip=0时,pc[0]指向Callers函数本身,pc[1]指向调用Callers的函数callers/tool.CallersTest,以此类推

你可能感兴趣的:(golang,开发语言,后端)