Existe-t-il un moyen de vider une trace de pile sans lever d'exception en java?

159

Je pense créer un outil de débogage pour mon application Java.

Je me demande s'il est possible d'obtenir une trace de pile, tout comme Exception.printStackTrace()mais sans réellement lever une exception?

Mon objectif est, dans n'importe quelle méthode donnée, de vider une pile pour voir qui est l'appelant de la méthode.

corgrath
la source

Réponses:

84

Vous pouvez également essayer Thread.getAllStackTraces()d'obtenir une carte des traces de pile pour tous les threads actifs.

Prabhu R
la source
250

Oui, utilisez simplement

Thread.dumpStack()
Rob Di Marco
la source
44
... mais ne le lance pas.
Rob
5
C'est la réponse qui répond en fait à la question.
bruno
7
Très simple et élégant. Cela aurait dû être la réponse acceptée.
deLock
11
Fwiw, la source de cette méthode: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK
C'est de loin la meilleure réponse à la question si vous êtes satisfait de la sortie vers stderr, ce qui semble être l'implication de la question.
mike rodent
46

Si vous voulez la trace uniquement pour le thread actuel (plutôt que pour tous les threads du système, comme le fait la suggestion de Ram), faites:

Thread.currentThread ().getStackTrace ()

Pour trouver l'appelant, procédez comme suit:

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

Et appelez cette méthode à partir de la méthode qui a besoin de savoir qui est son appelant. Cependant, un mot d'avertissement: l'index de la trame appelante dans la liste peut varier selon la JVM! Tout dépend du nombre de couches d'appels présents dans getStackTrace avant d'atteindre le point où la trace est générée. Une solution plus robuste serait d'obtenir la trace et de la parcourir à la recherche de la trame de getCallingMethodName, puis de faire deux pas plus loin pour trouver le véritable appelant.

Tom Anderson
la source
2
Alors créez simplement une nouvelle exception par vous-même et utilisez [1] comme index!
Daniel
3
Le titre de cette question dit "sans faire exception". Certes, vous suggérez de créer l'exception mais de ne pas la lancer, mais je pense toujours que ce n'est pas dans l'esprit de la question.
Tom Anderson
1
Bien sûr que ça l'est. La création d'une exception est le moyen le plus bas d'obtenir une trace de pile. Même votre Thread.currentThread (). GetStackTrace () le fait, mais ma façon le fait plus rapidement :).
Daniel
@Daniel Il y a une autre considération: avec de nombreux frameworks de test, par exemple Spock, il suffit de créer une exception (sans la lancer) pour que le framework reprenne: le test considérera alors qu'une exception s'est "produite" et le test va fin, avec un échec.
mike rodent
@mikerodent: Êtes-vous sûr? Spock devrait utiliser des agents pour le faire. Mais de toute façon, parce que j'avais juste besoin d'appeler la classe, j'ai utilisé Reflection.getCallerClass (2) dans une fonction utilitaire. Vous n'obtiendrez pas la fonction et le numéro de ligne de cette façon, pensé.
Daniel
37

Vous pouvez obtenir une trace de pile comme ceci:

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

Si vous souhaitez accéder au cadre, vous pouvez utiliser t.getStackTrace () pour obtenir un tableau de cadres de pile.

Sachez que ce stacktrace (comme n'importe quel autre) peut manquer certaines images si le compilateur hotspot a été occupé à optimiser les choses.

Gerco sèche
la source
J'aime cette réponse car elle m'offre l'opportunité de diriger la sortie. Je peux remplacer t.printStackTracepar t.printStackTrace(System.out).
4
En une seule ligne:new Throwable().printStackTrace(System.out);
1
Cela devrait être la réponse acceptée. C'est simple et direct! Merci pour le partage
Sam
2
et il est facile d'envoyer à java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead
13

Notez que Thread.dumpStack () lève en fait une exception:

new Exception("Stack trace").printStackTrace();
Ariel T
la source
12
Cela crée une exception, mais ne la lance pas.
MatrixFrog
6

Vous pouvez également envoyer un signal à la JVM pour exécuter Thread.getAllStackTraces () sur un processus Java en cours d'exécution en envoyant un signal QUIT au processus.

Sous Unix / Linux:

kill -QUIT process_id, où process_id est le numéro de processus de votre programme Java.

Sous Windows, vous pouvez appuyer sur Ctrl-Pause dans l'application, bien que vous ne le verrez généralement pas sauf si vous exécutez un processus de console.

JDK6 a introduit une autre option, la commande jstack, qui affichera la pile de tout processus JDK6 en cours d'exécution sur votre ordinateur:

jstack [-l] <pid>

Ces options sont très utiles pour les applications qui s'exécutent dans un environnement de production et ne peuvent pas être modifiées facilement. Ils sont particulièrement utiles pour diagnostiquer les blocages d'exécution ou les problèmes de performances.

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

Andrew Newdigate
la source
6

Si vous avez besoin d'une sortie de capture

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

Java 9 a introduit le StackWalker classes et les supports pour parcourir la pile.

Voici quelques extraits de Javadoc:

La walkméthode ouvre un flux séquentiel de StackFramespour le thread actuel, puis applique la fonction donnée pour parcourir le StackFrameflux. Le flux signale les éléments du cadre de la pile dans l'ordre, du cadre le plus haut qui représente le point d'exécution auquel la pile a été générée vers le cadre le plus bas. Le StackFrameflux est fermé lorsque la méthode de marche revient. Si une tentative est faite pour réutiliser le flux fermé, IllegalStateExceptionsera levé.

...

  1. Pour créer un instantané des 10 premiers cadres de pile du thread actuel,

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

Profiler Java HPROF

Vous n'avez même pas à faire cela dans le code. Vous pouvez attacher le Java HPROF à votre processus et à tout moment appuyez sur Control- \ pour afficher le vidage de tas, exécuter des threads, etc. . . sans gâcher votre code d'application. C'est un peu obsolète, Java 6 est livré avec la jconsole GUI, mais je trouve toujours HPROF très utile.

Gandalf
la source
0

La réponse de Rob Di Marco est de loin la meilleure si vous êtes heureux de sortirstderr .

Si vous souhaitez capturer vers un String, avec de nouvelles lignes selon une trace normale, vous pouvez le faire:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
Mike rongeur
la source