【问题标题】:How to dump goroutine stacktraces?如何转储 goroutine 堆栈跟踪?
【发布时间】:2013-10-06 07:25:13
【问题描述】:

我有 Java 背景,我喜欢使用信号 QUIT 来检查 Java 线程转储。

如何让 Golang 打印出所有的 goroutine 堆栈跟踪?

【问题讨论】:

标签: go


【解决方案1】:

要打印 current goroutine 的堆栈跟踪,请使用 PrintStack() from runtime/debug

PrintStack 将 Stack 返回的堆栈跟踪打印到标准错误。

例如:

import(
   "runtime/debug"
)
...    
debug.PrintStack()

要打印 所有 goroutines 的堆栈跟踪,请使用来自runtime/pprofLookupWriteTo

func Lookup(name string) *Profile
// Lookup returns the profile with the given name,
// or nil if no such profile exists.

func (p *Profile) WriteTo(w io.Writer, debug int) error
// WriteTo writes a pprof-formatted snapshot of the profile to w.
// If a write to w returns an error, WriteTo returns that error.
// Otherwise, WriteTo returns nil.

每个配置文件都有一个唯一的名称。预定义了一些配置文件:

goroutine - 所有当前 goroutines 的堆栈跟踪
heap - 所有堆分配的采样
threadcreate - 导致创建新操作系统线程的堆栈跟踪
block - 导致同步原语阻塞的堆栈跟踪

例如:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)

【讨论】:

  • 是否打印所有 goroutine 的堆栈跟踪?
  • 应该,它调用Stack。 “堆栈返回调用它的 goroutine 的格式化堆栈跟踪。对于每个例程,它包括源代码行信息和 PC 值,然后尝试为 Go 函数发现调用函数或方法以及包含调用。”
  • 对不起,它只打印当前的 goroutine 堆栈跟踪。
  • @HowardGuo 我添加了一个使用 runtime/pprof 转储所有堆栈跟踪的示例。
  • 我认为这只会输出每个线程当前正在运行的 goroutine,而不是 所有 goroutine,例如:play.golang.org/p/0hVB0_LMdm
【解决方案2】:

Intermernet 的回答中提到了 runtime/pprof 包的 HTTP 前端。导入net/http/pprof包为/debug/pprof注册一个HTTP处理程序:

import _ "net/http/pprof"
import _ "net/http"

如果您还没有 HTTP 侦听器,请启动一个:

go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

然后将浏览器指向http://localhost:6060/debug/pprof 以获取菜单,或http://localhost:6060/debug/pprof/goroutine?debug=2 以获取完整的 goroutine 堆栈转储。

您还可以通过这种方式了解有关正在运行的代码的其他有趣内容。查看博客文章以获取示例和更多详细信息: http://blog.golang.org/profiling-go-programs

【讨论】:

  • 我让它运行它只显示我所看到的执行的 goroutines。有什么方法可以看到 main.go 启动后执行的所有“方法”?
【解决方案3】:

模拟 SIGQUIT 上堆栈转储的 Java 行为,但仍让程序运行:

go func() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGQUIT)
    buf := make([]byte, 1<<20)
    for {
        <-sigs
        stacklen := runtime.Stack(buf, true)
        log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
    }
}()

【讨论】:

  • 我认为这正是作者真正想要的——模仿 Java 在发送 kill -QUIT 时所做的事情。我必须做的一个小改动是将 for() 循环的第一行更改为:“
  • @Bryan,您是否愿意根据 BSD 或 StackOverflow 要求的 CC-BY-SA 3.0 之外的其他更宽松的条款许可这个?
  • @CharlesDuffy 你可以在 Apache 许可证下找到很多相同的东西:github.com/weaveworks/weave/blob/…
  • 如果收到 os.Interupt 信号,我稍微改进了打印调试,如果第二个信号快速进入(play.golang.org/p/dWgWrDFBOth
【解决方案4】:

与 Java 类似,SIGQUIT 可用于打印 Go 程序及其 goroutines 的堆栈跟踪。
然而,一个关键的区别是,默认情况下,向 Java 程序发送 SIGQUIT 不会终止它们,而 Go 程序会退出。

这种方法不需要更改代码即可打印现有程序的所有 goroutine 的堆栈跟踪。

环境变量 GOTRACEBACK (see documentation of the runtime package) 控制生成的输出量。例如,要包含所有 goroutine,请设置 GOTRACEBACK=all。

堆栈跟踪的打印是由意外的运行时条件(未处理的信号)触发的,最初记录在 this commit 中,至少从 Go 1.1 开始就可用。


或者,如果可以选择修改源代码,请参阅其他答案。


请注意,在 Linux 终端中,可以使用组合键 Ctrl+\ 方便地发送 SIGQUIT。

【讨论】:

  • 在查看文档时,我没有发现任何关于 SIGQUIT 的内容,而是 SIGABRT。根据我自己的测试(使用 go 1.7),后者也优于前者。
  • 这应该是最佳答案。
  • 文档指的是“当 Go 程序由于未恢复的恐慌或意外的运行时条件而失败时”。未捕获的信号(SIGQUIT 等)是后者之一。为什么我提到了 SIGQUIT?因为 OP 表达了他们对在 Java 中使用 SIGQUIT 的喜爱,而这个答案强调了相似性。改写答案以使其更清晰。
【解决方案5】:

您可以使用runtime.Stack 获取所有goroutines的堆栈跟踪:

buf := make([]byte, 1<<16)
runtime.Stack(buf, true)
fmt.Printf("%s", buf)

来自文档:

func Stack(buf []byte, all bool) int

Stack 将调用 goroutine 的堆栈跟踪格式化为 buf 和 返回写入 buf 的字节数。如果一切都是真的,堆栈 在跟踪之后将所有其他 goroutine 的堆栈跟踪格式化为 buf 对于当前的 goroutine。

【讨论】:

  • 这包括来自 所有 goroutine 的回溯,很好!
  • 不要忘记添加字符串(buf),否则您将在那里打印原始字节。
  • 也许我做错了什么,或者功能已经改变,但这并没有为我检索到任何内容,除了一个空的字节片?
  • @koda 这里不需要string(buf)fmt.Printf("%s", buf)fmt.Printf("%s", string(buf)) 做同样的事情(参见fmt 包的文档);这里唯一的区别是string 版本将不必要地从buf 复制字节
  • 请注意 runtime.Stack 返回它在 buf 中实际写入的字节数,因此您应该在打印时手动将 buf 切片到该长度,以避免写入一堆 0 字节(这可能会被终端忽略,但会显示在输出重定向到的文件中)
【解决方案6】:

CTRL+\

(如果您在终端中运行它并且只想杀死您的程序并转储 go 例程等)

我发现这个问题正在寻找关键序列。只是想要一种快速简便的方法来判断我的程序是否泄漏了 goroutine :)

