Mostrando entradas con la etiqueta debugging. Mostrar todas las entradas
Mostrando entradas con la etiqueta debugging. Mostrar todas las entradas

martes, 18 de abril de 2023

Un pequeño y raro bug en PSeInt

La semana pasada corregí un bug de PSeInt de lo más curioso, por la forma en que se manifestaba, y la causa del mismo. Es un ejemplo de las "sutilezas" de programar orientado a eventos y la inversión del control, y de lo importante de la programación defensiva.

martes, 27 de agosto de 2019

Reviviendo una vieja funcionalidad de ZinjaI

En algunos prácticos de computación gráfica les damos a los alumnos programas que implementan algo visto en teoría para que experimenten: a) cambiar cosas, predecir el resultado y validar la predicción; b) pensar un resultado, deducir qué hay que cambiar, y validar la deducción; o c) si todavía no pueden predecir ni deducir nada, cambiar al azar y observar para desarrollar una primer intuición de qué hace cada cosa. Entonces, quiero programas donde sea fácil y rápido cambiar cosas.

Podemos empezar por decir que para cambiar el estudiante debe modificar el código, pero eso puede hacer que el loop de retroalimentación sea lento y tedioso. El plan B es programar alguna interfaz; pero como usamos bibliotecas muy simples (como FreeGLUT), no hay widgets o similares. Las alternativas parecen ser: a) programar controles propios, pero entonces es más código accesorio para ensuciar el ejemplo; b) usar mejores bibliotecas, pero entonces agregamos otra dependencia. Pues bien, ZinjaI viene al rescate con una tercera opción.

viernes, 9 de febrero de 2018

Release vs. Debug

He visto cosas que ustedes no creerían: programadores haciendo benchmarks de código sin optimizar, generando instaladores con ejecutables en modo debug, desdoblando loops a mano, haciendo inlining mediante copy-paste, distribuyendo los .o y olvidándose los dlls, rayos-C++ brillar en la oscuridad cerca de la puerta de Tannhäuser... Y hablo de alumnos avanzados de la carrera, hasta algunos egresados.

Pensaba en escribir un post sobre cómo distribuir un programa generado con ZinjaI (mayormente sobre qué va en el instalador), pero inmediatamente noté que un punto importante era de esto de compilar en modo "release"... y entonces me parece mejor aclarar ese punto antes de continuar, y dejar lo del instalador para la próxima.

lunes, 2 de octubre de 2017

Mac OS vs. GDB

Desde hace ya bastante tiempo, las herramientas de desarrollo de Mac OS pasaron de usar gcc+gdb (compilador y depurador) a llvm/clang+lldb. El paso a llvm/clang como nuevo compilador no me generó demasiados problemas ya que la linea de comandos de clang es 99% compatible con la de gcc. Pero para el depurador es otra historia. Integrar lldb en ZinjaI como alternativa a gdb implica muchísimos cambios.

Así que por el momento tenemos que instalar gdb "manualmente" para que funcione correctamente la depuración. Pero esto, por motivos de seguridad (creo), es increíblemente complicado. Armé un script para automatizar todo lo que pude automatizar de este proceso, y una página de ayuda donde encontrar las instrucciones para el resto. En este post, un video mostrando el proceso completo, y algunas explicaciones al respecto.

lunes, 30 de noviembre de 2015

Cuando el backtrace no dice nada...

Una nueva versión de ZinjaI se aproxima. En realidad, las vacaciones se aproximan, y eso probablemente haga que se aproximen nuevas versiones de todo. Pero hoy les cuento de ZinjaI. No hay grandes super-novedades en esta versión. Al menos no hasta el momento. Sí hay un millón de pequeños cambios y correcciones que juntos suman. Pero entre lo poco destacable hay tres "pequeñas" mejoras en la interfaz de depuración que pueden ser muy útiles.

jueves, 30 de julio de 2015

ASM caliente para el alma del programador

Depurar programas paralelos es terriblemente complicado. Y cuando uno le suma a eso su propia idiotez el combo se torna mortal. Esta semana perdí dos días completos por culpa de un error de tipeo. Pero, como toda historia para que merezca ser contada debe tener algún lado bueno, en este caso, mis dos días de caza incluyeron el desarrollo de un par de funcionalidades nuevas en ZinjaI. Funcionalidades que al final de cuentas no necesitaba en absoluto para resolver ese problema, pero que en algún momento pensé que sí, y supongo que a futuro ya les daré mejor uso. Se trata de un par de paneles para inspeccionar el código de máquina generado por el compilador, y el estado de los registros internos del procesador durante la depuración. He aquí la anécdota completa y el resultado.

martes, 19 de mayo de 2015

Problemas con la terminal y el depurador (parte 2)

