Como fazer o dump de stacktraces goroutine?

Respostas:

103

Para imprimir o rastreamento de pilha da goroutine atual , use PrintStack()deruntime/debug .

PrintStack imprime em erro padrão o rastreamento de pilha retornado por Stack.

Por exemplo:

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

Para imprimir o rastreamento de pilha para todos os goroutines, use Lookupe WriteTode 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 possui um nome único. Alguns perfis são predefinidos:

goroutine - rastreamentos de pilha de todos os
heap goroutines atuais - uma amostra de todas as alocações de heap
threadcreate - rastreamentos de pilha que levaram à criação de novos threads de sistema operacional
bloco - rastreamentos de pilha que levaram ao bloqueio de primitivos de sincronização

Por exemplo:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
Internet
fonte
1
Ele imprime rastreamento de pilha de todos os goroutines?
Deveria, ele chama Stack. "Stack retorna um rastreamento de pilha formatado da goroutine que a chama. Para cada rotina, inclui as informações da linha de origem e o valor do PC, em seguida, tenta descobrir, para funções Go, a função ou método de chamada e o texto da linha que contém o invocação."
Intermernet
1
Desculpe, ele imprime apenas o rastreamento de pilha goroutine atual.
4
@HowardGuo Eu adicionei um exemplo usando runtime / pprof para despejar todos os rastreamentos de pilha.
Intermernet
1
Acho que isso gera apenas goroutine de cada thread atualmente em execução, nem todos os goroutines, ex: play.golang.org/p/0hVB0_LMdm
rogerdpack
39

Existe um frontend HTTP para o runtime/pprofpacote mencionado na resposta da Intermernet. Importe o pacote net / http / pprof para registrar um manipulador HTTP para /debug/pprof:

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

Inicie um ouvinte HTTP, se ainda não tiver um:

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

Em seguida, aponte um navegador http://localhost:6060/debug/pprofpara um menu ou http://localhost:6060/debug/pprof/goroutine?debug=2para um despejo de pilha goroutine completo.

Existem outras coisas divertidas que você pode aprender sobre o código em execução dessa maneira também. Confira a postagem do blog para exemplos e mais detalhes: http://blog.golang.org/profiling-go-programs

lnmx
fonte
Eu o fiz rodar por ele mostra apenas os goroutines executados até onde eu vejo. Existe alguma maneira de ver todos os "métodos" que são executados após o início do main.go?
Lukas Lukac de
38

Para imitar o comportamento do Java de despejo de pilha no SIGQUIT, mas ainda deixando o programa em execução:

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
fonte
4
Acho que é isso que o autor estava realmente procurando - simula o que Java faz quando você envia um kill -QUIT. Uma pequena mudança que tive que fazer foi mudar a primeira linha do loop for () para: "<- sigs". Em outras palavras, apenas descarte o sinal após aguardá-lo. As versões recentes do Go não permitem que você declare uma variável sem usá-la posteriormente.
George Armhold,
@Bryan, você está disposto a licenciar sob o BSD ou outros termos mais permissivos adicionais ao CC-BY-SA 3.0 exigido pelo StackOverflow?
Charles Duffy
1
@CharlesDuffy você pode encontrar praticamente a mesma coisa aqui sob a licença Apache: github.com/weaveworks/weave/blob/…
Bryan
36

Semelhante ao Java, SIGQUIT pode ser usado para imprimir um rastreamento de pilha de um programa Go e suas goroutines.
Uma diferença chave, entretanto, é que por padrão o envio de SIGQUIT para programas Java não os encerra, enquanto os programas Go são encerrados.

Esta abordagem não requer nenhuma mudança de código para imprimir um rastreamento de pilha de todos os goroutines de programas existentes.

A variável de ambiente GOTRACEBACK ( consulte a documentação do pacote runtime ) controla a quantidade de saída gerada. Por exemplo, para incluir todas as goroutines, defina GOTRACEBACK = all.

A impressão do rastreamento de pilha é acionada por uma condição de tempo de execução inesperada (sinal não tratado), originalmente documentado neste commit , tornando-o disponível desde pelo menos Go 1.1.


Como alternativa, se modificar o código-fonte for uma opção, consulte outras respostas.


Observe que em um terminal Linux, SIGQUIT pode ser enviado convenientemente com a combinação de teclas Ctrl+ \.

Rodolfo carvalho
fonte
5
Enquanto olhava os documentos não encontrei nenhuma menção ao SIGQUIT, ao invés do SIGABRT. Em meus próprios testes (com o go 1.7), o último também funcionou sobre o primeiro.
soltysh
3
esta deve ser a melhor resposta.
Steven Soroka
Os documentos referem-se a "quando um programa Go falha devido a um pânico não recuperado ou uma condição de tempo de execução inesperada". Um sinal não capturado (SIGQUIT, etc) é um dos últimos. Por que mencionei SIGQUIT? Porque o OP expressa seu amor pelo uso de SIGQUIT com Java, e essa resposta enfatiza a semelhança. Reescrever a resposta para torná-la mais clara.
Rodolfo Carvalho
26

