【问题标题】:Stack trace of go routine from imported package?来自导入包的 goroutine 的堆栈跟踪?
【发布时间】:2016-09-12 22:47:53
【问题描述】:

如何获取最后一个(理想情况下)go 例程(应用程序有多个 go 例程)的堆栈跟踪,该例程恐慌并恢复并且只记录了一条描述性错误消息?我不知道哪个程序恢复了。另外,请记住,我不会更改任何导入包的代码。这种恐慌发生在一些导入的包中,它创建了多个 go 例程,所以我需要一种方法来获取最后恢复的例程的堆栈跟踪,以便找到它恐慌的位置。

【问题讨论】:

  • 为什么不能修改包码?如果你能构建程序,你就有了代码。
  • 我会尝试,但我要求一种更方便的方法来从主程序获取跟踪:)
  • 如果代码从恐慌中恢复,您不会看到恐慌。您可以尝试在调试器中设置断点,但鉴于调试器的状态,注释掉 recover 并获取堆栈跟踪可能要快得多。

标签: go


【解决方案1】:

简短的回答是:不可能,但有例外

Golang 有一些堆栈控制方法和类型。

您可以使用runtime/debug/SetTraceback控制堆栈级别

func SetTraceback(level string)

SetTraceback 设置运行时打印的详细信息量 在回溯中它在退出之前打印,因为 未恢复的恐慌或内部运行时错误。 level 参数采用与 GOTRACEBACK 相同的值 环境变量。例如, SetTraceback("all") 确保 程序在崩溃时会打印所有的 goroutine。
有关详细信息,请参阅包运行时文档。如果 SetTraceback 的调用级别低于 环境变量,调用被忽略。

您也可以使用runtime/debug/Stack 打印堆栈跟踪

func Stack() []byte

Stack 返回调用它的 goroutine 的格式化堆栈跟踪。它使用足够大的缓冲区调用 runtime.Stack 来捕获整个跟踪。

您还需要了解内置函数recover 的工作原理。

recover 内置函数允许程序管理 恐慌的 goroutine。在 deferred 中执行调用以恢复 函数(但不是它调用的任何函数)停止恐慌序列 通过恢复正常执行并检索传递给 恐慌的呼唤。如果在延迟函数之外调用recover,它将 不要停止恐慌的序列。在这种情况下,或者当 goroutine 不是 恐慌,或者如果提供给恐慌的参数为零,恢复返回 零。因此,recover 的返回值报告了 goroutine 是否是 惊慌失措。

func recover() interface{}

工作示例

这个例子假设包没有调用recover(在另一部分详细说明)。

Golang Playground Link

package main

import (
    "log"
    "errors"
    "runtime/debug"
    "time"
)

func f2() {
    panic(errors.New("oops")) // line 11
}

func f1() {
    f2() // line 15
}

func main() {
    defer func() {
        if e := recover(); e != nil {
            log.Printf("%s: %s", e, debug.Stack()) // line 20
        }
    }()

    go f1() // line 25

    time.Sleep(time.Second * 1)
}

如果包调用恢复

如果代码正在从恐慌中恢复,您需要使用调试器或删除 recover 以了解发生了什么,如下例所示,该示例表明已恢复的恐慌无法再次“恢复”。

Golang Playground Link

package main

import (
    "log"
    "errors"
    "runtime/debug"
    "time"
)

func f2() {
    panic(errors.New("oops")) // line 11
}

func f1() {
    defer func() {
        if e := recover(); e != nil {
            log.Printf("internal %s: %s", e, debug.Stack()) // line 20
        }
    }()
    f2() // line 15
}

func main() {
    defer func() {
        if e := recover(); e != nil {
            log.Printf("external %s: %s", e, debug.Stack()) // line 20
        } else {
            log.Println("Nothing to print")
        }
    }()

    go f1() // line 25

    time.Sleep(time.Second * 1)
}

少两害

使用Delve 调试或临时编辑包,以便记录完整消息(一旦了解,您可以恢复更改)。

此外,如果您发现问题,请告知包作者以便修复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-06
    • 2017-02-21
    • 1970-01-01
    • 2012-02-16
    • 2023-03-14
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    相关资源
    最近更新 更多