Libcwd es una biblioteca C ++, escrita por Carlo Wood , para agregar soporte de depuración en tiempo de ejecución para aplicaciones C ++ , particularmente para código desarrollado con GNU Compiler Collection . La funcionalidad que agrega la biblioteca a una aplicación se puede dividir en tres categorías:
- Salida de depuración basada en Ostream .
- Acceso en tiempo de ejecución a la información de depuración.
- Acceso en tiempo de ejecución a la administración de la asignación de memoria .
Plataformas compatibles
Aunque el código de la biblioteca en sí mismo intenta ser estrictamente ISO C ++ y ajustarse a POSIX tanto como sea posible, para lograr los puntos 2 y 3, se necesita un código bastante especializado, específico para la arquitectura en la que se ejecuta la aplicación. Libcwd se limita a una arquitectura estrecha por esta razón: debe compilarse con el compilador GNU y exige que el código objeto sea ELF de 32 o 64 bits y que la información de depuración generada por el compilador sea DWARF-2 .
La compilación de libcwd da como resultado dos bibliotecas: una que es segura para subprocesos (libcwd_r) y una versión (libcwd) sin soporte para subprocesos. La versión segura para subprocesos depende de detalles aún más específicos de la arquitectura (es decir, la biblioteca GNU C). Como resultado, un libcwd con todas las funciones básicamente solo es adecuado para el desarrollo en plataformas Linux .
Sin embargo, libcwd puede configurarse para eliminar el soporte de subprocesos, depurar la asignación de memoria y / o leer la información de depuración de ELF y DWARF-2, hasta que solo quede el soporte de salida de depuración de ostream. De esta manera, uno puede usarlo para desarrollar una aplicación en Linux hasta que sea robusta, y aún tener la salida de depuración en otras plataformas (POSIX), aunque una libcwd completa no esté disponible allí, siempre que no se necesite seguridad para subprocesos. para la salida de depuración en esas plataformas: dos o más subprocesos que escriben la salida de depuración en el mismo ostream pueden causar una salida bastante desordenada donde la salida de una línea comienza en el medio de otra, sin soporte de subprocesos.
Salida de depuración basada en Ostream
Libcwd proporciona varias macros que son fácilmente ampliables, lo que permite al usuario hacer básicamente cualquier cosa que uno pueda hacer normalmente con ostreams. Sin embargo, si uno solo quiere escribir la salida de depuración, dos macros serán suficientes: Dout y DoutFatal. Este último se utilizará para la salida de depuración fatal, después de lo cual se debe finalizar la aplicación. Por ejemplo:
if ( error ) DoutFatal ( dc :: fatal , "Ocurrió un error irrecuperable" );
La diferencia con Dout es que cuando la aplicación se compila sin código de depuración, la macro Dout () se reemplaza con nada, mientras que DoutFatal () se reemplaza con código que imprime su salida y termina (de una manera que el usuario puede definir).
La salida de depuración simple se escribe usando Dout, de la siguiente manera:
Dout ( dc :: notice , "llamado desde" << location_ct ( CALL_ADDR ));
donde el segundo parámetro puede contener '<<' para escribir cualquier tipo u objeto en el flujo de salida de depuración (un location_ct en este caso).
'Dc :: fatal' y 'dc :: notice' son 'canales' de depuración, que pueden activarse o desactivarse. El usuario puede crear y utilizar cualquier número de canales de depuración personalizados, además de los predeterminados . También es posible crear algo más que el objeto ostream de salida de depuración predeterminado 'libcw_do' y, por lo tanto, escribir la salida en más de un ostream. Cada objeto de depuración, que representa un ostream, puede, a su vez, activarse y desactivarse por separado.
Acceso en tiempo de ejecución a la información de depuración
Esta información incluye la posibilidad de buscar archivos de origen, ubicaciones de números de línea y nombres de funciones. Como resultado, es posible, por ejemplo, escribir una salida de depuración que imprima quién es el llamador de una función determinada, o imprimir el nombre de la función actual, incluso si esa función es una plantilla compleja. Por ejemplo,
PERSIST: Se llama a PersistXML :: serialize_builtin("M_hostname", @ 0xbfc1f214).
Acceso en tiempo de ejecución a la administración de la asignación de memoria
Libcwd mantiene una administración interna de las asignaciones de memoria. Esto le permite a uno hacer cosas como verificar fugas de memoria , imprimir una descripción general de la memoria asignada (de una manera muy poderosa, lo que permite filtrar sobre cualquier cosa: expresiones regulares para nombres de bibliotecas, nombres de funciones (exigidas o no) y / o tiempo intervalos durante los cuales se realizaron las asignaciones).
La biblioteca también proporciona algunas funciones globales que se pueden llamar desde un depurador, como gdb , lo que permite al desarrollador averiguar rápidamente a qué asignación apunta un puntero determinado. Por ejemplo,
(gdb) llamar a cwdebug_alloc (0x8a19450) 0x8a19450 puntos dentro de una asignación de memoria que comienza en 0x8a19448 inicio: 0x8a19448 tamaño: 12 tipo: char ** descripción: Matriz de argumentos de la línea de comandos pasados a libcw_app_ct :: options_done_event. ubicación: libcw_app.cc:304 en función: libcw_app_ct :: libcw_init (int, char * const *) cuando: 00: 31: 09.888760 (gdb) l libcw_app.cc:304