Empecé el artículo anterior diciendo que iba a mencionar dos problemas con las terminales y la depuración en ZinjaI, pero como se hizo largo describí solo uno. Pues aquí viene el segundo. Es un problema levemente relacionado, muy molesto, pero generalmente inofensivo. Es el mensaje "&warning: GDB failed to set controlling terminal: Operation not permited" que se muestra al comenzar la depuración en GNU/Linux. Desde la versión 7.0 de gdb, muchos IDEs sufren este mensaje, que asusta al usuario inexperto y genera consultas de lo más variadas. Hay miles de hilos al respecto en foros varios en Internet, pero absolutamente ninguna solución. Yo tampoco pude solucionarlo, pero al menos llegué a entenderlo y me convencí de por qué en general no se puede, y qué consecuencias puede traer. Vengo a documentarlo porque leí miles de respuestas, y la mayoría no están ni remotamente cerca de la verdadera causa, y sus consecuencias.

miércoles, 13 de mayo de 2015

Problemas con la terminal y el depurador (parte 1)

Estuve analizando dos diferencias en el comportamiento de la terminal que usa ZinjaI para ejecutar un programa o proyecto entre la ejecución normal y la ejecución para depuración. La primera es que hasta ahora, la terminal donde se ejecuta la depuración se cierra al finalizar el programa, pase lo que pase, y no le hace caso a las opciones de ejecución que pueden decir que espere una tecla, o que no se cierre si el código de salida no es 0. Esto implica que en la mayoría de los casos, si una ejecución en el depurador finaliza como debe, no llegamos a ver los resultados, a menos que nos tomemos el trabajo de poner algún breakpoint al final del main. Tengo solo la mitad de este problema resuelto.

martes, 23 de diciembre de 2014

Bibliotecas externas en versión debug

Siempre pensé que era mejor utilizar versiones "release" de las bibliotecas de terceros en mis proyectos, para todas las compilaciones, aún en mis versiones para debug. Es decir, que si mi programa usa una biblioteca B, al depurarlo me interesa solo depurar mi parte. Agregar la información de depuración de B sería agregar ruido. Hace poco comprobé que esto no siempre es recomendable. Es algo que ahora me parece muy obvio, pero la verdad es que antes no le había dado importancia. Nunca había usado intensivamente una versión debug de una biblioteca externa, y por eso no había notado las muchas ventajas que esto tiene.

domingo, 14 de diciembre de 2014

Por qué la ejecución era tan leeenta en el depurador

Hace unos días comenté que había estado renegando para depurar un proyecto en Windows porque al correrlo en el depurador la ejecución tardaba 17 veces más de lo normal. Encontré en aquel momento un workaround para salir del paso, pero no sabía entonces la causa real del problema. Cerré el post prometiendo escribir si la averiguaba, y ahora vengo a cumplir con eso. Ya encontré la causa, y una solución muy simple que, como siempre, estará incluida en ZinjaI para la próxima versión (muy pronto).

jueves, 11 de diciembre de 2014

Cuando la ejecución en el depurador es mucho más leeenta

Saben que en ZinjaI (y en muchísimos otros IDEs) tenemos dos formas de ejecutar un proyecto: la "normal" y la "debug". La "normal" (Ejecución->Ejecutar o F9) guarda, compila y ejecuta el resultado como si hubiésemos hecho doble click en el exe desde el explorador de archivos. La ejecución "debug" (Depuración->Iniciar o F5), en cambio, ejecuta a través de gdb. Es decir, ejecuta gdb (el verdadero depurador que hay detrás de ZinjaI), y le pide a este que cargue y ejecute el exe. Para tener control sobre esta ejecución, gdb puede hacer internamente cosas raras, normalmente sin que lo notemos. Me dí cuenta hace poco que en Windows alguna de estas cosas raras puede hacer que el programa corra mucho más lento que en la ejecución normal. Y cuando un programa, en su ejecución normal revienta luego de unos pocos minutos de proceso, este "enlentecimiento" se torna un problema grave, porque para intentar depurarlo debemos esperar una eternidad hasta llegar al problema, cada vez que lo probamos. Ahora tengo un mecanismo interesante para resolver el problema de analizar el error con el depurador sin esperar tanto tiempo.

miércoles, 19 de noviembre de 2014

Más novedades en la depuración con ZinjaI

Ya les comentaba en otros posts que venía haciendo cambios importantes en ZinjaI para la próxima versión, y especialmente en lo relacionado a la depuración y el manejo de inspecciones. Y como algunos son cambios muy grandes, esta próxima versión se viene demorando bastante. Pero creo que la espera va a valer la pena, porque así como por un lado estos cambios son grandes y lentos de implementar, por el otro lado los resultados son especialmente útiles. Todo rediseño en las tripas de un software, si se hace después de un tiempo prudencial y de haber aprendido mucho de los errores (u ordenado más precisamente, de haber aprendido de muchos errores), facilita el trabajo futuro, permitiendo pensar en cosas que antes no eran posibles o requerían demasiado trabajo. Y les vengo a mostrar un ejemplo de eso, que parece bastante mágico si se lo mira desde el ángulo adecuado.

