miércoles, 12 de diciembre de 2012

Trucos para depurar con ZinjaI

Este artículo bien podría llamarse "Tips para convertirse en un ZinjaI Master (parte 5)", pero preferí ponerle un título diferente donde aparezca la palabra "Depuración" ya que trata de este tema en particular, y los tips son algo más específicos. En la pestaña Depuración del cuadro de Preferencias (al cual se accede con el ítem "Preferencias..." del menú "Archivo"), los tres últimos ítems son bastante particulares, pero bien utilizados ayudan mucho.

El primero, "Mejorar inspecciones automáticamente según tipo", hace que cuando ingresemos (durante la depuración) una inspección de algún tipo configurado allí (se configuran con el botón "Opciones" que tiene al lado), ZinjaI la modifique automáticamente. Por ejemplo, cuando trabajamos con la clase std::string, usualmente queremos inspeccionar el contenido de la cadena, y no la estructura de la clase. En gcc, por ejemplo, esta clase tiene el contenido del string en un puntero llamado _M_p que está dentro de un struct miembro llamado _M_dataplus; y la clase tiene además otras cosas que en general no nos interesa ver, como el npos. En resúmen, si el string es s, no interesa evaluar solamente el valor de "s._M_dataplus._M_p" en lugar de "s". Sin esta opción, hay que buscar dentro de la clase el atributo que queremos, lo cual se hace fácilmente con un par de dobles clicks sobre el valor de la inspección y borrando luego las inspecciones que sobren, pero se torna tedioso y repetitivo. Con esta opción, ZinjaI puede hacerlo automáticamente.

Prueben crear un string s, asignarle alguna cadena, detener el depurador, e ingresar "s" en la tabla de inspecciones, para ver cómo al presionar Enter se convierte en "s._M_dataplus._M_p" y pueden ver directamente el valor de la cadena. Por defecto, ZinjaI tiene configurados los reemplazos para los tipos string y wxString, los cuales pueden tomar de ejemplo para crear reemplazos para inspecciones de cualquier otro tipo que quieran.


El ante-último elemento de la pestaña Depuración es "Archivo de definiciones de macros para gdb", y permite ingresar el nombre de un script que ejecutará gdb antes de comenzar la depuración. En este script se pueden definir varias cosas, pero lo más interesante son las macros, que son como comandos para el depurador definidos por el usuario. Por ejemplo, por defecto ZinjaI incluye macros para ver los contenedores STL. Si crean una "std::list<algo> la_lista" y quieren inspeccionarla, no van a ver nada útil, ya que verán la estructura de la clase list, que en este caso ni siquiera tiene a mano el contenido como pasaba con string. Para ver el contenido hay que saber cómo es por dentro esa clase, qué atributos seguir para encontrar los nodos y qué cast aplicar, nada fácil. Pero si ponen como expresión para inspeccionar ">plist int la_lista" verán el truco. Y si después le hacen click con el botón derecho y eligen "Mostrar en tabla separada" mejor todavía. El ">" al comienzo de la inspección le indica a ZinjaI que no es una inspección real, sino un comando que debe pasarle directamente a gdb; "plist" es una macro definida en ese archivo de macros, "int" es el tipo de datos que guarda la lista, y "la_lista" es el argumento. La macro plist está escrita por alguien que conoce como es la lista de gcc por dentro y sabe extraer los datos, y modificada por mí para que la salida se parezca a la de una clase común, y entonces ZinjaI pueda mostrarla en una tabla separada. Si ingresan ">help" verán una lista de las macros habilitadas, y si miran el archivo definido en "Archivo de definiciones de macros para gdb" pueden cambiarlas o agregar nuevas.


Y para terminar con las preferencias, una opción menos complicada y tal vez más útil es la que dice "Fuentes a evitar para el step in". Cuando estamos depurando y avanzamos con "Step In", si el depurador se detiene en algún archivo de esta lista, sigue avanzando un paso más. Por ejemplo, si estamos depurando un programa que llama a una función que recibe un string y queremos meternos en la función, el depurador primero se meterá en el constructor del string argumento. Una vez allí, hacemos click derecho sobre el trazado inverso y elegimos "Evitar detenerse para este fuente" para que ZinjaI nunca más se detenga dentro de un método de string; así la próxima vez se salteará el constructor e irá directamente a la función que queríamos depurar. Como ven, esta opción no hay que configurarla desde preferencias (a menos que quieran sacar algo de la lista), sino que es más fácil hacerlo durante la depuración, con el menú contextual del panel de trazado inverso.

Finalmente, quiero recomendar que se tomen unos segundos para observar las opciones del menú contextual de la tabla de Inspecciones, ya que allí tal vez encuentren algo útil que no esperaban. Por ejemplo, suelo querer recordar el valor que tenía una variable en algún momento, o en alguna corrida, para luego compararla con otro valor que tome. Para ello, teniendo la inspección del primer valor, puedo usar las opciones "Duplicar inspección" para obtener una copia y "Congelar valor" para que una de las copias ya no se actualice más. Otra opción útil es la de "Mostrar en ventana separada" para ver inspecciones muy largas que no entran en la tabla, como cadenas de texto.


Las que les acabo de comentar no son las mejores ni más importantes opciones de depuración, pero sí son, dentro de las útiles, las que suelen no saber que existen. Como dije en el post anterior, espero que estos trucos les sean útiles, pero también me pondría contento que si dan buenos resultados sirvan para que se les despierte la curiosidad y se acostumbren a investigar un poco más las opciones no tan a la vista de los programas que utilicen (no solo ZinjaI, sino en general).

No hay comentarios:

Publicar un comentario en la entrada