2010-01-11 15 views
9

Estoy tratando de depurar un simple recolector de basura stop-and-copy (escrito en C) usando GDB. El GC funciona mediante el manejo de SIGBUS. Establecí un punto de interrupción en la parte superior de mi controlador de señal SIGBUS. Le dije a GDB que pase SIGBUS a mi programa. Sin embargo, no parece funcionar.gdb: establezca un punto de interrupción para un manejador SIGBUS

el siguiente programa (que se explica en línea) muestra la esencia de mi problema:

#include <stdio.h> 
#include <sys/mman.h> 
#include <assert.h> 
#include <signal.h> 

#define HEAP_SIZE 4096 

unsigned long int *heap; 

void gc(int n) { 
    signal(SIGBUS, SIG_DFL); // just for debugging 
    printf("GC TIME\n"); 
} 

int main() { 

    // Allocate twice the required heap size (two semi-spaces) 
    heap = mmap(NULL, HEAP_SIZE * 2, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, 
       -1, 0); 
    assert (heap != MAP_FAILED); 
    // 2nd semi-space is unreadable. Using "bump-pointer allocation", a SIGBUS 
    // tells us we are out of space and need to GC. 
    void *guard = mmap(heap + HEAP_SIZE, HEAP_SIZE, PROT_NONE, MAP_ANON | 
        MAP_SHARED | MAP_FIXED, -1, 0); 
    assert (guard != MAP_FAILED); 
    signal(SIGBUS, gc); 
    heap[HEAP_SIZE] = 90; // pretend we are out of heap space 
    return 0; 
} 

me compilar y ejecutar el programa en Mac OS X 10.6 y obtener la salida espero:

$ gcc debug.c 
$ ./a.out 
GC TIME 
Bus error 

Quiero ejecutar y depurar este programa usando GDB. En particular, quiero establecer un punto de interrupción en la función gc (en realidad, el controlador de señal gc). Naturalmente, tengo que decirle BGF para no detener el SIGBUS así:

$ gdb ./a.out 
GNU gdb 6.3.50-20050815 (Apple version gdb-1346) (Fri Sep 18 20:40:51 UTC 2009) 
... snip ... 
(gdb) handle SIGSEGV SIGBUS nostop noprint 
Signal  Stop Print Pass to program Description 
SIGBUS  No No Yes  Bus error 
SIGSEGV  No No Yes  Segmentation fault 
(gdb) break gc 
Breakpoint 1 at 0x100000d6f 

Sin embargo, nunca alcanzamos el punto de interrupción:

(gdb) run 
Starting program: /snip/a.out 
Reading symbols for shared libraries +. done 

Program received signal EXC_BAD_ACCESS, Could not access memory. 
Reason: KERN_PROTECTION_FAILURE at address: 0x0000000100029000 
0x0000000100000e83 in main() 
(gdb) 

Al parecer, el manejador de la señal no se invoca (GC tiempo no es impreso). Además, todavía estamos en main(), en el mov de fallas:

0x0000000100000e83 <main+247>: movq $0x5a,(%rax) 

¿Alguna idea?

Gracias.

Respuesta

5

El mismo código (modificado para manejar SIGSEGV también) funciona como se esperaba en GDB en Linux; puede ser un error en OS X o el puerto de GDB a esa plataforma.

Google encuentra broken OS X behavior igual que el tuyo todo el camino de regreso en 10.1, con una especie de solución (set inferior-bind-exception-port off antes de ejecutar el programa).

(Hay un similar bug on Windows.)

+0

Gracias, es un error específico de OS X. La solución hace que el programa se ejecute hasta su finalización, pero gdb ya no se detiene en el punto de interrupción. Oh, bueno ... –

+0

Ver mi respuesta para una solución que le permite romper dentro del controlador. – LaC

4

¿Qué le parece poner un for(;;); después del printf(), ejecutando el programa normalmente, y luego conectando al proceso con gdb después de que se imprima GC TIME?

+0

Bueno, esto funcionaría. Pero, ¿no hay una solución más limpia? ¿Realmente no es posible establecer un punto de interrupción en un controlador SIGBUS? –

2

¿Por qué esperas conseguir un SIGBUS? SIGBUS generalmente significa error de alineación, en una arquitectura donde algunos tipos de datos tienen requisitos de alineación. Parece que solo intenta acceder a la memoria fuera de su área asignada, y esperaría que obtenga SIGSEGV en lugar de SIGBUS.

Editar:

Parece que el nombre de Mac OS X para lo que pienso de como SIGSEGV es SIGBUS. Por lo tanto, ignora esta respuesta.

Si sirve de ayuda, el punto de interrupción funciona como se espera (es decir, funciona) cuando pruebo el programa en un sistema Linux, con SIGBUS reemplazado por SIGSEGV.

Edición 2:

¿Se puede tratar de atrapar SIGSEGV en su programa también? Parece que el tipo de señal puede variar según el lugar en que esté mapeada la memoria en Mac OS X (acabo de leer la discusión here y here), ¿y tal vez podría arrojarse una señal diferente cuando ejecute el depurador?

+0

Espero un SIGBUS porque obtengo un SIGBUS cuando lo ejecuto sin gdb. Como sugieres, no es con lo que esperaba comenzar. –

5

Internamente, los malos accesos a la memoria provocan que la excepción de Mach EXC_BAD_ACCESS sea enviada al programa. Normalmente, esto se traduce en una señal SIGBUS UNIX. Sin embargo, gdb intercepta las excepciones de Mach directamente, antes de la traducción de la señal. La solución es darle a gdb el comando set dont-handle-bad-access 1 antes de ejecutar su programa. Luego se usa el mecanismo normal y se respetan los puntos de interrupción dentro de su manejador de señal.

Cuestiones relacionadas