【讨论】:

    【解决方案7】:

    在 *NIX 系统(包括 OSX)上发送信号 abort SIGABRT

    pkill -SIGABRT program_name

    【讨论】:

    • 显然,像 SIGABRT 一样将 SIGQUIT 发送到 Java 进程 does not terminate it
    • 我发现这是对原始问题最简单和最匹配的解决方案。通常您需要立即进行堆栈跟踪,而无需更改代码。
    【解决方案8】:

    默认情况下,按^\ 键(CTRL+\)转储所有 goroutine 的堆栈跟踪。


    否则,要进行更精细的控制,您可以使用panicGo 1.6+的简单方法:

    go func() {
        s := make(chan os.Signal, 1)
        signal.Notify(s, syscall.SIGQUIT)
        <-s
        panic("give me the stack")
    }()
    

    然后,像这样运行你的程序:

    # Press ^\ to dump the stack traces of all the user-created goroutines
    $ GOTRACEBACK=all go run main.go
    

    如果你还想打印 go runtime goroutines:

    $ GOTRACEBACK=system go run main.go
    

    以下是所有 GOTRACEBACK 选项:

    • GOTRACEBACK=none 完全省略了 goroutine 堆栈跟踪。
    • GOTRACEBACK=single (默认) 的行为如上所述。
    • GOTRACEBACK=all 为所有用户创建的 goroutine 添加堆栈跟踪。
    • GOTRACEBACK=systemall 类似,但为运行时函数添加了堆栈帧,并显示了运行时内部创建的 goroutine。
    • GOTRACEBACK=crash 类似于 system,但以特定于操作系统的方式崩溃而不是退出。例如,在 Unix 系统上,崩溃引发 SIGABRT 以触发核心转储。

    Here is the documentation

    GOTRACEBACK 变量控制当 Go 程序由于未恢复的恐慌或意外的运行时条件而失败时生成的输出量。

    默认情况下,失败会打印当前 goroutine 的堆栈跟踪,省略运行时系统内部的函数,然后以退出代码 2 退出。如果没有当前 goroutine,则失败会打印所有 goroutine 的堆栈跟踪,或者故障是运行时内部的。

    由于历史原因,GOTRACEBACK 设置 0、1 和 2 分别是 none、all 和 system 的同义词。

    runtime/debug 包的 SetTraceback 函数允许在运行时增加输出量,但它不能减少低于环境变量指定的量。见https://golang.org/pkg/runtime/debug/#SetTraceback

    【讨论】:

      【解决方案9】:

      必须使用runtime.Stack() 返回的长度以避免在堆栈跟踪之后打印一堆空行。下面的恢复函数打印出格式良好的跟踪:

      if r := recover(); r != nil {
          log.Printf("Internal error: %v", r))
          buf := make([]byte, 1<<16)
          stackSize := runtime.Stack(buf, true)
          log.Printf("%s\n", string(buf[0:stackSize]))
      }
      

      【讨论】:

      • 没有runtime.Trace; runtime.Stackwas already mentioned a year and a half ago.
      • 我从未见过;你在哪个平台上运行?
      • 什么是你没见过的?代码应该在所有平台上运行;我已经在 Windows 7、Ubuntu 14.04 和 Mac 上对其进行了测试。
      • 从未见过空行。
      【解决方案10】:

      你可以用这个:

      kill -3 YOUR_PROCESS_PID_ID
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-01-02
        • 1970-01-01
        • 1970-01-01
        • 2011-09-29
        • 2017-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多