lunes, 1 de septiembre de 2014

Entendiendo la tabla de inspecciones (parte 2)

Siguiendo con esta serie de artículos sobre cómo hace ZinjaI para mostrar las inspecciones durante la depuración, ahora voy contar un poco más qué pasa por debajo, en gdb, el verdadero depurador. Si alguien alguna vez usó directamente gdb sabrá que su interfaz es básicamente una linea de comandos, donde uno ingresa por ejemplo "break hola.cpp:12" para agregar un punto de interrupción, o "print x" para ver qué hay en la variable "x". Sin embargo, cuando uno programa un front-end para gdb (una interfaz, como ZinjaI), puede utilizar un conjunto diferente de comandos que otorgan una salida más uniforme y predecible para que analizarla automáticamente sea más simple y eficiente. Esta interfaz alternativa (también tipo linea de comandos) se conoce como MI (creo que por machine-interface).

jueves, 31 de julio de 2014

Entendiendo la tabla de inspecciones (parte 1)

La tabla de inspecciones de ZinjaI es esa grilla que aparece abajo a la izquierda al iniciar una depuración, donde podemos escribir nombres de variables y otras expresiones C++ para ver cuánto valen en cada pausa de la ejecución. Hay algunos detalles de interfaz conviene conocer como usuario de ZinjaI, y hay otros sobre la implementación interna de la misma y su comunicación con gdb que se podrían ignorar tranquilamente, pero igual resultan interesantes. Y es que desde hace un buen tiempo venía pensando en reescribir en ZinjaI todo el código relacionado a esta tabla y al manejo de inspecciones en general. Ahora que la reescritura ha avanzado bastante y las cosas se van definiendo, aprovecho para contarles de qué se trata y documentar un poco algunos trucos que se usan por debajo.

martes, 24 de junio de 2014

Sobre señales y volcado de datos

Creo que para la mayoría de los programadores, la depuración es la etapa que más tiempo consume en un desarrollo. A mi me gusta modificar un poco la regla del noventa-noventa para decir que el segundo 90% corresponde a la depuración. Depurar implica probar un programa para detectar errores, identificar sus causas, plantear posibles soluciones, y aplicar el mismo proceso recursivamente sobre estas soluciones, hasta que la recursión se corte porque ya [creemos que] no hay errores.

La parte de identificar la causa puede ser muy tediosa cuando llegar al punto en que se produce o manifiesta el error lleva bastante tiempo de ejecución. Porque el procesamiento previo es costoso, porque hay que cargar muchos datos, porque depende de acciones manuales del usuario, porque no sabemos bien cuándo va a manifestarse, por lo que sea. Y a veces hay que repetirlo y repetirlo para ir acotando el problema, porque cuando vemos el error, nos damos cuenta de que nos falta información, de que tenemos que modificar un poco el código para que genere y/o muestre más resultados intermedios y volver a probar. Hoy les comento un truco que, utilizando señales, permite generar en cualquier momento, desde cualquier punto de la ejecución, un volcado de datos ad-hoc y arbitrario hacia un archivo, para analizar el estado del programa.

martes, 17 de junio de 2014

Corrigiendo un ejecutable sin reiniciar la depuración

En este post les vengo a mostrar una funcionalidad nueva en ZinjaI, que verán en la próxima versión (a publicarse dentro de pocos días), y que bien presentada parece mágica, aunque está  lejos de serlo. Con el ejemplo adecuado, puedo hacer que se parezca al maravilloso Edit & Continue de Visual Studio (la funcionalidad que realmente me maravilla y envidio de ese IDE que tan poco conozco). Consiste en la posibilidad de modificar el ejecutable mientras se está ejecutando, durante una pausa en una depuración, alterando el código fuente, recompilando, y continuando luego de la pausa desde el mismo punto con el binario nuevo como si nada. Estoy a años luz de ofrecer algo como eso de verdad, pero puedo hacer algunos trucos simples para emularlo en casos muy muy particulares. Vean el video y luego sigan leyendo.

miércoles, 5 de junio de 2013

Sobre optimizaciones y precisión numérica

Recibí un correo diciendo que el siguiente código en PSeInt:
    a<-rc(13);
    Escribir a<a;

