diff -r 069840de4fac src/core/abort.h --- a/src/core/abort.h Fri Jun 04 07:12:20 2010 +0900 +++ b/src/core/abort.h Fri Jun 04 15:44:47 2010 +1000 @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2008 INRIA + * Copyright (c) 2008 INRIA, 2010 NICTA * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,50 +15,120 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * + * Author: Original author unknown + * Quincy Tse */ #ifndef NS3_ABORT_H #define NS3_ABORT_H #include "fatal-error.h" +/** + * \ingroup debugging + * \brief Abnormal program termination + * + * \param msg message to output when this macro is hit. + * + * This macro is essentially equivalent to NS_FATAL_ERROR, + * excepts it prepends the error message with the string + * "aborted. ". When this macro is hit a runtime, the + * program will be halted using std::terminate, which + * triggers clean up code regestered by std::set_terminate. + * + * This macro is enable unconditionally in all builds, + * including debug and optimized builds. + * + * \see NS_FATAL_ERROR + */ #define NS_ABORT_MSG(msg) \ - do { \ - std::cerr << "file=" << __FILE__ << \ - ", line=" << __LINE__ << ", abort, msg=\"" << \ - msg << "\"" << std::endl; \ - int *a = 0; \ - *a = 0; \ + do { \ + std::cerr << "aborted. "; \ + NS_FATAL_ERROR (msg); \ } while (false) -#define NS_ABORT_IF(cond) \ - do { \ - if (cond) \ - { \ - std::cerr << "file=" << __FILE__ << \ - ", line=" << __LINE__ << ", abort on=\""#cond << \ - "\"" << std::endl; \ - int *a = 0; \ - *a = 0; \ - } \ +/** + * \ingroup debugging + * \brief Abnormal program termination if cond is true. + * + * \param cond condition to be evaluated. + * + * This is similar to NS_ASSERT(!(cond)), except this check + * is enabled in all builds. If cond is evaluated to true, + * the espression evaluating to true is printed to stderr, + * followed by a call to the NS_FATAL_ERROR_NO_MSG() macro + * which prints the details of filename and line number to + * stderr. The program will be halted by calling + * std::terminate(), triggering any clean up code registered + * by std::set_terminate (NS3 default is a stream-flushing + * code, but may be overridden). + * + * This macro is enable unconditionally in all builds, + * including debug and optimized builds. + */ +#define NS_ABORT_IF(cond) \ + do { \ + if (cond) \ + { \ + std::cerr << "aborted. cond=\"" << #cond << ", "; \ + NS_FATAL_ERROR_NO_MSG (); \ + } \ } while (false) -#define NS_ABORT_MSG_IF(cond, msg) \ - do { \ - if (cond) \ - { \ - std::cerr << "file=" << __FILE__ << \ - ", line=" << __LINE__ << ", abort on=\""#cond << \ - "\", msg=\"" << msg << "\"" << std::endl; \ - int *a = 0; \ - *a = 0; \ - } \ +/** + * \ingroup debugging + * \brief Abnormal program termination if cond is true. + * + * \param cond condition to be evaluated. + * \param msg message to output when cond is true. + * + * This is similar to NS_ASSERT_MSG(!(cond)), except this + * check is enabled in all builds. If cond is evaluated to + * true, the espression evaluating to true is printed to + * stderr, followed by a call to the NS_FATAL_ERROR() macro + * which prints the user-specified error message, and details + * of filename and line number to stderr. The program will + * be halted by calling std::terminate(), triggering any + * clean up code registered by std::set_terminate (NS3 + * default is a stream-flushing code, but may be overridden). + * + * This macro is enable unconditionally in all builds, + * including debug and optimized builds. + */ +#define NS_ABORT_MSG_IF(cond, msg) \ + do { \ + if (cond) \ + { \ + std::cerr << "aborted. cond=\"" << #cond << "\", "; \ + NS_FATAL_ERROR (msg); \ + } \ } while (false) -#define NS_ABORT_UNLESS(cond) \ +/** + * \ingroup debugging + * \brief Abnormal program termination if cond is false. + * + * \param cond condition to be evaluated. + * + * This is an alias for NS_ABORT_IF(!(cond)) + * + * \see NS_ABORT_IF + */ +#define NS_ABORT_UNLESS(cond) \ NS_ABORT_IF(!(cond)) -#define NS_ABORT_MSG_UNLESS(cond, msg) \ +/** + * \ingroup debugging + * \brief Abnormal program termination if cond is false. + * + * \param cond condition to be evaluated. + * \param msg message to output if cond is false. + * + * This is an alias for NS_ABORT_MSG_IF(!(cond)) + * + * \see NS_ABORT_MSG_IF + */ +#define NS_ABORT_MSG_UNLESS(cond, msg) \ NS_ABORT_MSG_IF(!(cond),msg) #endif /* NS3_ABORT_H */ diff -r 069840de4fac src/core/assert.h --- a/src/core/assert.h Fri Jun 04 07:12:20 2010 +0900 +++ b/src/core/assert.h Fri Jun 04 15:44:47 2010 +1000 @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2006 INRIA + * Copyright (c) 2006 INRIA, 2010 NICTA * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,14 +16,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Quincy Tse */ -#ifndef ASSERT_H -#define ASSERT_H +#ifndef NS_ASSERT_H +#define NS_ASSERT_H #ifdef NS3_ASSERT_ENABLE #include +#include "fatal-error.h" + /** * \ingroup core * \defgroup debugging Debugging @@ -39,6 +42,15 @@ * not true, the program halts. These checks are built * into the program only in debugging builds. They are * removed in optimized builds. + * + * These macro are intended to check certain conditions + * to be true. Do not put code that also have side effects + * that your program relies on (eg. code that advances + * an iterator and return false at end of file) because + * the code will not be executed on release builds!! + * + * If assertion-style checks are required for release + * builds, use NS_ABORT_UNLESS and NS_ABORT_MSG_UNLESS. */ /** @@ -47,18 +59,16 @@ * * At runtime, in debugging builds, if this condition is not * true, the program prints the source file, line number and - * unverified condition and halts by dereferencing a null pointer. + * unverified condition and halts by calling std::terminate */ #define NS_ASSERT(condition) \ do \ { \ if (!(condition)) \ { \ - std::cerr << "assert failed. file=" << __FILE__ << \ - ", line=" << __LINE__ << ", cond=\""#condition << \ - "\"" << std::endl; \ - int *a = 0; \ - *a = 0; \ + std::cerr << "assert failed. cond=\"" << \ + #condition << "\", "; \ + NS_FATAL_ERROR_NO_MSG(); \ } \ } \ while (false) @@ -71,18 +81,18 @@ * * At runtime, in debugging builds, if this condition is not * true, the program prints the message to output and - * halts by dereferencing a null pointer. + * halts by calling std::terminate. */ -#define NS_ASSERT_MSG(condition, message) \ - do \ - { \ - if (!(condition)) \ - { \ - std::cerr << message << std::endl; \ - int *a = 0; \ - *a = 0; \ - } \ - } \ +#define NS_ASSERT_MSG(condition, message) \ + do \ + { \ + if (!(condition)) \ + { \ + std::cerr << "assert failed. cond=\"" << \ + #condition << "\", "; \ + NS_FATAL_ERROR (message); \ + } \ + } \ while (false) #else /* NS3_ASSERT_ENABLE */ diff -r 069840de4fac src/core/fatal-error.h --- a/src/core/fatal-error.h Fri Jun 04 07:12:20 2010 +0900 +++ b/src/core/fatal-error.h Fri Jun 04 15:44:47 2010 +1000 @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2006 INRIA + * Copyright (c) 2006 INRIA, 2010 NICTA * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,11 +16,39 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: Mathieu Lacage + * Quincy Tse */ -#ifndef FATAL_ERROR_H -#define FATAL_ERROR_H +#ifndef NS3_FATAL_ERROR_H +#define NS3_FATAL_ERROR_H #include +#include +#include + +#include "fatal-impl.h" + +/** + * \ingroup debugging + * \brief fatal error handling + * + * When this macro is hit at runtime, details of filename + * and line number is printed to stderr, and the program + * is halted by calling std::terminate(). This will + * trigger any clean up code registered by + * std::set_terminate (NS3 default is a stream-flushing + * code), but may be overridden. + * + * This macro is enabled unconditionally in all builds, + * including debug and optimized builds. + */ +#define NS_FATAL_ERROR_NO_MSG() \ + do \ + { \ + std::cerr << "file=" << __FILE__ << ", line=" << \ + __LINE__ << std::endl; \ + std::terminate (); \ + } \ + while (false) /** * \ingroup debugging @@ -28,20 +56,129 @@ * * \param msg message to output when this macro is hit. * - * When this macro is hit at runtime, the user-specified - * error message is output and the program is halted by - * dereferencing a null pointer. This macro is enabled - * unconditionally in all builds, including debug and - * optimized builds. + * When this macro is hit at runtime, the user-specified + * error message is printed to stderr, followed by a call + * to the NS_FATAL_ERROR_NO_MSG() macro which prints the + * details of filename and line number to stderr. The + * program will be halted by calling std::terminate(), + * triggering any clean up code registered by + * std::set_terminate (NS3 default is a stream-flushing + * code, but may be overridden). + * + * This macro is enabled unconditionally in all builds, + * including debug and optimized builds. */ -#define NS_FATAL_ERROR(msg) \ +#define NS_FATAL_ERROR(msg) \ do \ { \ - std::cerr << msg << std::endl; \ - int *a = 0; \ - *a = 0; \ + std::cerr << "msg=\"" << msg << "\", "; \ + NS_FATAL_ERROR_NO_MSG(); \ } \ while (false) +/** + * \ingroup debugging + * + * \brief Sets the terminate handler to the normal C++ handler. + * + * This macro sets the default handler for std::terminate() back + * to the standard C++ handler. + * + * The standard C++ handler (post-gcc3.4) will identify any + * uncaught exceptions and print the output of what(), before + * raising SIGIOT and terminate without clean up. + */ +#define NS_FATAL_SET_CXX_VERBOSE() \ + do \ + { \ + std::set_terminate (__gnu_cxx::__verbose_terminate_handler); \ + } \ + while (false) + +/** + * \ingroup debugging + * + * \brief Sets the terminate handler to silent abort. + * + * This macro replaces the default handler for std::terminate() + * with the std::abort(). + * + * std::abort will raise SIGIOT and terminates without clean up + * or any output. + */ +#define NS_FATAL_SET_CXX_SILENT() \ + do \ + { \ + std::set_terminate (std::abort); \ + } \ + while (false) + +/** + * \ingroup debugging + * + * \brief Sets the terminate handler to the NS3 default. + * + * This macro replaces the default handler for std::terminate() + * with the ns3::FatalError::HandleTerminate(). + * + * ns3::FatalError::HandleTerminate will flush all registered + * ostreams and all FILE* before aborting. + */ +#define NS_FATAL_SET_DEFAULT() \ + do \ + { \ + std::set_terminate (ns3::FatalError::HandleTerminate); \ + } \ + while (false) + +/** + * \ingroup debugging + * \param handler The handler to ben triggered on std::terminate() + * + * \brief Replaces the default terminate handler. + * + * This macro replaces the default handler for std::terminate() + * with the specified handler. + * + * The handler should be a function that takes no arguments and + * returns void. The function should not return. If the function + * returns, std::terminate will raise SIGIOT and abort execution. + */ +#define NS_FATAL_SET_HANDLER(handler) \ + do \ + { \ + std::set_terminate (static_cast(handler)); \ + } \ + while (false) + +/** + * \ingroup debugging + * \param stream pointer to stream to be flushed. + * + * \brief Register a stream to be flushed on abnormal exit. + * + * This is an alias to ns3::FatalError::RegisterStream() + */ +#define NS_FATAL_REGISTER_STREAM(stream) \ + do \ + { \ + ns3::FatalError::RegisterStream (stream); \ + } \ + while (false) + +/** + * \ingroup debugging + * \param stream A pointer to the stream to be unregistered. + * + * \brief Unregister a stream for flushing on abnormal exit. + * + * This is an alias to ns3::FatalError::UnregisterStream() + */ +#define NS_FATAL_UNREGISTER_STREAM(stream) \ + do \ + { \ + ns3::FatalError::UnregisterStream (stream); \ + } \ + while (false) #endif /* FATAL_ERROR_H */ diff -r 069840de4fac src/core/fatal-impl.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/fatal-impl.cc Fri Jun 04 15:44:47 2010 +1000 @@ -0,0 +1,116 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 NICTA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Quincy Tse + */ +#include "fatal-impl.h" + +#include +#include + +#include +#include + +#include + +using std::list; +using std::ostream; + +namespace ns3 { +namespace FatalError { + +/* File-scope */ +namespace { + std::list& GetStreamList (void) + { + static std::list streams; + return streams; + } +} + +void +RegisterStream (ostream* stream) +{ + GetStreamList ().push_back (stream); +} + +void +UnregisterStream (ostream* stream) +{ + GetStreamList ().remove (stream); +} + +void +HandleTerminate (void) +{ + FlushStreams (); + std::abort (); +} + + +namespace { + /* Overrides normal SIGSEGV handler once the + * HandleTerminate function is run. */ + void sigHandler(int sig) {HandleTerminate ();} +} + +inline void +FlushStreams (void) +{ + struct sigaction hdl; + + /* Override default SIGSEGV handler - will flush subsequent + * streams even if one of the stream pointers is bad. + * The SIGSEGV override should only be active for the + * duration of this function. */ + hdl.sa_handler=sigHandler; + sigaction (SIGSEGV, &hdl, 0); + + std::list& l = GetStreamList (); + + /* Need to do it this way in case any of the ostream* causes SIGSEGV */ + while (!l.empty ()) + { + ostream* s (l.front ()); + l.pop_front (); + s->flush (); + } + + /* Restore default SIGSEGV handler (Not that it matters anyway) */ + hdl.sa_handler=SIG_DFL; + sigaction (SIGSEGV, &hdl, 0); + + /* Flush all opened FILE* */ + std::fflush (0); + + /* Flush stdandard streams - shouldn't be required (except for clog) */ + std::cout.flush (); + std::cerr.flush (); + std::clog.flush (); +} + +namespace { + struct _StartUp { + // Run following code on startup + _StartUp (void) { + std::set_terminate (HandleTerminate); + } + } _start; +} + +} //FatalError +} //ns3 diff -r 069840de4fac src/core/fatal-impl.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/core/fatal-impl.h Fri Jun 04 15:44:47 2010 +1000 @@ -0,0 +1,137 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 NICTA + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Quincy Tse + */ + +#ifndef FATAL_IMPL_H +#define FATAL_IMPL_H + +#include + +/** + * \ingroup debugging + * \defgroup fatalHandler Fatal Error Handler + * + * \brief Functions to help clean up when fatal error + * is encountered. + * + * The functions in this group are used to perform + * limited clean up, like flushing active streams, when + * fatal error are encountered (through assertion fail, + * calls to NS_ABORT_* and calls to NS_FATAL_ERROR. + * + * Currently, other than flushing active ostreams, these + * functions does not interfere with outside memory. There + * is still a residual risk that may be invalid ostream + * pointers may be present, and may corrupt the memory + * on the attempt to execute the flush() function. + * + * The function to handle abnormal termination should not + * be called directly. It is automatically triggered when + * a std::terminate() is called. Do not call FlushStreams() + * or HandleTerminate - they will cause the program to + * perform abnormal termination. + * + * Terminating by dereferencing null pointers and by calls + * to std::abort will not trigger the clean up code. + * + * NS3 developers may use NS_FATAL_SET_HANDLER to change + * the behaviour on abnormal termination caused by calls + * to std::terminate(). Developers should call the + * FlushStreams() function at the end of their handler + * when they are implementing their own handler, otherwise + * the ostreams will be not flushed, or their handling + * code may not be executed (FlushStreams() may call + * std::abort() if there are bad ostream* registered. + * + */ + +namespace ns3 { +namespace FatalError { + +/** + * \ingroup fatalHandler + * \param stream The stream to be flushed on abnormal exit. + * + * \brief Register a stream to be flushed on abnormal exit. + * + * If a std::terminate() call is encountered after the + * stream had been registered and before it had been + * unregistered, stream->flush() will be called. Users of + * this function is to ensure stream remains valid until + * it had been unregistered. + */ +void RegisterStream (std::ostream* stream); + +/** + * \ingroup fatalHandler + * \param stream The stream to be unregistered. + * + * \brief Unregister a stream for flushing on abnormal exit. + * + * After a stream had been unregistered, stream->flush() + * will no longer be called should abnormal termination is + * encountered. + * + * If stream is not registered, nothing will happen. + */ +void UnregisterStream (std::ostream* stream); + +/** + * \ingroup fatalHandler + * + * \brief Flush all currently registered streams. + * + * This function iterates through each registered stream and + * unregister them. The default SIGSEGV handler is overridden + * when this function is being executed, and will be restored + * when this function returns. + * + * If a SIGSEGV is encountered (most likely due to bad ostream* + * being registered, or a registered osteam* pointing to an + * ostream that had already been destroyed), this function will + * skip the bad ostream* and continue to flush the next stram. + * The function will then terminate raising SIGIOT (aka SIGABRT) + * + * DO NOT call this function until the program is ready to crash. + */ +void FlushStreams (void); + +/** + * \ingroup fatalHandler + * + * \brief Handles clean up when std::terminate() is called. + * + * Once this handler had been registered using + * std::set_terminate() (via the macro NS_FATAL_SET_DEFAULT()), + * The system will trigger this function when std::terminate() is + * called. std::terminate() expects this function not to return. + * If this function returns, std::terminate will raise a SIGIOT + * and terminate the program. + * + * This function calls FlushStreams() and then aborts execution + * using std::abort(). + * + * This function does not return. + */ +void HandleTerminate (void); + +} //FatalError +} //ns3 + +#endif diff -r 069840de4fac src/core/wscript --- a/src/core/wscript Fri Jun 04 07:12:20 2010 +0900 +++ b/src/core/wscript Fri Jun 04 15:44:47 2010 +1000 @@ -80,6 +80,7 @@ 'type-traits-test-suite.cc', 'traced-callback-test-suite.cc', 'ptr-test-suite.cc', + 'fatal-impl.cc', ] headers = bld.new_task_gen('ns3header') @@ -128,6 +129,7 @@ 'names.h', 'vector.h', 'default-deleter.h', + 'fatal-impl.h', ] if sys.platform == 'win32':