/*****************************************************************************/ /*! * \file debug.h * \brief Description: Collection of debugging macros and functions. * * Author: Sergey Berezin * * Created: Thu Dec 5 13:12:59 2002 * *
* * License to use, copy, modify, sell and/or distribute this software * and its documentation for any purpose is hereby granted without * royalty, subject to the terms and conditions defined in the \ref * LICENSE file provided with this distribution. * *
* */ /*****************************************************************************/ #ifndef _cvc3__debug_h #define _cvc3__debug_h #include #include #include #include #include "os.h" #include "exception.h" /*! @brief If something goes horribly wrong, print a message and abort immediately with exit(1). */ /*! This macro stays even in the non-debug build, so the end users can send us meaningful bug reports. */ #define FatalAssert(cond, msg) if(!(cond)) \ CVC3::fatalError(__FILE__, __LINE__, #cond, msg) namespace CVC3 { //! Function for fatal exit. /*! It just exits with code 1, but is provided here for the debugger to set a breakpoint to. For this reason, it is not inlined. */ extern void fatalError(const std::string& file, int line, const std::string& cond, const std::string& msg); } #ifdef DEBUG #include "compat_hash_map.h" #include "compat_hash_set.h" //! Any debugging code must be within IF_DEBUG(...) #define IF_DEBUG(code) code //! Print a value conditionally. /*! If 'cond' is true, print 'pre', then 'v', then 'post'. The type of v must have overloaded operator<<. It expects a ';' after it. */ #define DBG_PRINT(cond, pre, v, post) if(cond) CVC3::debugger.getOS() \ << (pre) << (v) << (post) << std::endl //! Print a message conditionally #define DBG_PRINT_MSG(cond, msg) \ if(cond) CVC3::debugger.getOS() << (msg) << std::endl /*! @brief Same as DBG_PRINT, only takes a flag name instead of a general condition */ #define TRACE(flag, pre, v, post) \ DBG_PRINT(CVC3::debugger.trace(flag), pre, v, post) //! Same as TRACE, but for a simple message #define TRACE_MSG(flag, msg) \ DBG_PRINT_MSG(CVC3::debugger.trace(flag), msg) //! Sanity check for debug build. It disappears in the production code. #define DebugAssert(cond, str) if(!(cond)) \ CVC3::debugError(__FILE__, __LINE__, #cond, str) namespace CVC3 { /*! @brief A class which sets a boolean value to true when created, * and resets to false when deleted. * * Useful for tracking when the control is within a certain method or * not. For example, TheoryCore::addFact() uses d_inAddFact to check * that certain other methods are only called from within addFact(). * However, when an exception is thrown, this variable is not reset. * The watcher class will reset the variable even in those cases. */ class ScopeWatcher { private: bool *d_flag; public: ScopeWatcher(bool *flag): d_flag(flag) { *d_flag = true; } ~ScopeWatcher() { *d_flag = false; } }; class Expr; //! Our exception to throw class DebugException: public Exception { public: // Constructor DebugException(const std::string& msg): Exception(msg) { } // Printing virtual std::string toString() const { return "Assertion violation " + d_msg; } }; // end of class DebugException //! Similar to fatalError to raise an exception when DebugAssert fires. /*! This does not necessarily cause the program to quit. */ extern void debugError(const std::string& file, int line, const std::string& cond, const std::string& msg); // First, wrapper classes for flags, counters, and timers. Later, // we overload some operators like '=', '++', etc. for those // classes. //! Boolean flag (can only be true or false) class DebugFlag { private: bool* d_flag; // We don't own the pointer public: // Constructor: takes the pointer to the actual flag, normally // stored in class Debug below. DebugFlag(bool& flag) : d_flag(&flag) { } // Destructor ~DebugFlag() { } // Auto-cast to boolean operator bool() { return *d_flag; } // Setting and resetting by ++ and -- // Prefix versions: bool operator--() { *d_flag = false; return false; } bool operator++() { *d_flag = true; return true; } // Postfix versions: bool operator--(int) { bool x=*d_flag; *d_flag=false; return x; } bool operator++(int) { bool x=*d_flag; *d_flag=true; return x; } // Can be assigned only a boolean value DebugFlag& operator=(bool x) { *d_flag=(x!=false); return *this; } // Comparisons friend bool operator==(const DebugFlag& f1, const DebugFlag& f2); friend bool operator!=(const DebugFlag& f1, const DebugFlag& f2); // Printing friend std::ostream& operator<<(std::ostream& os, const DebugFlag& f); }; // end of class DebugFlag //! Checks if the *values* of the flags are equal inline bool operator==(const DebugFlag& f1, const DebugFlag& f2) { return (*f1.d_flag) == (*f2.d_flag); } //! Checks if the *values* of the flags are disequal inline bool operator!=(const DebugFlag& f1, const DebugFlag& f2) { return (*f1.d_flag) != (*f2.d_flag); } //! Printing flags inline std::ostream& operator<<(std::ostream& os, const DebugFlag& f) { if(*f.d_flag) return(os << "true"); else return(os << "false"); } //! Integer counter for debugging purposes. /*! Intended use is to count events (e.g. number of function calls), but can be used to store any integer value (e.g. size of some data structure) */ class DebugCounter { private: int* d_counter; //!< We don't own the pointer public: //! Constructor /*! Takes the pointer to the actual counter, normally stored in class Debug below. */ DebugCounter(int& c) : d_counter(&c) { } //! Destructor ~DebugCounter() { } //! Auto-cast to int. /*! In particular, arithmetic comparisons like <, >, <=, >= will work because of this. */ operator int() { return *d_counter; } // Auto-increment operators //! Prefix auto-decrement int operator--() { return --(*d_counter); } //! Prefix auto-increment int operator++() { return ++(*d_counter); } //! Postfix auto-decrement int operator--(int) { return (*d_counter)--; } //! Postfix auto-increment int operator++(int) { return (*d_counter)++; } //! Value assignment. DebugCounter& operator=(int x) { *d_counter=x; return *this; } DebugCounter& operator+=(int x) { *d_counter+=x; return *this; } DebugCounter& operator-=(int x) { *d_counter-=x; return *this; } //! Assignment from another counter. /*! It copies the value, not the pointer */ DebugCounter& operator=(const DebugCounter& x) { *d_counter=*x.d_counter; return *this; } /*! It copies the value, not the pointer */ DebugCounter& operator-=(const DebugCounter& x) { *d_counter-=*x.d_counter; return *this; } /*! It copies the value, not the pointer */ DebugCounter& operator+=(const DebugCounter& x) { *d_counter+=*x.d_counter; return *this; } // Comparisons to integers and other DebugCounters friend bool operator==(const DebugCounter& c1, const DebugCounter& c2); friend bool operator!=(const DebugCounter& c1, const DebugCounter& c2); friend bool operator==(int c1, const DebugCounter& c2); friend bool operator!=(int c1, const DebugCounter& c2); friend bool operator==(const DebugCounter& c1, int c2); friend bool operator!=(const DebugCounter& c1, int c2); //! Printing counters friend std::ostream& operator<<(std::ostream& os, const DebugCounter& f); }; // end of class DebugCounter inline bool operator==(const DebugCounter& c1, const DebugCounter& c2) { return (*c1.d_counter) == (*c2.d_counter); } inline bool operator!=(const DebugCounter& c1, const DebugCounter& c2) { return (*c1.d_counter) != (*c2.d_counter); } inline bool operator==(int c1, const DebugCounter& c2) { return c1 == (*c2.d_counter); } inline bool operator!=(int c1, const DebugCounter& c2) { return c1 != (*c2.d_counter); } inline bool operator==(const DebugCounter& c1, int c2) { return (*c1.d_counter) == c2; } inline bool operator!=(const DebugCounter& c1, int c2) { return (*c1.d_counter) != c2; } inline std::ostream& operator<<(std::ostream& os, const DebugCounter& c) { return (os << *c.d_counter); } //! A class holding the time value. /*! What exactly is time is not exposed. It can be the system's struct timeval, or it can be the (subset of the) user/system/real time tuple. */ class DebugTime; //! Time counter. /*! Intended use is to store time intervals or accumulated time for multiple events (e.g. time spent to execute certain lines of code, or accumulated time spent in a particular function). */ class DebugTimer { private: DebugTime* d_time; //!< The time value bool d_clean_time; //!< Set if we own *d_time public: //! Constructor: takes the pointer to the actual time value. /*! It is either stored in class Debug below (then the timer is "public"), or we own it, making the timer "private". */ DebugTimer(DebugTime* time, bool take_time = false) : d_time(time), d_clean_time(take_time) { } /*! @brief Copy constructor: copy the *pointer* from public timers, and *value* from private. */ /*! The reason for different behavior for public and private time is the following. When you modify a public timer, you want the changes to show in the central database and everywhere else, whereas private timers are used as independent temporary variables holding intermediate time values. */ DebugTimer(const DebugTimer& timer); //! Assignment: same logistics as for the copy constructor DebugTimer& operator=(const DebugTimer& timer); //! Destructor ~DebugTimer(); // Operators //! Set time to zero void reset(); DebugTimer& operator+=(const DebugTimer& timer); DebugTimer& operator-=(const DebugTimer& timer); //! Produces new "private" timer DebugTimer operator+(const DebugTimer& timer); //! Produces new "private" timer DebugTimer operator-(const DebugTimer& timer); // Our friends friend class Debug; // Comparisons friend bool operator==(const DebugTimer& t1, const DebugTimer& t2); friend bool operator!=(const DebugTimer& t1, const DebugTimer& t2); friend bool operator<(const DebugTimer& t1, const DebugTimer& t2); friend bool operator>(const DebugTimer& t1, const DebugTimer& t2); friend bool operator<=(const DebugTimer& t1, const DebugTimer& t2); friend bool operator>=(const DebugTimer& t1, const DebugTimer& t2); //! Print the timer's value friend std::ostream& operator<<(std::ostream& os, const DebugTimer& timer); }; // end of class DebugTimer //! The heart of the Bug Extermination Kingdom. /*! This class exposes many important components of the entire CVC-lite system for use in debugging, keeps all the flags, counters, and timers in the central database, and provides timing and printing functions. */ class Debug { private: //! Command line options for tracing; these override the TRACE command const std::vector >* d_traceOptions; //! name of dump file const std::string* d_dumpName; // Output control std::ostream* d_os; // Stream for dumping trace to file ("dump-trace" option) std::ostream* d_osDumpTrace; //! Private hasher class for strings class stringHash { public: size_t operator()(const std::string& s) const { std::hash h; return h(s.c_str()); } }; // end of stringHash // Hash tables for storing flags, counters, and timers typedef std::hash_map FlagMap; typedef std::hash_map CounterMap; typedef std::hash_map TimerMap; FlagMap d_flags; //!< Set of flags FlagMap d_traceFlags; //!< Set of trace flags CounterMap d_counters; //!< Set of counters /*! Note, that the d_timers map does *not* own the pointers; so the objects in d_timers must be destroyed explicitly in our destructor. */ TimerMap d_timers; //!< Set of timers public: //! Constructor Debug(): d_traceOptions(NULL), d_os(&std::cerr), d_osDumpTrace(NULL) { } //! Destructor (must destroy objects it d_timers) ~Debug(); //! Must be called before Debug class can be safely used void init(const std::vector >* traceOptions, const std::string* dumpName); //! Accessing flags by name. /*! If a flag doesn't exist, it is created and initialized to false. */ DebugFlag flag(const std::string& name) { return DebugFlag(d_flags[name]); } //! Accessing tracing flags by name. /*! If a flag doesn't exist, it is created and initialized to false. */ DebugFlag traceFlag(const std::string& name) { return DebugFlag(d_traceFlags[name]); } //! Accessing tracing flag by char* name (mostly for GDB) DebugFlag traceFlag(char* name); //! Set tracing of everything on (1) and off (0) [for use in GDB] void traceAll(bool enable = true); //! Accessing counters by name. /*! If a counter doesn't exist, it is created and initialized to 0. */ DebugCounter counter(const std::string& name) { return DebugCounter(d_counters[name]); } //! Accessing timers by name. /*! If a timer doesn't exist, it is created and initialized to 0. */ DebugTimer timer(const std::string& name); //! Check whether to print trace info for a particular flag. /*! Trace flags are the same DebugFlag objects, but live in a different namespace from the normal debug flags */ bool trace(const std::string& name); // Timer functions //! Create a new "private" timer, initially set to 0. /*! The new timer will not be added to the set of timers, will not have a name, and will not be printed by 'printAll()'. It is intended to be used to measure time intervals which are later added or assigned to the named timers. */ static DebugTimer newTimer(); //! Set the timer to the current time (whatever that means) void setCurrentTime(DebugTimer& timer); void setCurrentTime(const std::string& name) { DebugTimer t(timer(name)); setCurrentTime(t); } /*! @brief Set the timer to the difference between current time and the time stored in the timer: timer = currentTime - timer. */ /*! Intended to obtain the time interval since the last call to setCurrentTime() with that timer. */ void setElapsed(DebugTimer& timer); //! Return the ostream used for debugging output std::ostream& getOS() { return *d_os; } //! Return the ostream for dumping trace std::ostream& getOSDumpTrace(); //! Print an entry to the dump file void dumpTrace(const std::string& title, const std::vector >& fields); //! Set the debugging ostream void setOS(std::ostream& os) { d_os = &os; } //! Print all the collected data if "DEBUG" flag is set to 'os' void printAll(std::ostream& os); /*! @brief Print all the collected data if "DEBUG" flag is set to the default debug stream */ void printAll() { printAll(*d_os); } // Generally useful functions //! Get the current scope level int scopeLevel(); }; // end of class Debug extern Debug debugger; } // end of namespace CVC3 #else // if DEBUG is not defined // All debugging macros are empty here #define IF_DEBUG(code) #define DebugAssert(cond, str) #define DBG_PRINT(cond, pre, v, post) #define TRACE(cond, pre, v, post) #define DBG_PRINT_MSG(cond, msg) #define TRACE_MSG(flag, msg) // to make the CLI wrapper happy namespace CVC3 { class DebugException: public Exception { }; } #endif // DEBUG #include "cvc_util.h" #endif // _cvc3__debug_h