2011-07-20 11 views
9

en x86 (64-bit o 32-bit) Linux - por ejemplo:En un manejador de señal, ¿cómo saber dónde se interrumpe el programa?

void signal_handler(int) { 
    // want to know where the program is interrupted ... 
} 

int main() { 
    ... 
    signal(SIGALRM, signal_handler); 
    alarm(5); 
    ... 
    printf(...); <------- at this point, we trigger signal_handler 
    ... 
} 

En signal_handler, ¿cómo podemos saber que estamos interrumpido en printf en main()?

Respuesta

11

Usa sigaction con SA_SIGINFO establecido en sa_flags.

código Prototipo:

#define _GNU_SOURCE 1 /* To pick up REG_RIP */ 
#include <stdio.h> 
#include <signal.h> 
#include <assert.h> 

static void 
handler(int signo, siginfo_t *info, void *context) 
{ 
    const ucontext_t *con = (ucontext_t *)context; 

    /* I know, never call printf from a signal handler. Meh. */ 
    printf("IP: %lx\n", con->uc_mcontext.gregs[REG_RIP]); 
} 

int 
main(int argc, char *argv[]) 
{ 
    struct sigaction sa = { }; 
    sa.sa_flags = SA_SIGINFO; 
    sa.sa_sigaction = handler; 
    assert(sigaction(SIGINT, &sa, NULL) == 0); 
    for (;;); 
    return 0; 
} 

Ejecutar y golpeó Ctrl-C. (Use Ctrl- \ para terminar ...)

Esto es para x86_64. Para 32 bits x86, use REG_EIP en lugar de REG_RIP.

[editar]

Por supuesto, si usted está realmente en una función de biblioteca (como printf) o una llamada al sistema (como write), el registro PIR/EIP podría señalar alguna parte divertida ...

Es posible que desee utilizar libunwind para rastrear la pila.

+0

¡Muchas gracias! ¡Esto es lo que necesito! – flyingbin

+0

printf generalmente no es reentrante, así que si usted está en printf cuando recibe la señal, esto probablemente imprimirá basura o se bloqueará ... –

+2

@Chris: Sí, lo sé. Este programa de ejemplo funciona bien por sí mismo (probablemente), y fue la forma más fácil de demostrar el principio.Pero es por eso que incluí el comentario y lo llamé "código de prototipo" :-) – Nemo

1

Dependiendo de su sistema operativo/plataforma, que podría estar en una variedad de áreas:

  • En la pila actual de un número determinado de registros profundos
  • En una/la pila de interrupción
  • Dentro de algunas tipo de devolución de llamada asociada con su rutina de señal ...

Sin tener información adicional, no creo que podamos rastrear esto mucho más. Agregar una etiqueta C/C++ podría generar más respuestas y vistas.

+0

Gracias. Intento usar sigaction. Parece que ucontext_t puede ayudar un poco. Pero no hay ningún progreso ahora ... – flyingbin

0

Idea de solución alternativa: si solo hay un pequeño número de lugares que pueden activar un manejador de señal o si solo está interesado en qué bloque más grande sucedió, podría mantenerlo en una variable.

entered = 1; // or entered = ENTER_PRINTF1; 
printf(....); 
1

En signal_handler, ¿cómo podemos saber que estamos interrumpido en printf en main()? No puede, al menos no desde una perspectiva C, C++ o POSIX. Su sistema operativo puede proporcionar llamadas específicas del sistema operativo que le permiten insertar en la pila. Eso es más que un poco dudoso, sin embargo.

Con señales basadas en temporizador, qué instrucción desencadenó la señal es un lanzamiento de la moneda.

Cuestiones relacionadas