我有 Java 背景,我喜欢使用信号 QUIT 来检查 Java 线程转储。
如何让 Golang 打印出所有 goroutines 堆栈跟踪?
To print the stack trace for the current goroutine, use PrintStack()
from runtime/debug
.
PrintStack prints to standard error the stack trace returned by Stack.
For example:
import(
"runtime/debug"
)
...
debug.PrintStack()
To print the stack trace for all goroutines use Lookup
and WriteTo
from runtime/pprof
.
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.
Each Profile has a unique name. A few profiles are predefined:
goroutine - stack traces of all current goroutines
heap - a sampling of all heap allocations
threadcreate - stack traces that led to the creation of new OS threads
block - stack traces that led to blocking on synchronization primitives
For example:
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
runtime/pprof
Intermernet 的答案中提到的包有一个 HTTP 前端。导入net/http/pprof包以注册 HTTP 处理程序/debug/pprof
:
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
为了模仿 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 类似,SIGQUIT 可用于打印 Go 程序及其 goroutines 的堆栈跟踪。
然而,一个关键的区别是,默认情况下,向 Java 程序发送 SIGQUIT 不会终止它们,而 Go 程序会退出。
这种方法不需要更改代码即可打印现有程序的所有 goroutine 的堆栈跟踪。
环境变量 GOTRACEBACK(参见运行时包的文档)控制生成的输出量。例如,要包含所有 goroutine,设置 GOTRACEBACK=all。
堆栈跟踪的打印是由意外的运行时条件(未处理的信号)触发的,最初记录在这个 commit中,至少从 Go 1.1 开始就可以使用。
或者,如果可以选择修改源代码,请参阅其他答案。
请注意,在 Linux 终端中,可以使用组合键Ctrl+方便地发送 SIGQUIT \。
您可以使用runtime.Stack获取所有 goroutine 的堆栈跟踪:
buf := make([]byte, 1<<16)
runtime.Stack(buf, true)
fmt.Printf("%s", buf)
从文档中:
func Stack(buf []byte, all bool) int
Stack 将调用 goroutine 的堆栈跟踪格式化为 buf 并返回写入 buf 的字节数。如果一切为真,Stack 会将所有其他 goroutine 的堆栈跟踪格式化为当前 goroutine 的跟踪之后的 buf。
按CTRL+\
(如果您在终端中运行它并且只想杀死您的程序并转储 go 例程等)
我发现这个问题正在寻找关键序列。只是想要一种快速简便的方法来判断我的程序是否泄漏了 goroutine :)
在 *NIX 系统(包括 OSX)上发送信号 abort SIGABRT
:
pkill -SIGABRT program_name
默认情况下,^\
按键(CTRL+\)转储所有 goroutine 的堆栈跟踪。
否则,要进行更精细的控制,您可以使用panic
. Go 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=system
类似all
,但为运行时函数添加了堆栈帧,并显示了运行时内部创建的 goroutine。GOTRACEBACK=crash
就像system
但以特定于操作系统的方式崩溃而不是退出。例如,在 Unix 系统上,崩溃引发SIGABRT
以触发核心转储。GOTRACEBACK 变量控制当 Go 程序由于未恢复的恐慌或意外的运行时条件而失败时生成的输出量。
默认情况下,失败打印当前 goroutine 的堆栈跟踪,省略运行时系统内部的函数,然后以退出代码 2 退出。如果没有当前 goroutine 或失败是失败,则失败打印所有 goroutine 的堆栈跟踪运行时的内部。
由于历史原因,GOTRACEBACK 设置 0、1 和 2 分别是 none、all 和 system 的同义词。
runtime/debug 包的 SetTraceback 函数允许在运行时增加输出量,但它不能减少低于环境变量指定的量。请参阅https://golang.org/pkg/runtime/debug/#SetTraceback。
有必要使用返回的长度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]))
}
你可以使用这个:
kill -3 YOUR_PROCESS_PID_ID