¿Cómo volcar las trazas de pila de goroutine?

Respuestas:

107

Para imprimir el seguimiento de la pila para la goroutine actual , use PrintStack()fromruntime/debug .

PrintStack imprime con error estándar el seguimiento de pila devuelto por Stack.

Por ejemplo:

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

Para imprimir el seguimiento de la pila para todas las rutinas gor, use Lookupy WriteTofrom 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.

Cada perfil tiene un nombre único. Algunos perfiles están predefinidos:

goroutine - seguimientos de pila de todos los goroutines actuales
del montón - una muestra de todas las asignaciones del montón
threadcreate - seguimientos de pila que llevaron a la creación de nuevas hebras de SO
bloquear - seguimientos de pila que llevaron al bloqueo de primitivas de sincronización

Por ejemplo:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
Intermernet
fuente
1
¿Imprime el rastro de pila de todas las goroutines?
Debería, llama Stack. "Stack devuelve un seguimiento de pila formateado de la goroutine que la llama. Para cada rutina, incluye la información de la línea fuente y el valor de la PC, luego intenta descubrir, para las funciones Go, la función o método que llama y el texto de la línea que contiene el invocación."
Intermernet
1
Lo sentimos, solo imprime el seguimiento de la pila de goroutine actual.
4
@HowardGuo He agregado un ejemplo usando runtime / pprof para volcar todos los rastros de pila.
Intermernet
1
Creo que esto solo genera la goroutine que se está ejecutando actualmente en cada hilo, no todas las goroutines, por ejemplo: play.golang.org/p/0hVB0_LMdm
rogerdpack
41

Hay una interfaz HTTP para el runtime/pprofpaquete mencionado en la respuesta de Intermernet. Importe el paquete net / http / pprof para registrar un controlador HTTP para /debug/pprof:

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

Inicie un oyente HTTP si aún no tiene uno:

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

Luego, apunte un navegador a http://localhost:6060/debug/pprofpara un menú, o http://localhost:6060/debug/pprof/goroutine?debug=2para un volcado de pila de goroutine completo.

También hay otras cosas divertidas que puede aprender sobre su código en ejecución de esta manera. Consulte la publicación del blog para ver ejemplos y más detalles: http://blog.golang.org/profiling-go-programs

lnmx
fuente
Lo hice ejecutar solo muestra las goroutines ejecutadas por lo que veo. ¿Hay alguna forma de que pueda ver todos los "métodos" que se ejecutan después del lanzamiento de main.go?
Lukas Lukac
38

Para imitar el comportamiento de Java de stack-dump en SIGQUIT pero aún dejando el programa en ejecución:

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])
    }
}()
Bryan
fuente
4
Creo que esto es lo que el autor realmente buscaba: imita lo que hace Java cuando envías un kill -QUIT. Un pequeño cambio que tuve que hacer fue cambiar la primera línea del bucle for () a: "<- sigs". En otras palabras, simplemente descarte la señal después de esperarla. Las versiones recientes de Go no le permitirán declarar una variable sin usarla posteriormente.
George Armhold
@Bryan, ¿está dispuesto a licenciar esto bajo BSD u otros términos más permisivos adicionales a CC-BY-SA 3.0 requerido por StackOverflow?
Charles Duffy
1
@CharlesDuffy, puede encontrar casi lo mismo aquí con la licencia de Apache: github.com/weaveworks/weave/blob/…
Bryan
37

Al igual que en Java, SIGQUIT se puede utilizar para imprimir un seguimiento de la pila de un programa Go y sus gorutinas.
Sin embargo, una diferencia clave es que, de forma predeterminada, el envío de SIGQUIT a los programas Java no los termina, mientras que los programas Go sí lo hacen.

Este enfoque no requiere ningún cambio de código para imprimir un seguimiento de la pila de todas las rutinas de programas existentes.

La variable de entorno GOTRACEBACK ( consulte la documentación del paquete de tiempo de ejecución ) controla la cantidad de salida generada. Por ejemplo, para incluir todas las goroutines, establezca GOTRACEBACK = all.

La impresión del seguimiento de la pila se desencadena por una condición de tiempo de ejecución inesperada (señal no controlada), originalmente documentada en esta confirmación , que la hace disponible al menos desde Go 1.1.


Alternativamente, si modificar el código fuente es una opción, vea otras respuestas.


Tenga en cuenta que en un terminal Linux, SIGQUIT se puede enviar convenientemente con la combinación de teclas Ctrl+ \.

Rodolfo Carvalho
fuente
5
Mientras revisaba los documentos, no encontré ninguna mención de SIGQUIT, sino de SIGABRT. De mis propias pruebas (con go 1.7), este último también funcionó sobre el primero.
soltysh
4
esta debería ser la mejor respuesta.
Steven Soroka
Los documentos se refieren a "cuando un programa Go falla debido a un pánico no recuperado o una condición de ejecución inesperada". Una señal no detectada (SIGQUIT, etc.) es una de las últimas. ¿Por qué mencioné SIGQUIT? Porque el OP expresa su amor por usar SIGQUIT con Java, y esta respuesta enfatiza la similitud. Reformulando la respuesta para que quede más clara.
Rodolfo Carvalho
26

