1

I want to do something more useful with assert than just aborting, that includes flushing some open files, and printing the stack trace.

I read a few articles on assert such as Assertions by Andrei Alexandrescu which talks about how to implement assertions. However I want to replace the assert handler by my own even in third party libraries which are used by my program. I understand most (but not all) compilers (gcc, MSVC, clang) & libraries (Qt,boost) which I use has options to set user defined assert handler. However at present I am not interested to have Abort,Retry,Ignore feature for the assert, and so not looking to my code in compiler/library specific assert handler.

As I understand, assert calls abort which sends SIGABRT signal, can I trap this signal and do the useful tasks?

abir
  • 1,797
  • 14
  • 26
  • 1
    FYI, assert is a debug thing. So I wouldn't use it for anything other than unit testing. Also why not just do `if(a == b) DoMySpecialFunction` instead of assert? – Caesar Feb 14 '13 at 05:55
  • see handling sigabrt - http://stackoverflow.com/questions/8934879/how-to-handle-sigabrt-signal – djechlin Feb 14 '13 at 05:56
  • @Caesar The question does NOT suggest the `useful things` are something not related to testing. Printing a stack trace and flushing some of the files & closing is part of debugging. – abir Feb 14 '13 at 06:10

1 Answers1

2

How about hijacking assert() with your own implementation? On Unix systems you can use the LD_PRELOAD trick to hijack __assert_fail (assert() itself is just a macro which calls the later one).

Another possibility would be to capture SIGABRT as you suggest. However I see a few requirements:

  1. A way to distinguish a call to assert() from the other possible causes of SIGABRT being received. That could be a call kill() from another thread or process, or even a call to abort() itself from the sanity checks of the heap management functions (free(), malloc() ...). This can be done by the ABRT signal handler, by checking the execution stack of the process or the context passed to sigaction() in some systems (that void* parameter nobody knows how to use).

  2. If you decide to capture SIGABRT you should bear in mind that signal handlers are not "legally" allowed to do much (not even a call printf), which make them bad candidates to do "something more useful" as you ask.

    So it would be better to have a dedicated thread permanently awating for the signal with sigwait() (based on how abort() is implemented this might not be an option) or notified by the signal handler whenever a signal is received ao that it can do the bulk of the work out of a signal handler context.

    Also, it is important to note that, if in its implementation, SIGABRT is sent by abort() specifically to its calling thread then its reception will be synchronous and you are free to call asynchronous-unsafe functions.

  3. By capturing the signal yourself and modifying the signal mask of the threads you might impact the signal behaviour of those third party libraries you mention (they could be capturing SIGABRT themselves).

  4. Even if you capture SIGABRT with your own signal handler you wont be able to return to normal execution the signal was sent from a call to abort() because (quoting from alarm(8)):

If the SIGABRT signal is ignored, or caught by a handler that returns, the abort() function will still terminate the process. It does this by restoring the default disposition for SIGABRT and then raising the signal for a second time.

Community
  • 1
  • 1
fons
  • 4,905
  • 4
  • 29
  • 49
  • Possibly because it is non-standard and there are so many versions, like libc/klibc has __assert_fail, bionic has __assert/__assert2 osx has __assert, msvc has _assert/_wassert, newlib has __assertfail , boost has assertion_failed/assertion_failed_msg Qt has qt_assert etc. And every other library has its own implementation. However all of them supposed to call abort. – abir Feb 14 '13 at 11:02
  • @abir I added some recommendations on what to take into account if you decided to capture the ABRT signal. – fons Feb 14 '13 at 11:29
  • I will look at how to distinguish between abort due to assert from other cases, but I can live without that. I understand it will any way terminate the application, but as I mentioned I do not interested in `Abort,Retry,Ignore` feature either. I am looking at whether I can have portable implementation of stack trace even if that is `illegal`. I will be happy to hear about any known way to do it. Thank you for the detailed answer. – abir Feb 14 '13 at 12:02
  • @abir the problem *per se* is not so much that printing a stack trace is *illegal* it's more the reason behind tagging it as such. Using any non-reentrant function (and non-thread safe, obviously) in a signal handler can be disastrous. Deadlocks, crashes, memory corruptions or even a security breach (at least in theory) if you manage to make an exploit out of how memory is corrupted ... You could allow yourself to do it if you were under total control of the execution environment. But, as I understand it, your intention is to use this mechanism with third party libraries and software. – fons Feb 20 '13 at 13:40