2009-11-12 15 views
15

Quando un'asserzione fallisce con Visual C++ su Windows, il debugger si arresta, visualizza il messaggio e quindi consente di continuare (oppure, se nessuna sessione di debug è in esecuzione, offre di lanciare Visual Studio per te).Continuare a eseguire il debug dopo l'asserzione fallita su Linux?

Su Linux, sembra che il comportamento predefinito di assert() sia quello di visualizzare l'errore e chiudere il programma. Dal momento che tutta la mia afferma passare attraverso le macro, ho cercato di usare i segnali per aggirare questo problema, come

#define ASSERT(TEST) if(!(TEST)) raise(SIGSTOP); 

Ma anche se GDB (attraverso KDevelop) si ferma al punto corretto, non riesco a continuare oltre il segnale e l'invio manuale del segnale all'interno di GDB mi lascia semplicemente sospeso, senza controllo né di GDB né del processo di debug.

risposta

18

Si desidera veramente ricreare il comportamento di DebugBreak. Ciò interrompe il programma nel debugger.

Il mio googling di "DebugBreak linux" ha restituito severalreferences a questo pezzo di assembly inline che dovrebbe fare lo stesso.

#define DEBUG_BREAK asm("int $3") 

Allora il vostro assert può diventare

#define ASSERT(TEST) if(!(TEST)) asm("int $3"); 

Secondo Andomar int 3 fa sì che la CPU per aumentare interrupt 3. Secondo drpepper un modo più portatile per farlo sarebbe quello di chiamare :

raise(SIGTRAP); 
+1

Farà in modo che la CPU sollevi l'interrupt 3 (http://faydoc.tripod.com/cpu/int3.htm) Il debugger ha un gestore di interrupt registrato per l'interrupt 3 e interromperà il programma. – Andomar

+0

perfetto! prende un evento SIGTRAP, si ferma su una monetina e poi mi lascia continuare! molte grazie. – drpepper

+1

per renderlo un po 'più portatile, ho sostituito il gruppo con l'equivalente codice c: raise (SIGTRAP); funziona alla grande. – drpepper

10

È possibile configurare gdb per gestire segnali specifici in un modo diverso. Ad esempio, quanto segue causerà il mancato funzionamento di SIGSTOP come evento di arresto.

handle SIGSTOP nostop noprint pass

help handle all'interno di gdb vi darà ulteriori informazioni.

1

Hai provato a inviare un segnale SIGCONT al processo?

kill -s SIGCONT <pid> 
+0

ho provato, ma nulla sembra accadere ... – drpepper

2

Ancora migliore usabilità si ottiene con

/*! 
* \file: assert_x.h 
* \brief: Usability Improving Extensions to assert.h. 
* \author: Per Nordlöw 
*/ 

#pragma once 

#include <errno.h> 
#include <signal.h> 
#include <assert.h> 

#ifdef __cplusplus 
extern "C" { 
#endif 

#if !defined(NDEBUG) 
# define passert(expr)             \ 
    if (!(expr)) {              \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' failed.",    \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(SIGTRAP); \ 
    } 
# define passert_with(expr, sig)          \ 
    if (!(expr)) {              \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' failed.",    \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(sig); \ 
    } 
# define passert_eq(expected, actual)         \ 
    if (!(expected == actual)) {           \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' == `%s' failed.",  \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expected), __STRING(actual)); raise(SIGTRAP); \ 
    } 
# define passert_neq(expected, actual)         \ 
    if (!(expected != actual)) {           \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' != `%s' failed.",  \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expected), __STRING(actual)); raise(SIGTRAP); \ 
    } 
# define passert_lt(lhs, rhs)           \ 
    if (!(lhs < rhs)) {             \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' < `%s' failed.",   \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \ 
    } 
# define passert_gt(lhs, rhs)           \ 
    if (!(lhs > rhs)) {             \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' < `%s' failed.",   \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \ 
    } 
# define passert_lte(lhs, rhs)           \ 
    if (!(lhs <= rhs)) {             \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' <= `%s' failed.",  \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \ 
    } 
# define passert_gte(lhs, rhs)           \ 
    if (!(lhs >= rhs)) {             \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' >= `%s' failed.",  \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(lhs), __STRING(rhs)); raise(SIGTRAP); \ 
    } 
# define passert_zero(expr)           \ 
    if (!(expr == 0)) {             \ 
    fprintf(stderr, "%s:%d: %s: Assertion `%s' is zero failed.",  \ 
      __FILE__, __LINE__, __ASSERT_FUNCTION, __STRING(expr)); raise(SIGTRAP); \ 
    } 
#else 
# define passert(expr) 
# define passert_with(expr, sig) 
# define passert_eq(expected, actual) 
# define passert_lt(lhs, rhs) 
# define passert_gt(lhs, rhs) 
# define passert_lte(lhs, rhs) 
# define passert_gte(lhs, rhs) 
# define passert_zero(expr) 
#endif 

#ifdef __cplusplus 
} 
#endif 
1

È possibile sostituire assert con la propria versione che prevede pause() invece di abort(). Quando l'asserzione fallisce, il programma si interrompe ed è possibile eseguire gdb --pid $(pidof program) per esaminare il callstack e le variabili. Un vantaggio di questo approccio è che non è necessario avviare program in GDB.

file di intestazione (sulla base di /usr/include/assert.h):

#include <assert.h> 

#ifndef NDEBUG 
    void assert_fail(const char *assertion, const char *file, unsigned line, const char *function) 
    __attribute__ ((noreturn)); 
    #undef assert 
    #define assert(expr)   \ 
     ((expr)      \ 
     ? __ASSERT_VOID_CAST (0) \ 
     : assert_fail (__STRING(expr), __FILE__, __LINE__, __ASSERT_FUNCTION)) 
#endif /* NDEBUG */ 

Attuazione della assert_fail (basata su assert.c in glibc):

void assert_fail(const char *assertion, const char *file, unsigned line, const char *function) { 
    extern const char *__progname; 
    fprintf(stderr, "%s%s%s:%u: %s%sAssertion `%s' failed.\n", 
     __progname, 
     __progname[0] ? ": " : "", 
     file, 
     line, 
     function ? function : "", 
     function ? ": " : "", 
     assertion 
    ); 
    pause(); 
    abort(); 
} 
+0

Oh, bello. Posix ha davvero abbandonato la palla quando ha specificato il comportamento di interruzione quando 'NDBUG' non è stato definito. Chi è a posto pensa che un crash autoindotto sia appropriato per il debug e la diagnostica ... – jww

+0

Dove viene dichiarata la funzione "pausa"? – Brent

Problemi correlati