Depurar Aplicaciones No Responsivas

Hay muchos tutoriales de depuradora que te enseñan cómo establecer puntos de interrupción de línea, registrar valores o evaluar expresiones. Si bien este conocimiento por sí solo te ofrece muchas herramientas para depurar tu aplicación, los escenarios del mundo real pueden ser un poco más complicados y requieren un enfoque más avanzado.

En este artículo, aprenderemos cómo localizar el código que provoca un bloqueo de la UI sin mucho conocimiento previo del proyecto y corregir el código defectuoso sobre la marcha.

El problema

Si quieres seguir el ejemplo, comienza clonando este repositorio: https://github.com/flounder4130/debugger-example

Supongamos que tienes una aplicación compleja que se bloquea cuando realizas alguna acción. Sabes cómo reproducir el error, pero la dificultad es que no sabes qué parte del código se encarga de esta funcionalidad.

La interfaz de usuario de la aplicación de ejemplo tiene muchos botones para realizar algunas acciones

En nuestra aplicación de ejemplo, el bloqueo ocurre cuando haces clic en el Button N. Sin embargo, no es tan fácil encontrar el código que es responsable de esta acción:

Buscar Button N en todo el proyecto no da ningún resultado Buscar Button N en todo el proyecto no da ningún resultado

Veamos cómo podemos usar el depurador para encontrarlo.

Puntos de interrupción de método

La ventaja de los puntos de interrupción de método por encima de los puntos de interrupción de línea es que se pueden usar en jerarquías completas de clases. ¿Cómo es útil esto en nuestro caso?

Si observas el proyecto de ejemplo, verás que todas las clases de acción se derivan de la interfaz Action con un solo método: perform().

Icono de punto de interrupción de método en el margen del editor Icono de punto de interrupción de método en el margen del editor

Establecer un punto de interrupción de método en este método de la interfaz suspenderá la aplicación cada vez que se llame a uno de los métodos derivados. Para establecer un punto de interrupción de método, haz clic en la línea que declara el método.

Inicia la sesión de depuración y haz clic en el Button N. La aplicación queda suspendida en ActionImpl14. Ahora sabemos dónde está ubicado el código correspondiente a este botón.

La aplicación quedó suspendida en una clase que implementa la interfaz de Acción La aplicación quedó suspendida en una clase que implementa la interfaz de Acción

Aunque en este artículo estamos centrados en encontrar el bug, esta técnica también puede ahorrarte mucho tiempo cuando quieras entender cómo funciona algo en una base de código grande.

Pausar aplicación

El enfoque con puntos de interrupción de método funciona bien, pero se basa en la suposición de que sabemos algo sobre la interfaz padre. ¿Qué pasa si esta suposición está equivocada, o no podemos usar este enfoque por alguna otra razón?

Bueno, incluso podemos hacerlo sin puntos de interrupción. Haz clic en el Button N, y mientras la aplicación se cuelga, ve a IntelliJ IDEA. Desde el menú principal, selecciona Run | Debugging Actions | Pause Program.

El stack de llamadas para el hilo principal muestra lo que está haciendo actualmente El stack de llamadas para el hilo principal muestra lo que está haciendo actualmente

La aplicación se suspenderá, permitiéndonos examinar el estado actual de los hilos en la pestaña Threads & Variables. Esto nos da una idea de lo que la aplicación está haciendo en ese momento. Como está colgada, podemos identificar el método que causa el bloqueo y rastrearlo hasta el sitio de la llamada.

Este enfoque tiene algunas ventajas sobre un volcado de hilo más tradicional, que cubriremos en breve. Por ejemplo, te proporciona información sobre las variables en una forma conveniente y te permite controlar la ejecución adicional del programa.

Volcados de hilo

Finalmente, podemos utilizar un volcado de hilo, que no es estrictamente una característica del depurador. Está disponible independientemente de si estás utilizando el depurador.

Haz clic en el Button N. Mientras la aplicación se bloquea, ve a IntelliJ IDEA. Desde el menú principal, selecciona Run | Debugging Actions | Get Thread Dump.

Explora los hilos disponibles a la izquierda, y en AWT-EventQueue verás qué es lo que está causando el problema.

Visor de volcado de hilo en IntelliJ IDEA Visor de volcado de hilo en IntelliJ IDEA

La desventaja de los volcados de hilo es que solo proporcionan una instantánea del estado del programa en el momento en que se hicieron. No puedes usar los volcados de hilo para explorar variables o controlar la ejecución del programa.

En nuestro ejemplo, no necesitamos recurrir a un volcado de hilo. Sin embargo, aún quería mencionar esta técnica ya que puede ser útil en otros casos, como cuando estás intentando depurar una aplicación que se ha lanzado sin el agente de depuración.

Entender el problema

Independientemente de la técnica de depuración, llegamos a ActionImpl14. En esta clase, alguien tenía la intención de realizar el trabajo en un hilo separado, pero confundió Thread.start() conThread.run(), que ejecuta el código en el mismo hilo que el código que realiza la llamada.

El analizador estático de IntelliJ IDEA incluso nos advierte sobre esto en tiempo de diseño:

El análisis estático de IntelliJ IDEA da una advertencia sobre la llamada sospechosa a Thread.run() El análisis estático de IntelliJ IDEA da una advertencia sobre la llamada sospechosa a Thread.run()

Un método que hace un trabajo pesado (o duerme mucho en este caso) se llama en el hilo de la UI y lo bloquea hasta que el método termina. Es por eso que no podemos hacer nada en la UI durante un tiempo después de hacer clic en el Button N.

HotSwap

Ahora que hemos descubierto la causa del error, corrijamos el problema.

Podríamos detener el programa, recompilar el código y luego volver a ejecutarlo. Sin embargo, no siempre es conveniente volver a implementar toda la aplicación solo porque se hizo un pequeño cambio.

Hagámoslo de la manera inteligente. Primero, corrige el código utilizando la corrección rápida sugerida:

El menú contextual (Alt-Enter) da una opción para corregir el código sospechoso El menú contextual (Alt-Enter) da una opción para corregir el código sospechoso

Después de que el código esté listo, haz clic en Run | Debugging Actions | Reload Changed Classes. Aparece un globo, confirmando que el nuevo código ha llegado a la VM.

Un globo confirma que las clases actualizadas han llegado al tiempo de ejecución Un globo confirma que las clases actualizadas han llegado al tiempo de ejecución

Volvamos a la aplicación y comprobemos. Al hacer clic en Button N ya no se bloquea la aplicación.

Tip icon

Ten en cuenta que HotSwap tiene sus limitaciones. Si estás interesado en capacidades extendidas de HotSwap, podría ser una buena idea echar un vistazo a herramientas avanzadas como DCEVM o JRebel

Resumen

Usando nuestro razonamiento y un par de características del depurador, pudimos localizar el código que estaba causando un bloqueo de la UI en nuestro proyecto. Luego, procedimos a corregir el código sin perder tiempo en la recompilación y la redistribución, que puede ser larga en proyectos del mundo real.

Espero que encuentres útiles las técnicas descritas. ¡Hazme saber lo que piensas!

¡Estén atentos para más!

all posts ->