如何转储 goroutine 堆栈跟踪?

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

如何让戈兰打印出所有的 Goroutines 堆栈跟踪?

124201 次浏览

要打印 目前 goroutine 的堆栈跟踪,请使用 ABC1呼叫 PrintStack()

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 的 goroutine 堆栈跟踪
Heap-所有堆分配的抽样
导致创建新操作系统线程的 threadcreate-stack 跟踪
导致同步原语阻塞的块堆栈跟踪

例如:

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

在 Internet 的回答中提到的 runtime/pprof包有一个 HTTP 前端。导入 网址/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作为完整的常规堆栈转储。

您还可以通过这种方式了解有关运行代码的其他有趣的事情。看看这篇博客文章中的例子和更多细节: Http://blog.golang.org/profiling-go-programs

您可以使用 运行时,堆栈获取所有 goroutine 的堆栈跟踪:

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

根据文件:

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

Stack 将调用 goroutine 的堆栈跟踪格式化为 buf 和 返回写入缓冲区的字节数 在跟踪之后,将所有其他 goroutine 的跟踪堆栈到 buf 中 现在的舞蹈。

为了在 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])
}
}()

有必要使用 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]))
}

在 * NIX 系统(包括 OSX)上发送中止 SIGABRT的信号:

pkill -SIGABRT program_name

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

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

环境变量 GOTRACEback (请参阅运行时包的文档)控制生成的输出量,例如,为了包含所有 goroutine,设置 GOTRACEback = all。

堆栈跟踪的打印由一个意外的运行时条件(未处理的信号)触发,该条件最初在 这个承诺中记录,至少从 Go 1.1开始就可以使用。


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


注意,在 Linux 终端中,SIGQUIT 可以方便地与键组合 Ctrl + \一起发送。

CTRL +

(如果你在一个终端运行它,只是想杀死你的程序和转储去例程等)

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

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


否则,为了更细粒度的控制,你可以使用 panic:

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

如果你还想打印到运行时常规:

$ GOTRACEBACK=system go run main.go

以下是 GOTRACEback 的所有选项:

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

这是文件

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

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

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

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

你可以用这个:

kill -3 YOUR_PROCESS_PID_ID