Existe uma maneira de despejar um rastreamento de pilha sem gerar uma exceção em java?

159

Estou pensando em criar uma ferramenta de depuração para meu aplicativo Java.

Gostaria de saber se é possível obter um rastreamento de pilha, assim como, Exception.printStackTrace()mas sem realmente lançar uma exceção?

Meu objetivo é, em qualquer método, despejar uma pilha para ver quem é o responsável pela chamada do método.

corgrath
fonte

Respostas:

84

Você também pode tentar Thread.getAllStackTraces()obter um mapa dos rastreamentos de pilha para todos os threads ativos.

Prabhu R
fonte
250

Sim, basta usar

Thread.dumpStack()
Rob Di Marco
fonte
44
... mas não joga.
4309 Rob
5
Essa é a resposta que realmente aborda a questão.
22615 bruno
7
Muito simples e elegante. Essa deveria ter sido a resposta aceita.
deLock
11
Fwiw, a fonte desse método: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK 26/11
Essa é de longe a melhor resposta para a pergunta, se você estiver satisfeito com a saída stderr, o que parece ser a implicação da pergunta.
Mike Roedor
46

Se você deseja rastrear apenas o encadeamento atual (em vez de todos os encadeamentos no sistema, como a sugestão de Ram), faça:

Thread.currentThread (). getStackTrace ()

Para encontrar o chamador, faça:

private String getCallingMethodName() {
    StackTraceElement callingFrame = Thread.currentThread().getStackTrace()[4];
    return callingFrame.getMethodName();
}

E chame esse método de dentro do método que precisa saber quem é o responsável pela chamada. No entanto, uma palavra de aviso: o índice do quadro de chamada na lista pode variar de acordo com a JVM! Tudo depende de quantas camadas de chamadas existem no getStackTrace antes de você atingir o ponto em que o rastreamento é gerado. Uma solução mais robusta seria obter o rastreamento e iterar sobre ele, procurando o quadro para getCallingMethodName e, em seguida, dê dois passos adiante para encontrar o verdadeiro chamador.

Tom Anderson
fonte
2
Então, basta criar uma nova exceção por conta própria e usar [1] como índice!
Daniel
3
O título desta pergunta diz "sem lançar uma exceção". Concedido, você sugere criar a exceção, mas não jogá-la, mas ainda acho que isso não está no espírito da pergunta.
Tom Anderson
1
Claro que é. Criar uma exceção é a maneira de nível mais baixo para obter um rastreamento de pilha. Até o seu Thread.currentThread (). GetStackTrace () faz isso, mas da minha maneira faz isso mais rápido :).
Daniel
@ Daniel Há outra consideração: com muitas estruturas de teste, por exemplo, Spock, basta criar uma Exceção (sem lançá-la) será suficiente para a estrutura pegar: o teste considerará que uma exceção "ocorreu" e o teste será final, com um fracasso.
mike roedor
@mikerodent: Você tem certeza? Spock teria que usar agentes para fazer isso. Mas de qualquer maneira, porque eu só precisava chamar a classe, usei Reflection.getCallerClass (2) em uma função de utilitário. Você não receberá a função e o número da linha dessa maneira, pensou.
Daniel
37

Você pode obter um rastreamento de pilha como este:

Throwable t = new Throwable();
t.printStackTrace();

Se você deseja acessar o quadro, pode usar t.getStackTrace () para obter uma matriz de quadros de pilha.

Esteja ciente de que esse rastreamento de pilha (como qualquer outro) pode estar faltando alguns quadros se o compilador de hotspot estiver ocupado otimizando as coisas.

Gerco Dries
fonte
Eu gosto desta resposta porque me oferece a oportunidade de direcionar a saída. Eu posso substituir t.printStackTracepor t.printStackTrace(System.out).
4
Em uma linha:new Throwable().printStackTrace(System.out);
1
Essa deve ser a resposta aceita. É simples e direto! Obrigado por compartilhar
Sam
2
e é fácil para enviar para java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead
13

Observe que Thread.dumpStack () realmente lança uma exceção:

new Exception("Stack trace").printStackTrace();
Ariel T
fonte
12
Cria uma exceção, mas não a lança.
MatrixFrog
6

Você também pode enviar um sinal para a JVM para executar Thread.getAllStackTraces () em um processo Java em execução, enviando um sinal QUIT para o processo.

No Unix / Linux, use:

kill -QUIT process_id, em que process_id é o número do processo do seu programa Java.

No Windows, você pode pressionar Ctrl-Break no aplicativo, embora normalmente não veja isso a menos que esteja executando um processo de console.

O JDK6 introduziu outra opção, o comando jstack, que exibirá a pilha de qualquer processo JDK6 em execução no seu computador:

jstack [-l] <pid>

Essas opções são muito úteis para aplicativos em execução em um ambiente de produção e não podem ser modificados facilmente. Eles são especialmente úteis para diagnosticar conflitos de tempo de execução ou problemas de desempenho.

http://java.sun.com/developer/technicalArticles/Programming/Stacktrace/ http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html

Andrew Newdigate
fonte
6

Se você precisar capturar saída

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
Ricardo Jl Rufino
fonte
6

O Java 9 introduziu o StackWalker classes e de suporte para caminhar pela pilha.

Aqui estão alguns trechos do Javadoc:

O walkmétodo abre um fluxo seqüencial de StackFramespara o encadeamento atual e aplica a função fornecida para percorrer o StackFramefluxo. O fluxo relata os elementos do quadro da pilha em ordem, do quadro superior que representa o ponto de execução no qual a pilha foi gerada até o quadro mais inferior. O StackFramefluxo é fechado quando o método walk retorna. Se for feita uma tentativa de reutilizar o fluxo fechado, IllegalStateExceptionserá lançada.

...

  1. Para capturar instantaneamente os 10 principais quadros de pilha do segmento atual,

    List<StackFrame> stack = StackWalker.getInstance().walk(s ->
     s.limit(10).collect(Collectors.toList()));
mkobit
fonte
0

HPROF Java Profiler

Você nem precisa fazer isso no código. Você pode anexar o Java HPROF ao seu processo e, a qualquer momento, pressionar Control- \ para gerar heap dump, threads em execução, etc. . . sem estragar seu código de aplicativo. Isso está um pouco desatualizado, o Java 6 vem com o jconsole da GUI, mas ainda acho o HPROF muito útil.

Gandalf
fonte
0

A resposta de Rob Di Marco é de longe a melhor se você estiver satisfeito com a saída stderr.

Se você deseja capturar para a String, com novas linhas de acordo com um rastreamento normal, faça o seguinte:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
microfone roedor
fonte