Você pode usar runtime.Stack para obter o rastreamento de pilha de todos os goroutines:

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

Da documentação:

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

Stack formata um rastreamento de pilha da goroutine de chamada em buf e retorna o número de bytes gravados em buf. Se tudo for verdade, Stack formata os rastreamentos de pilha de todas as outras goroutines em buf após o rastreio da goroutine atual.

Robert Kajic
fonte
Isso inclui rastros de todos os goroutines, ótimo!
rogerdpack de
É este o formato que um pânico não recuperado se resume a usar?
Ztyx
2
Não se esqueça de adicionar string (buf) ou você imprimirá os bytes brutos lá.
koda
2
Talvez eu esteja fazendo algo errado ou talvez a funcionalidade tenha mudado, mas isso não recupera nada para mim, exceto uma fatia vazia de bytes.
17xande
1
@koda não há necessidade de fazer string(buf)aqui, fmt.Printf("%s", buf)e fmt.Printf("%s", string(buf))fazer exatamente a mesma coisa (veja a documentação do fmtpacote); a única diferença aqui é que a stringversão irá copiar os bytes de bufdesnecessariamente
kbolino
19

Pressione CTRL + \

(Se você executá-lo em um terminal e só quiser encerrar seu programa e descartar as rotinas go, etc.)

Encontrei esta questão procurando a sequência chave. Só queria uma maneira rápida e fácil de saber se meu programa está vazando rotinas de go :)

Øyvind Skaar
fonte
11

Em sistemas * NIX (incluindo OSX), envie um sinal de cancelamento SIGABRT:

pkill -SIGABRT program_name

Kyle Kloepper
fonte
Aparentemente, o envio de SIGQUIT para um processo Java não o encerra como o SIGABRT fará.
Dave C
Achei que esta é a solução mais simples e mais adequada para a pergunta original. Freqüentemente, você precisa de um rastreamento de pilha imediatamente, sem alterar seu código.
jotrocken,
5

É necessário usar o comprimento retornado por runtime.Stack()para evitar a impressão de um monte de linhas vazias após o rastreamento da pilha. A seguinte função de recuperação imprime um rastreamento bem formatado:

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
fonte
Não há runtime.Trace; runtime.Stack já foi mencionado há um ano e meio .
Dave C de
Eu nunca vi isso; em qual plataforma você está rodando?
Bryan
O que é que você não viu? O código deve ser executado em todas as plataformas; Eu testei no Windows 7, Ubuntu 14.04 e Mac.
David Tootill
Nunca vi linhas vazias.
Bryan
4

Por padrão, pressione as ^\teclas ( CTRL + \ ) para despejar os rastreamentos de pilha de todas as goroutines.


Caso contrário, para um controle mais granular, você pode usar panic. A maneira simples a partir do Go 1.6+ :

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

Em seguida, execute seu programa assim:

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

Se você também deseja imprimir goroutines go runtime:

$ GOTRACEBACK=system go run main.go

Aqui estão todas as opções GOTRACEBACK:

  • GOTRACEBACK=none omite os rastreamentos de pilha goroutine inteiramente.
  • GOTRACEBACK=single (o padrão) se comporta conforme descrito acima.
  • GOTRACEBACK=all adiciona rastreamentos de pilha para todas as goroutines criadas pelo usuário.
  • GOTRACEBACK=system é como `` all '', mas adiciona frames de pilha para funções de tempo de execução e mostra goroutinas criadas internamente pelo tempo de execução.
  • GOTRACEBACK=crashé como o `` sistema '', mas trava de maneira específica do sistema operacional ao invés de sair. Por exemplo, em sistemas Unix, a falha aumenta SIGABRTpara acionar um despejo de memória.

Aqui está a documentação

A variável GOTRACEBACK controla a quantidade de saída gerada quando um programa Go falha devido a um pânico não recuperado ou uma condição de tempo de execução inesperada.

Por padrão, uma falha imprime um rastreamento de pilha para a goroutine atual, elidindo funções internas ao sistema de tempo de execução e, em seguida, sai com o código de saída 2. A falha imprime rastreamentos de pilha para todas as goroutines se não houver goroutine atual ou a falha for interno ao tempo de execução.

Por razões históricas, as configurações GOTRACEBACK 0, 1 e 2 são sinônimos para nenhum, todos e sistema, respectivamente.

A função SetTraceback do pacote de tempo de execução / depuração permite aumentar a quantidade de saída em tempo de execução, mas não pode reduzir a quantidade abaixo daquela especificada pela variável de ambiente. Consulte https://golang.org/pkg/runtime/debug/#SetTraceback .

Inanc Gumus
fonte