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.

Empecemos pensando como usuarios. Podemos interactuar con la tabla de inspecciones en una pausa cualquiera de la depuración (por ejemplo cuando se llega a un punto de interrupción). La acción más usual es ingresar allí, en la columna "expresiones" nombres de variables para las cuales queremos ver su contenido. No solo se puede inspeccionar variables, sino que se puede armar expresiones completas (siempre que el evaluarlas no modifique nada, o sea todo const), pero por el momento limitémonos a variables. Las variables pueden ser en principio tipos simples (punteros, int, float, etc) o tipos compuestos (mayormente clases y arreglos). En el primer caso vemos directamente su valor en la última columna, en el segundo vemos pares "nombre=valor" para cada atributo de una clase o estructura, o vemos una lista de valores separados por comas para un arreglo o matríz (ejemplo en la siguiente imagen).


Además, la columna nivel muestra un número o un asterisco. Ya sabemos que en C++ un mismo identificador puede referirse a distintas variables en distintos scopes (ámbitos). Si la columna nivel muestra un número, quiere decir que la expresión corresponde a un ámbito particular (el que en la tabla de trazado inverso tiene el mismo número). Entonces, no importa dónde vaya la depuración, siempre se evalúa la misma variable/expresión en su mismo ámbito. Es importante para el alumno comprender el concepto de ámbito de una variable, y entender qué está realmente inspeccionando. Sin embargo, el nivel tiene una granularidad fija (funciones/métodos) e insuficiente. Varios scopes pueden anidarse dentro de un mismo nivel (por ejemplo, un variable redeclarada dentro de un for) y esto no se refleja en la tabla. No veo una forma simple de corregirlo (más granularidad) sin introducir ruido (confundir más que ayudar). Consideremos que la expresión puede ser efectivamente una expresión arbitraria (con varios operadores y operandos), y entonces no tener una referencia clara en el código (como sí podría tener una variable: el punto en donde se declara). En general uno no repite los nombres al anidar para evitar esta clase de problemas y entonces para la mayoría de los casos el nivel es suficiente.


Por otro lado, si en la columna nivel hay un asterisco, entonces la inspección se evalúa en donde sea que se pare el depurador, o en cualquier nivel que seleccionemos luego en la tabla de trazado inverso (con doble-click). Así, una misma expresión hace referencia a distintas variables en distintos momentos. Al finalizar la depuración, todas las inspecciones que quedan cargadas para la próxima ejecución funcionarán de esta manera (llamémosla ámbito-independiente), ya que no se puede volver a identificar exactamente el mismo ámbito en una segunda ejecución si el programa no sigue exactamente el mismo flujo y no se detiene en exactamente los mismos puntos. Imaginen por ejemplo una función recursiva, donde hay muchas variables de igual nombre, en diferentes copias todas de una misma función.

En conclusión, estoy relativamente conforme con esta política. Aunque sé que no es perfecta, va a seguir así hasta que alguien me sugiera algo mejor y factible de implementar con base en gdb. Siempre, analizándolo desde el punto de vista del usuario, pensando tanto en un alumno que no tiene todavía los conceptos del todo claros, como en un programador avanzado que intenta desarrollar con ZinjaI algún proyecto serio y complejo de su trabajo. Entonces, a nivel de usuario las cosas se van a mantener en esta linea. Tal vez sí vean algo nuevo, como pestañas en la tabla de inspecciones, con algunas especiales (una para ver automáticamente todas las variables locales, o tal vez otra para ver los registros, etc), pero todo funcionando sobre las mismas ideas básicas. También puede que noten otros cambios menores como una mejor organización y contextualización del menú contextual, la posibilidad de aplicar acciones a múltiples inspecciones (por ahora todo se tiene que hacer una por una), o que zinjai será capaz de señalar cuales inspecciones pertenecen a otro hilo de ejecución (algo que actualmente no se considera). Pero internamente sí va a haber muchos cambios, porque estoy reescribiendo todo esto casi de cero desde hace ya un tiempo, así que espero que ande todo mejor (más rápido y con menos errores) para la próxima release. Habiendo dejado más o menos claro lo que se pretende para el usuario, en el siguiente post les contaré mejor qué ocurre por debajo entre ZinjaI y gdb para que esto sea posible. Conocer algunos detalles puede llevarlos a sacarle más provecho a estas funcionalidades.

No hay comentarios:

Publicar un comentario