C'è un modo per scaricare una traccia dello stack senza generare un'eccezione in Java?


159

Sto pensando di creare uno strumento di debug per la mia applicazione Java.

Mi chiedo se è possibile ottenere una traccia dello stack, proprio come Exception.printStackTrace()ma senza effettivamente lanciare un'eccezione?

Il mio obiettivo è, in un dato metodo, scaricare uno stack per vedere chi è il chiamante del metodo.

Risposte:


84

Puoi anche provare Thread.getAllStackTraces()a ottenere una mappa delle tracce dello stack per tutti i thread attivi.


250

Sì, basta usare

Thread.dumpStack()

44
... ma non lo lancia.
Rob

5
Questa è la risposta che risponde effettivamente alla domanda.
bruno,

7
Molto semplice ed elegante. Questa avrebbe dovuto essere la risposta accettata.
deLock

11
Dopo, la fonte di questo metodo: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK,

Questa è di gran lunga la migliore risposta alla domanda se sei soddisfatto di presentare stderr, che sembra essere la conseguenza della domanda.
mike rodent,

46

Se vuoi la traccia solo per il thread corrente (piuttosto che per tutti i thread nel sistema, come fa il suggerimento di Ram), fai:

Thread.currentThread ().getStackTrace ()

Per trovare il chiamante, fai:

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

E chiama quel metodo dall'interno del metodo che deve sapere chi è il suo chiamante. Tuttavia, un avvertimento: l'indice del frame chiamante all'interno dell'elenco potrebbe variare in base alla JVM! Tutto dipende da quanti strati di chiamate ci sono all'interno di getStackTrace prima di raggiungere il punto in cui viene generata la traccia. Una soluzione più robusta sarebbe quella di ottenere la traccia e iterare su di essa cercando il frame per getCallingMethodName, quindi fare due passi più avanti per trovare il vero chiamante.


2
Quindi crea una nuova eccezione e usa [1] come indice!
Daniel,

3
Il titolo di questa domanda dice "senza gettare un'eccezione". Certo, suggerisci di creare l'eccezione ma non di lanciarla, ma penso ancora che non sia nello spirito della domanda.
Tom Anderson,

1
Ovviamente è. La creazione di un'eccezione è il modo più basso livello per ottenere una stack stack. Anche il tuo Thread.currentThread (). GetStackTrace () lo fa, ma a modo mio lo fa più velocemente :).
Daniel

@Daniel C'è un'altra considerazione: con molti framework di test, ad esempio Spock, basta creare un'eccezione (senza lanciarla) per far sì che il framework prenda in considerazione: il test considererà quindi che si è verificata un'eccezione e il test sarà fine, con un fallimento.
mike rodent,

@mikerodent: sei sicuro? Spock avrebbe dovuto usare gli agenti per farlo. Ma comunque, poiché avevo solo bisogno di chiamare la classe, ho usato Reflection.getCallerClass (2) in una funzione di utilità. Non otterrai la funzione e il numero di riga in quel modo, pensai.
Daniel,

37

Puoi ottenere una traccia dello stack in questo modo:

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

Se si desidera accedere al frame, è possibile utilizzare t.getStackTrace () per ottenere una matrice di frame stack.

Essere consapevoli del fatto che questo stacktrace (proprio come qualsiasi altro) potrebbe mancare di alcuni frame se il compilatore di hotspot è stato impegnato a ottimizzare le cose.


Mi piace questa risposta perché mi offre l'opportunità di dirigere l'output. Posso sostituire t.printStackTracecon t.printStackTrace(System.out).

4
In una riga:new Throwable().printStackTrace(System.out);

1
Questa dovrebbe essere la risposta accettata. È semplice e diretto! Grazie per aver condiviso
Sam l'

2
ed è facile inviare a java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead il

13

Si noti che Thread.dumpStack () in realtà genera un'eccezione:

new Exception("Stack trace").printStackTrace();

12
Crea un'eccezione, ma non la lancia.
MatrixFrog,

6

È inoltre possibile inviare un segnale a JVM per eseguire Thread.getAllStackTraces () su un processo Java in esecuzione inviando un segnale QUIT al processo.

Su Unix / Linux utilizzare:

kill -QUIT process_id, dove process_id è il numero di processo del programma Java.

Su Windows, puoi premere Ctrl-Break nell'applicazione, anche se di solito non lo vedrai se non esegui un processo della console.

JDK6 ha introdotto un'altra opzione, il comando jstack, che visualizzerà lo stack da qualsiasi processo JDK6 in esecuzione sul tuo computer:

jstack [-l] <pid>

Queste opzioni sono molto utili per le applicazioni in esecuzione in un ambiente di produzione e non possono essere modificate facilmente. Sono particolarmente utili per diagnosticare deadlock di runtime o problemi di prestazioni.

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


6

Se è necessario l'output di acquisizione

StringWriter sw = new StringWriter();
new Throwable("").printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();

6

Java 9 ha introdotto il StackWalker classi di supporto per lo stack.

Ecco alcuni frammenti di Javadoc:

Il walkmetodo apre un flusso sequenziale di StackFramesper il thread corrente e quindi applica la funzione specificata per percorrere il StackFrameflusso. Il flusso riporta gli elementi del frame dello stack in ordine, dal frame più in alto che rappresenta il punto di esecuzione in cui lo stack è stato generato al frame più in basso. Il StackFrameflusso viene chiuso quando viene restituito il metodo walk. Se si tenta di riutilizzare il flusso chiuso, IllegalStateExceptionverrà generato.

...

  1. Per creare un'istantanea dei primi 10 frame dello stack del thread corrente,

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

0

HPROF Java Profiler

Non devi nemmeno farlo nel codice. È possibile collegare Java HPROF al processo e in qualsiasi momento premere Control- \ per generare dump dell'heap, eseguire thread, ecc. . . senza confondere il codice dell'applicazione. Questo è un po 'obsoleto, Java 6 viene fornito con la GUI jconsole, ma trovo ancora HPROF molto utile.


0

La risposta di Rob Di Marco è di gran lunga la migliore se si è felici di produrre stderr.

Se si desidera acquisire in a String, con nuove righe come da una traccia normale, è possibile effettuare questa operazione:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.