evidenciaba un error de lógica, ya que el resultado daba Verdadero, cuando debiera dar Falso (un número no puede ser menor que sí mismo). Pruebo el código en GNU/Linux y da Falso, pero el usuario dijo que usaba Windows. Entonces voy a Windows y efectivamente da Verdadero. Así que abro ZinjaI y me dispongo a Depurar en Windows, pero he aquí la sorpresa: en el depurador da Falso. Cuando se trata de variables sin inicializar, o manejo de direcciones de memoria, no es raro que una versión Debug dé mejor que una Release, pero en este caso estaba seguro que no se trataba de eso. Estaba a punto de ponerme a analizar el código objeto generado en cada caso (o sea, leer ensamblador, o tal vez saltar por la ventana, estaba decidiendo), pero antes noté algo interesante.

lunes, 20 de mayo de 2013

Depuración (parte 3): Controlando la ejecución II

En el post anterior mencioné los mecanismos básicos para controlar la ejecución del programa: puntos de interrupción, step over y step in. En esta tercera parte voy a completar la idea con algunos más para detener el programa y una aclaración importante sobre el nivel de granularidad con que se pueden indicar estos puntos de la ejecución en el código fuente. También, les voy a contar sobre algunos otras formas especiales para volver atrás en el tiempo o alterar (casi) arbitrariamente la ejecución, cosas que en la práctica permiten analizar varias veces un mismo error, o ensayar algunas soluciones antes de codificarlas. La ventaja radica en no tener que reiniciar el programa y llegar nuevamente hasta el punto conflictivo, lo cual puede llevar bastante tiempo o ser tedioso. Pero además, como efecto colateral, esto sentará una base que permitirá hacer algunos trucos para sortear ciertas limitaciones más adelante cuando hablemos de inspección de expresiones y otras acciones similares.

martes, 23 de abril de 2013

Depuración (parte 2): Controlando la ejecución I

Habiendo introducido las nociones básicas en la parte 1, vamos a profundizar sobre los dos aspectos prácticos más importantes: el control de la ejecución y la inspección de los datos en memoria. En esta parte 2 (y la que sigue) voy a hablar del control de la ejecución. Con el control de la ejecución me refiero a cómo definimos cuándo y hasta dónde se ejecuta. Hay que saber que el mecanismo básico es el punto de interrupción, pero cualquier depurador nos provee un conjunto de instrucciones para avanzar de a poco sin tener que explicitar nuevos puntos de interrupción. Por ejemplo, si estoy detenido en cierto punto y quiero que ejecute una sola linea y vuelva a detenerse (ejecución paso a paso), tendría que poner un punto de interrupción en la siguiente línea y decirle luego que continúe ejecutando, y una vez detenido nuevamente quitar ese punto de interrupción. Si bien es un mecanismo válido, los depuradores tienen instrucciones para hacer todo esto junto y de forma transparente, de modo que al usuario el usuario ni se entere de ese punto de interrupción auxiliar. Y así, hay comandos para avanzar por linea o por instrucción (que no es lo mismo), hasta llegar a cierta línea, o hasta salir de cierta función, etc. También hay mecanismos para alterar la ejecución y volver a ejecutar una línea que ya pasamos, y otros trucos útiles. De eso habla este post.

lunes, 8 de abril de 2013

Depuración (parte 1): ¿para qué sirve y cómo funciona?

Si hay una habilidad imprescindible en todo buen programador, es la habilidad para depurar un programa. La depuración es en general el proceso de corregir errores, pero cuando hablamos de programación, yendo a la práctica, la clave es ejecutar el programa con un "depurador", una herramienta que nos permite interrumpirlo en cualquier momento para ver qué está haciendo o qué tiene en su memoria. Un programador habilidoso en la depuración, sabe dónde interrumpirlo o qué buscar en la memoria a la hora de encontrar la causa de un problema, o entender cierto comportamiento. Y esto se adquiere con la práctica, como todo en la programación. Hay que conocer qué pueden hacer en teoría las herramientas, pero la práctica y la experiencia nos ayudarán a decidir cómo utilizarlas. Sin embargo, hay muchas funcionalidades básicas de un depurador que puede aprovechar incluso el principiante para entender cómo funciona un ejemplo o una estructura en particular.

El problema es que a pesar de lo importante que nos parezca a muchos programadores, cuando se enseña a programar en general se le da poco y nada de importancia. En la mayoría de los casos, los libros de programación no dedican un capítulo a esto, ni los docentes dedican una de sus clases. Pareciera que es algo para aprender cuando ya sabemos programar, para dejar para cursos avanzados y segundas partes, y esa idea no puede estar más equivocada. Las nociones básicas y el hábito de usar un depurador se deben inculcar desde temprano (desde que dejamos el pseudocódigo y pasamos a un lenguaje real diría yo). Por eso, en esta serie les voy a empezar a contar a los principiantes qué se puede hacer con un depurador, y daré algunas pistas para los más curiosos o avanzados de cómo hace lo que hace, aunque esto último no sea necesario saberlo para comenzar a utilizarlo.