如何在不终止Go进程的情况下转储其所有堆栈?

huangapple go评论85阅读模式
英文:

How can I dump all a Go process's stacks without killing it?

问题

一个Go进程正在运行。我想要:

  1. 对每个goroutine进行堆栈跟踪
  2. 从外部进行操作,而不依赖于我添加到源代码中的任何内容
  3. 不杀死该进程。

我该如何做到这一点?

这应该很简单 - 这个功能已经被请求:https://code.google.com/p/go/issues/detail?id=2516 并且根据该线程的结论,已经实现了。那是两年多以前的事了。但是无论是问题线程还是提交中都没有提到如何调用这个功能的任何提示。

该功能请求中提到了SIGQUIT作为JVM接受的信号来调用相应的功能。但SIGQUIT不是答案;至少在go1.2中,SIGQUIT会执行#1和#2,但也会终止进程。

有人在这里之前问过一个相关的问题:https://stackoverflow.com/questions/19094099/how-to-dump-goroutine-stacktrace
但他们没有明确要求#2或#3,所有答案都不符合#2,并且他们接受了一个不符合#2的答案。所以这是一个不同的问题。

英文:

A Go process is running. I want to

  1. dump a stack trace for each of its goroutines
  2. from the outside, without depending on anything I add to its source code
  3. without killing it.

How can I do that?

This should be easy -- the feature was requested: https://code.google.com/p/go/issues/detail?id=2516 and, according to the conclusion of that thread, implemented. That was over two years ago. But neither the issue thread nor the commit contains any hint as to how to invoke this feature.

The feature request mentioned SIGQUIT as the signal the JVM accepts to invoke the corresponding feature there. But SIGQUIT isn't the answer; on go1.2 at least, SIGQUIT does #1 and #2 but also kills the process.

Someone asked a related question here a while ago: https://stackoverflow.com/questions/19094099/how-to-dump-goroutine-stacktrace
but they didn't clearly ask for #2 or #3, none of the answers meet #2, and they accepted an answer that doesn't meet #2. So this is a different question.

答案1

得分: 14

你可以使用类似以下代码来设置一个处理程序:

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

func main() {
	sigChan := make(chan os.Signal)
	go func() {
		stacktrace := make([]byte, 8192)
		for _ = range sigChan {
			length := runtime.Stack(stacktrace, true)
			fmt.Println(string(stacktrace[:length]))
		}
	}()
	signal.Notify(sigChan, syscall.SIGQUIT)

	// ...
}

现在,SIGQUIT信号将被捕获并发送到指定的通道。然后,runtime.Stack函数用于将堆栈跟踪格式化为预先准备好的缓冲区(如果它大于缓冲区,则会被截断),然后打印出来。

英文:

You can set up a handler like that using code something like this:

import (
	"fmt"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

func main() {
	sigChan := make(chan os.Signal)
	go func() {
		stacktrace := make([]byte, 8192)
		for _ = range sigChan {
			length := runtime.Stack(stacktrace, true)
			fmt.Println(string(stacktrace[:length]))
		}
	}()
	signal.Notify(sigChan, syscall.SIGQUIT)


	...
}

The SIGQUIT signal will now be caught and sent to the given channel. The runtime.Stack function is then used to format the stack trace into a prepared buffer (if it is larger than the buffer, it will be truncated), and then printed.

答案2

得分: 6

如果你正在使用net/http,你可以通过调试处理程序访问goroutines。如果你查看以下源代码:

http://golang.org/src/pkg/runtime/pprof/pprof.go

你会在第62行看到一个名为goroutineProfile的profile。这个profile通过writeGoroutine进行输出。如果以debug >= 2的方式调用writeGoroutine,它将输出所有的goroutines。

你可以使用curl http://localhost:<port>/debug/pprof/goroutine?debug=2来获取所有的goroutines。不幸的是,我没有看到任何调用该代码的信号处理程序的引用,但你可以查看上述源代码中pprof如何使用runtime.Stack来自己实现这个功能。

英文:

If you are using net/http you can access the goroutines via the debug handlers. If you look at the following source

http://golang.org/src/pkg/runtime/pprof/pprof.go

You will see the profile, goroutineProfile, on line 62. This profile writes out via writeGoroutine. If writeGoroutine is called with debug >= 2 then it will write out all goroutines.

You should be able to curl http://localhost:&lt;port&gt;/debug/pprof/goroutine?debug=2 to get all goroutines dumped. Unfortunately I didn't see any references to signal handlers that would call that code but you can look at references to how pprof makes use of runtime.Stack in the source above to implement this yourself pretty easily.

huangapple
  • 本文由 发表于 2014年4月29日 11:14:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/23354810.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定