como ver paso a paso la ejecucion de cada linea de una macro expansion usando gdb

5 minutos de lectura

avatar de usuario de mezda
mezda

tengo una macro cuya definición ocupa alrededor de 50 líneas y tiene muchas declaraciones ‘if else’. Esta definición de macro aparece en un archivo .h. Estoy ejecutando ‘gdb en modo TUI’, pero cuando la ejecución llega a esa macro, la ventana de código se queda en blanco y regresa solo después de que se ejecuta el código de la macro. Quiero ver la ejecución línea por línea del código macro completo. Dígame cómo se puede hacer eso (una forma es reemplazar la macro con su definición en el código y luego volver a compilarla. No quiero usar esta opción ya que hay varias macros de este tipo en mi código).

Cualquier ayuda será apreciada. deseando obtener la solución para este problema. Por favor, hágame saber si hay alguna otra forma de solucionar este problema en lugar del uso de un archivo preprocesado. Tengo un código que se ejecuta en varios cientos de archivos .c y .h.

Una opción es preprocesar completamente su archivo C, expandir todas las macros en él y luego compilar el archivo preprocesado resultante.

Por ejemplo, considere este sencillo programa en C:

// file: prep.c
#include <stdio.h>

#define MY_BIG_MACRO \
  int i; \
  printf("integers from 0 to 9:\n"); \
  for (i = 0; i < 10; i++) \
    printf("%d ", i); \
  printf("\n");

int main(void)
{
  MY_BIG_MACRO
  return 0;
}

Compílelo, guardando los archivos temporales (incluido el código fuente preprocesado):

gcc -Wall -O2 -g -std=c99 prep.c -o prep.exe -save-temps

Esto debería darle una versión preprocesada de prep.c, prep.i (abreviado por brevedad):

# 1 "prep.c"
# 1 "C:\\MinGW\\msys\\1.0\\home\\Alex//"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "prep.c"

# 1 "c:\\mingw\\bin\\../lib/gcc/mingw32/4.6.2/../../../../include/stdio.h" 1 3

...

int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) vswscanf (const wchar_t * __restrict__,
         const wchar_t * __restrict__, __gnuc_va_list);
# 3 "prep.c" 2
# 11 "prep.c"
int main(void)
{
  int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) printf("%d ", i); printf("\n");
  return 0;
}

Ahora quiere deshacerse de las líneas #. De una forma u otra, si se dejan, afectarán la información de depuración. Sorprendentemente, eso significa que la macro no aparecerá expandida en gdb.

Agradecidamente, grep puede ayudar (no soy un profesional de grep, así que verifique si los parámetros son correctos, pero parecen funcionar para mí en Windows con MinGW x86):

grep ^[^\#].*$ prep.i > prepi.c

Esto le dará una versión simplificada de prep.i en prepi.c:

typedef unsigned int size_t;
typedef short unsigned int wchar_t;
typedef short unsigned int wint_t;

...

int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) vswscanf (const wchar_t * __restrict__,
         const wchar_t * __restrict__, __gnuc_va_list);
int main(void)
{
  int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) printf("%d ", i); printf("\n");
  return 0;
}

Ahora puedes compilarlo:

gcc -Wall -O2 -g -std=c99 prepi.c -o prepi.exe

Y ejecútalo gdb:

gdb prepi.exe

Ejecute los siguientes comandos:

b main
r
l

Esto ejecutará la aplicación hasta que main() y enumere el código fuente relacionado con el punto de interrupción alcanzado:

(gdb) b main
Breakpoint 1 at 0x40643f: file prepi.c, line 184.
(gdb) r
Starting program: C:\MinGW\msys\1.0\home\Alex\prepi.exe
[New Thread 7340.0x20c4]

Breakpoint 1, main () at prepi.c:184
184       int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) pri
ntf("%d ", i); printf("\n");
(gdb) l
179              const wchar_t * __restrict__, __gnuc_va_list);
180     int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) vswscanf (c
onst wchar_t * __restrict__,
181              const wchar_t * __restrict__, __gnuc_va_list);
182     int main(void)
183     {
184       int i; printf("integers from 0 to 9:\n"); for (i = 0; i < 10; i++) pri
ntf("%d ", i); printf("\n");
185       return 0;
186     }
(gdb)

Como puede ver, el cuerpo de la macro ahora está a la vista.

Un pequeño problema aquí es que las macros de varias líneas (las que continúan con \) se expanden en una sola línea. No he encontrado una opción para expandirlos en varias líneas, pero puede hacerlo manualmente.

  • ¿A los votantes negativos les importa explicar qué es exactamente lo que está mal en mi respuesta? Podría aprender una cosa o dos de tal explicación.

    – Alexei Frunze

    2 de octubre de 2012 a las 8:17

  • gracias por tu tiempo y respuesta. ¿Hay alguna otra forma de resolver el problema en lugar del uso de un archivo preprocesado? Tengo un código que se ejecuta en varios cientos de archivos .c y .h.

    – mezda

    2 de octubre de 2012 a las 8:17

  • No conozco otra forma, aunque podría haber (ya sea con gcc/gdb o con otro conjunto de herramientas). Lo siento.

    – Alexei Frunze

    2 de octubre de 2012 a las 8:19

  • O_O, wow, ahora que es una respuesta elaborada, detallada e inteligente. No puedo imaginar por qué alguien votaría negativo. Realmente muy útil, gracias por tomarse el tiempo para publicarlo.

    – Leo Ufimtsev

    31/03/2016 a las 18:45

  • Es extraño que el entendimiento macro de GDB sea restringido a esto

    – tontería

    18 de mayo de 2017 a las 13:28

“Uno no lo hace simplemente step en macros”.

Todavía tienes algunas opciones:

  1. Use el preprocesador, como recomienda @WhozCraig.
  2. Para un poco menos de código, convierta sus macros en funciones y vuelva a compilar.
  3. Si absolutamente no desea volver a compilar y se siente cómodo con el código ensamblador, puede usar stepi para ejecutar su macro una instrucción de máquina a la vez.

  • Iría por el n. ° 3 si fuera yo antes de seguir mi propio consejo y comenzar a verter 20,000 líneas de fuente preprocesada, pero me siento cómodo en el extremo comercial de un desensamblador. Tal vez el OP también lo sea.

    – WhozCraig

    2 de octubre de 2012 a las 8:01

Si todo lo anterior no funciona, realmente deberías volver a usar printf/fprintf dentro de su gran macro.

Tuve que lidiar con una MACRO de 300 líneas, enterrada en lo profundo de la biblioteca. Esto fue más fácil que compilar a mano y tratar con archivos posprocesados.

¿Ha sido útil esta solución?