debugging Go打印从runtime.Callers()返回的程序计数器的不同函数名,如果我使用errors包

cygmwpex  于 2023-05-07  发布在  Go
关注(0)|答案(1)|浏览(82)

代码1:

package main

import (
    "fmt"
    "github.com/pkg/errors"
    "runtime"
)

func main() {
    ptrs, _ := Test1("arg1", "arg2")
    for _, pc := range *ptrs {
        f := runtime.FuncForPC(pc)
        if f == nil {
            continue
        }
        filename, line := f.FileLine(pc)
        fmt.Printf("func name: %s, entry: %x, filename: %s, line: %d\n", f.Name(), f.Entry(), filename, line)
    }
}

func Test0(args ...interface{}) *[]uintptr {
    const depth = 32
    var pcs [depth]uintptr
    n := runtime.Callers(0, pcs[:])
    st := pcs[0:n]
    return &st
}

func Test1(args ...interface{}) (*[]uintptr, error) {
    return Test0(args...), errors.New("Test")
}

代码2:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    ptrs, _ := Test1("arg1", "arg2")
    for _, pc := range *ptrs {
        f := runtime.FuncForPC(pc)
        if f == nil {
            continue
        }
        filename, line := f.FileLine(pc)
        fmt.Printf("func name: %s, entry: %x, filename: %s, line: %d\n", f.Name(), f.Entry(), filename, line)
    }
}

func Test0(args ...interface{}) *[]uintptr {
    const depth = 32
    var pcs [depth]uintptr
    n := runtime.Callers(0, pcs[:])
    st := pcs[0:n]
    return &st
}

func Test1(args ...interface{}) (*[]uintptr, error) {
    return Test0(args...), nil//, errors.New("Test")
}

唯一不同的部分是返回错误值Test1()。* * code 1**返回erors.New("Test")code 2返回nil。在main函数的FOR循环中,程序打印函数名,结果不同:

code 1的结果:

func name: runtime.Callers, entry: 6a20c0, filename: C:/Program Files/Go/src/runtime/extern.go, line: 247
func name: runtime.Callers, entry: 6a20c0, filename: C:/Program Files/Go/src/runtime/extern.go, line: 247
func name: main.Test1, entry: 6a2180, filename: D:/develop/go-testbed/cmd/slicetest/slicetest.go, line: 30
func name: main.main, entry: 6a1ea0, filename: D:/develop/go-testbed/cmd/slicetest/slicetest.go, line: 11
func name: runtime.main, entry: 6476a0, filename: C:/Program Files/Go/src/runtime/proc.go, line: 259
func name: runtime.goexit, entry: 66eee0, filename: C:/Program Files/Go/src/runtime/asm_amd64.s, line: 1595

代码2的结果

func name: runtime.Callers, entry: b30020, filename: C:/Program Files/Go/src/runtime/extern.go, line: 247
func name: runtime.Callers, entry: b30020, filename: C:/Program Files/Go/src/runtime/extern.go, line: 247
func name: main.main, entry: b2fe00, filename: D:/develop/go-testbed/cmd/slicetest/slicetest.go, line: 10
func name: main.main, entry: b2fe00, filename: D:/develop/go-testbed/cmd/slicetest/slicetest.go, line: 9
func name: runtime.main, entry: ad6fc0, filename: C:/Program Files/Go/src/runtime/proc.go, line: 259
func name: runtime.goexit, entry: afe580, filename: C:/Program Files/Go/src/runtime/asm_amd64.s, line: 1595

如您所见,code 1打印了正确的函数名main.Test1,但code 2没有。我读了runtime.Callers print different program counters depending on where its run from,它重新命令使用runtime.CallersFrames(),但我认为这不是问题,因为code 1code 2都不使用它。我也看到了errors.New()函数,但我没有发现什么特别的东西。它通过caller()函数创建callstack,这与我代码中的Test0()几乎相同-实际上,我从caller()复制了Test0()的内容。
有人能告诉我是什么原因造成的吗?
Go版本:go1.19 windows/amd 64
操作系统:Windows XP
github.com/pkg/errors版本:版本0.9.1
如果我在Goland IDE的调试模式下运行这两个代码,则使用main.Test1打印相同的结果。
谢谢大家!

qni6mghb

qni6mghb1#

从runtime.Callers的文档中
要将这些PC转换为符号信息,如函数名和行号,使用CallersFrames。CallersFrames考虑内联函数并将返回程序计数器调整为调用程序计数器。不鼓励直接在返回的PC片上迭代,因为在任何返回的PC上使用FuncForPC都不能解释内联或返回程序计数器调整。
(emph你不能试图解释运行时的结果。你自己调用,因为你不能正确地做。你的错误尝试清楚地证明了文档是正确的。
这实际上与www.example.com无关github.com/pkg/errors(无论如何,它都是未维护的,并且在包错误中被新的东西超级播种)。这是编译器优化代码的结果(调试时不同)。
经验法则:阅读你所使用的函数的文档,并按照文档的指示去做。这是“有时在实验室里呆3个月可以让你在图书馆里呆6个小时”的一个例子。

相关问题