¿Hay alguna manera de volcar un seguimiento de la pila sin lanzar una excepción en Java?


159

Estoy pensando en crear una herramienta de depuración para mi aplicación Java.

Me pregunto si es posible obtener un seguimiento de la pila, ¿ Exception.printStackTrace()pero sin lanzar una excepción?

Mi objetivo es, en cualquier método, volcar una pila para ver quién llama el método.

Respuestas:


84

También puede intentar Thread.getAllStackTraces()obtener un mapa de trazas de pila para todos los hilos que están vivos.


250

Sí, simplemente usa

Thread.dumpStack()

44
... pero no lo tira.
Rob

55
Esa es la respuesta que realmente aborda la pregunta.
bruno

77
Muy simple y elegante. Esta debería haber sido la respuesta aceptada.
deLock

11
Fwiw, la fuente de este método: public static void dumpStack() { new Exception("Stack trace").printStackTrace(); }
JohnK

Esta es, con mucho, la mejor respuesta a la pregunta si está satisfecho con la salida stderr, lo que parece ser la implicación de la pregunta.
Mike roedor

46

Si desea rastrear solo el hilo actual (en lugar de todos los hilos en el sistema, como lo sugiere la sugerencia de Ram), haga lo siguiente:

Thread.currentThread (). getStackTrace ()

Para encontrar a la persona que llama, haz:

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

Y llame a ese método desde dentro del método que necesita saber quién llama. Sin embargo, una advertencia: ¡el índice del marco de llamada dentro de la lista puede variar de acuerdo con la JVM! Todo depende de cuántas capas de llamadas hay dentro de getStackTrace antes de llegar al punto donde se genera el seguimiento. Una solución más robusta sería obtener el rastreo e iterar sobre él buscando el marco para getCallingMethodName, luego dar dos pasos más arriba para encontrar el verdadero llamador.


2
¡Así que cree una nueva excepción por su cuenta y use [1] como índice!
Daniel

3
El título de esta pregunta dice "sin lanzar una excepción". De acuerdo, sugieres crear la excepción pero no lanzarla, pero sigo pensando que eso no está en el espíritu de la pregunta.
Tom Anderson

1
Por supuesto que es. Crear una excepción es la forma más baja para obtener un seguimiento de pila. Incluso tu Thread.currentThread (). GetStackTrace () lo hace, pero a mi manera lo hace más rápido :).
Daniel

@Daniel Hay otra consideración: con muchos marcos de prueba, por ejemplo, Spock, solo crear una Excepción (sin lanzarlo) será suficiente para que el marco se recupere: la prueba considerará que una Excepción ha "ocurrido" y la prueba será final, con un fracaso.
Mike roedor

@mikerodent: ¿Estás seguro? Spock tendría que usar agentes para hacer eso. Pero de todos modos, porque solo necesitaba llamar a la clase, usé Reflection.getCallerClass (2) en una función de utilidad. No obtendrá la función y el número de línea de esa manera, pensó.
Daniel

37

Puede obtener un seguimiento de pila como este:

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

Si desea acceder al marco, puede usar t.getStackTrace () para obtener una matriz de marcos de pila.

Tenga en cuenta que a este stacktrace (como a cualquier otro) le pueden faltar algunos fotogramas si el compilador de punto de acceso ha estado ocupado optimizando las cosas.


Me gusta esta respuesta porque me ofrece la oportunidad de dirigir la salida. Puedo reemplazar t.printStackTracecon t.printStackTrace(System.out).

44
En una línea:new Throwable().printStackTrace(System.out);

1
Esta debería ser la respuesta aceptada. ¡Es simple y directo! Gracias por compartir
Sam

2
y es fácil de enviar a java.util.Loggerlog.log(Level.SEVERE, "Failed to do something", new Throwable());
MitchBroadhead el

13

Tenga en cuenta que Thread.dumpStack () en realidad arroja una excepción:

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

12
Crea una excepción, pero no la arroja.
MatrixFrog

6

También puede enviar una señal a la JVM para ejecutar Thread.getAllStackTraces () en un proceso Java en ejecución enviando una señal QUIT al proceso.

En uso de Unix / Linux:

kill -QUIT process_id, donde process_id es el número de proceso de su programa Java.

En Windows, puede presionar Ctrl-Break en la aplicación, aunque generalmente no verá esto a menos que esté ejecutando un proceso de consola.

JDK6 introdujo otra opción, el comando jstack, que mostrará la pila de cualquier proceso JDK6 en ejecución en su computadora:

jstack [-l] <pid>

Estas opciones son muy útiles para aplicaciones que se ejecutan en un entorno de producción y no se pueden modificar fácilmente. Son especialmente útiles para diagnosticar puntos muertos de tiempo de ejecución o problemas de rendimiento.

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


6

Si necesita captura de salida

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

6

Java 9 introdujo las StackWalkerclases de apoyo para caminar por la pila.

Aquí hay algunos fragmentos del Javadoc:

El walkmétodo abre una secuencia secuencial StackFramespara el hilo actual y luego aplica la función dada para recorrer la StackFramesecuencia. La secuencia informa los elementos del marco de la pila en orden, desde el cuadro más superior que representa el punto de ejecución en el que se generó la pila hasta el cuadro más inferior. La StackFramesecuencia se cierra cuando vuelve el método de caminar. Si se intenta reutilizar la secuencia cerrada, se IllegalStateExceptionlanzará.

...

  1. Para tomar una foto de los 10 marcos principales de la secuencia actual,

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

0

HPROF Java Profiler

Ni siquiera tiene que hacer esto en el código. Puede adjuntar el HPROF de Java a su proceso y, en cualquier momento, presione Control- \ para generar el volcado del montón, ejecutar subprocesos, etc. . . sin estropear su código de aplicación. Esto está un poco desactualizado, Java 6 viene con la interfaz gráfica de usuario de GUI, pero todavía encuentro que HPROF es muy útil.


0

La respuesta de Rob Di Marco es, con mucho, la mejor si está contento de salir stderr.

Si desea capturar a a String, con nuevas líneas según un trazado normal, puede hacer esto:

Arrays.toString(Thread.currentThread().getStackTrace()).replace( ',', '\n' );
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.