Puede usar runtime.Stack para obtener el seguimiento de la pila de todas las goroutines:

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

De la documentación:

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

Stack formatea un seguimiento de pila de la goroutine que llama en buf y devuelve el número de bytes escritos en buf. Si todo es cierto, los formatos de pila apilan los rastros de todas las demás goroutines en buf después del rastro de la goroutine actual.

Robert Kajic
fuente
Esto incluye trazas de todas las goroutines, ¡genial!
rogerdpack
¿Es este el formato que un pánico no recuperado se reduce a usar?
Ztyx
2
No olvide agregar una cadena (buf) o imprimirá los bytes sin procesar allí.
Koda
2
¿Quizás estoy haciendo algo mal, o quizás la funcionalidad ha cambiado, pero esto no recupera nada para mí, excepto un segmento vacío de bytes?
17xande
1
@koda no hay necesidad de hacer string(buf)aquí, fmt.Printf("%s", buf)y fmt.Printf("%s", string(buf))haga exactamente lo mismo (vea los documentos para el fmtpaquete); la única diferencia aquí es que la stringversión copiará los bytes bufinnecesariamente
kbolino
20

Presione CTRL + \

(Si lo ejecuta en una terminal y solo quiere matar su programa y volcar las rutinas de ir, etc.)

Encontré esta pregunta buscando la secuencia de teclas. Solo quería una forma rápida y fácil de saber si mi programa tiene una fuga de rutinas :)

Øyvind Skaar
fuente
11

En los sistemas * NIX (incluido OSX) envían una señal de cancelación SIGABRT:

pkill -SIGABRT program_name

Kyle Kloepper
fuente
Aparentemente, enviar SIGQUIT a un proceso Java no lo termina como lo hará SIGABRT.
Dave C
Descubrí que esta es la solución más simple y coincidente a la pregunta original. A menudo, necesita un seguimiento de pila de inmediato, sin cambiar su código.
jotrocken
5

Es necesario usar la longitud devuelta por runtime.Stack()para evitar imprimir un montón de líneas vacías después del seguimiento de la pila. La siguiente función de recuperación imprime una traza muy bien formateada:

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]))
}
David Tootill
fuente
No hay runtime.Trace; runtime.Stack ya se mencionó hace año y medio .
Dave C
Nunca he visto eso; ¿en qué plataforma estás corriendo?
Bryan
¿Qué es lo que no has visto? El código debe ejecutarse en todas las plataformas; Lo probé en Windows 7, Ubuntu 14.04 y Mac.
David Tootill
Nunca he visto líneas vacías.
Bryan
4

De forma predeterminada, presione las ^\teclas ( CTRL + \ ) para volcar los rastros de pila de todas las goroutines.


De lo contrario, para un control más granular, puede usar panic. La forma sencilla a partir de Go 1.6+ :

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

Luego, ejecute su programa así:

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

Si también desea imprimir go runtime goroutines:

$ GOTRACEBACK=system go run main.go

Aquí están todas las opciones de GOTRACEBACK:

  • GOTRACEBACK=none omite por completo los seguimientos de la pila de goroutine.
  • GOTRACEBACK=single (el predeterminado) se comporta como se describe arriba.
  • GOTRACEBACK=all agrega seguimientos de pila para todas las goroutines creadas por el usuario.
  • GOTRACEBACK=system es como "todos" pero agrega marcos de pila para funciones en tiempo de ejecución y muestra las rutinas creadas internamente por el tiempo de ejecución.
  • GOTRACEBACK=crashes como `` sistema '' pero se bloquea de una manera específica del sistema operativo en lugar de salir. Por ejemplo, en los sistemas Unix, el bloqueo se produce SIGABRTpara desencadenar un volcado del núcleo.

Aquí está la documentación

La variable GOTRACEBACK controla la cantidad de salida generada cuando un programa Go falla debido a un pánico no recuperado o una condición de tiempo de ejecución inesperada.

De forma predeterminada, una falla imprime un seguimiento de pila para la goroutine actual, eliminando las funciones internas del sistema de tiempo de ejecución y luego sale con el código de salida 2. La falla imprime rastros de pila para todas las goroutines si no hay ninguna goroutine actual o la falla es interno al tiempo de ejecución.

Por razones históricas, los valores 0, 1 y 2 de GOTRACEBACK son sinónimos de none, all y system, respectivamente.

La función SetTraceback del paquete en tiempo de ejecución / depuración permite aumentar la cantidad de salida en tiempo de ejecución, pero no puede reducir la cantidad por debajo de la especificada por la variable de entorno. Consulte https://golang.org/pkg/runtime/debug/#SetTraceback .

Inanc Gumus
fuente