libdbg 1.2
|
00001 /* 00002 * File: dbg.cpp 00003 * Author: Pete Goodliffe 00004 * Version: 1.10 00005 * Created: 7 June 2001 00006 * 00007 * Purpose: C++ debugging support library 00008 * 00009 * Copyright (c) Pete Goodliffe 2001-2002 (pete@cthree.org) 00010 * 00011 * This file is modifiable/redistributable under the terms of the GNU 00012 * Lesser General Public License. 00013 * 00014 * You should have recieved a copy of the GNU General Public License along 00015 * with this program; see the file COPYING. If not, write to the Free Software 00016 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 0211-1307, USA. 00017 */ 00018 00019 #ifndef DBG_ENABLED 00020 #define DBG_ENABLED 00021 #endif 00022 00023 #include "dbg.h" 00024 00025 #include <iostream> 00026 #include <cstdlib> 00027 #include <cstring> 00028 #include <cstdio> 00029 #include <string> 00030 #include <vector> 00031 #include <map> 00032 #include <algorithm> 00033 #include <new> 00034 00035 /********************************************************************** 00036 * Implementation notes 00037 ********************************************************************** 00038 * Tested and found to work ok under 00039 * - gcc 2.96 00040 * - gcc 3.0 00041 * - gcc 3.1 00042 * - gcc 3.2 00043 * - bcc32 5.5.1 00044 * - MSVC 6.0 00045 * 00046 * MSVC v6.0 00047 * - This platform makes me cry. 00048 * - There are NUMEROUS hacks around it's deficient behaviour, just 00049 * look for conditional complation based around _MSC_VER 00050 * - The <ctime> header doesn't put all the definitions into the std 00051 * namespace. 00052 * - This means that we have to sacrifice our good namespace-based code 00053 * for something more disgusting and primitve. 00054 * - Where this has happened, and where in the future I'd really like to 00055 * put the "std" namespace back in, I have instead used a STDCLK macro. 00056 * See the implementation comment about this below for more grief. 00057 * - A documented hack has been made in the dbg.h header file, of slightly 00058 * less ghastly proportions. See dbgclock_t there. 00059 * - Additionally, the dbg::array_size template utility could be (and was) 00060 * more elegantly be written: 00061 * template <class T, int size> 00062 * inline unsigned int array_size(T (&array)[size]) 00063 * { 00064 * return size; 00065 * } 00066 * Of course, MSVC doesn't like that. Sigh. The version in dbg.h also 00067 * works, its just not quite so nice. 00068 * - The map implentation of MSVC doesn't provide data_type, so I have to 00069 * hack around that. 00070 * - The compiler doesn't like the dbg_ostream calling it's parent 00071 * constructor by the name "ostream", it doesn't recognise the typedef. 00072 * Ugh. 00073 * 00074 * Other thoughts: 00075 * - Break out to debugger facility? 00076 * - Only works for ostreams, not all basic_ostreams 00077 * - Post-conditions are a bit limited, this is more of a C++ 00078 * language limitation, really. 00079 *********************************************************************/ 00080 00081 /****************************************************************************** 00082 * Tedious compiler-specific issues 00083 *****************************************************************************/ 00084 00085 // Work around MSVC 6.0 00086 #ifdef _MSC_VER 00087 #define STDCLK 00088 #pragma warning(disable:4786) 00089 #else 00090 // In an ideal world, the following line would be 00091 // namespace STDCLK = std; 00092 // However, gcc 2.96 doesn't seem to cope well with namespace aliases. 00093 // Sigh. 00094 #define STDCLK std 00095 #endif 00096 00097 // Quieten tedius build warnings on Borland C++ compiler 00098 #ifdef __BCPLUSPLUS__ 00099 #pragma warn -8066 00100 #pragma warn -8071 00101 #pragma warn -8070 00102 #endif 00103 00104 /****************************************************************************** 00105 * General dbg library private declarations 00106 *****************************************************************************/ 00107 00108 namespace 00109 { 00110 /************************************************************************** 00111 * Constants 00112 *************************************************************************/ 00113 00114 const char *LEVEL_NAMES[] = 00115 { 00116 "info", 00117 "warning", 00118 "error", 00119 "fatal", 00120 "tracing", 00121 "debug", 00122 "none", 00123 "all" 00124 }; 00125 const char *BEHAVIOUR_NAMES[] = 00126 { 00127 "assertions_abort", 00128 "assertions_throw", 00129 "assertions_continue" 00130 }; 00131 enum constraint_type 00132 { 00133 why_assertion, 00134 why_sentinel, 00135 why_unimplemented, 00136 why_check_ptr 00137 }; 00138 00139 const char *TRACE_IN = "->"; 00140 const char *TRACE_OUT = "<-"; 00141 const char *INDENT = " "; 00142 const char *PREFIX = "*** "; 00143 const char *TRUE_STRING = "true"; 00144 const char *FALSE_STRING = "false"; 00145 const unsigned int ALL_SOURCES_MASK = 0xff; 00146 const unsigned int NUM_DBG_LEVELS = dbg::all-1; 00147 00148 /************************************************************************** 00149 * Internal types 00150 *************************************************************************/ 00151 00158 struct period_data 00159 { 00160 size_t no_triggers; 00161 STDCLK::clock_t triggered_at; 00162 00163 period_data(); 00164 }; 00165 00171 struct lt_sp 00172 { 00173 bool operator()(const dbg::source_pos &a, const dbg::source_pos &b) 00174 const 00175 { 00176 if (a.file == b.file) 00177 { 00178 if (a.func == b.func) 00179 { 00180 return a.line < b.line; 00181 } 00182 else 00183 { 00184 return a.func < b.func; 00185 } 00186 } 00187 else 00188 { 00189 return a.file < b.file; 00190 } 00191 } 00192 }; 00193 00205 class dbg_streambuf : public std::streambuf 00206 { 00207 public: 00208 00209 dbg_streambuf(std::vector<std::ostream*> &ostreams, int bsize = 0); 00210 ~dbg_streambuf(); 00211 00212 int pubsync() { return sync(); } 00213 00214 protected: 00215 00216 int overflow(int); 00217 int sync(); 00218 00219 private: 00220 00221 void put_buffer(void); 00222 void put_char(int); 00223 00224 std::vector<std::ostream *> &ostreams; 00225 }; 00226 00237 class null_streambuf : public std::streambuf 00238 { 00239 public: 00240 00241 null_streambuf() {} 00242 ~null_streambuf() {} 00243 00244 protected: 00245 00246 int overflow(int) { return 0; } 00247 int sync() { return 0; } 00248 }; 00249 00259 class dbg_ostream : public std::ostream 00260 { 00261 public: 00262 00263 #ifndef _MSC_VER 00264 dbg_ostream() : std::ostream(&dbg_buf), dbg_buf(streams) {} 00265 dbg_ostream(const dbg_ostream &rhs) 00266 : std::ostream(&dbg_buf), streams(rhs.streams), 00267 dbg_buf(streams) {} 00268 #else 00269 // MSVC workaround. Sigh. It won't let us call the parent ctor as 00270 // "ostream" - it doesn't like the use of a typedef. On the other 00271 // hand gcc 2.96 doesn't provide basic_ostream, so I can't call the 00272 // base basic_ostream<> class there. 00273 dbg_ostream() 00274 : std::basic_ostream<char>(&dbg_buf), dbg_buf(streams) {} 00275 dbg_ostream(const dbg_ostream &rhs) 00276 : std::basic_ostream<char>(&dbg_buf), streams(rhs.streams), 00277 dbg_buf(streams) {} 00278 #endif 00279 ~dbg_ostream() { dbg_buf.pubsync(); } 00280 00281 void add(std::ostream &o); 00282 void remove(std::ostream &o); 00283 void clear(); 00284 00285 private: 00286 00287 dbg_ostream &operator=(const dbg_ostream&); 00288 00289 typedef std::vector<std::ostream*> stream_vec_type; 00290 00291 stream_vec_type streams; 00292 dbg_streambuf dbg_buf; 00293 }; 00294 00307 class source_info 00308 { 00309 public: 00310 00316 enum ConstructionStyle 00317 { 00318 ConstructTheDefaultSource = 0, 00319 ConstructCopyOfDefaultSource = 1 00320 }; 00321 00322 source_info(ConstructionStyle cs = ConstructCopyOfDefaultSource); 00323 source_info(const source_info &rhs); 00324 ~source_info(); 00325 00330 void enable(dbg::level lvl, bool enable); 00331 00335 bool enabled(dbg::level lvl) const 00336 { 00337 return (levels & dbg_source_mask(lvl)) != 0; 00338 } 00339 00343 void add_ostream(dbg::level lvl, std::ostream &o); 00344 00348 void remove_ostream(dbg::level lvl, std::ostream &o); 00349 00353 void clear_ostream(dbg::level lvl); 00354 00359 std::ostream &out(dbg::level lvl); 00360 00361 private: 00362 00367 static unsigned int dbg_source_mask(dbg::level lvl) 00368 { 00369 return (lvl != dbg::all) ? 1 << lvl : ALL_SOURCES_MASK; 00370 } 00371 00372 unsigned int levels; 00373 00374 // We do a placement new of the dbg_streams array. 00375 // It looks somewhat tacky, but it allows us to have a single 00376 // constructor, which simplifies the client interface of this class. 00377 // It specifically avoids tonnes of grotesque unused dbg_ostream 00378 // constructions, as you'd create an array, and then copy the 00379 // default_source elements directly over these freshly constructed 00380 // elements. dbg_ostream is complex enough that this matters. 00381 // If we didn't have a clever cloning constructor, these lines 00382 // would just be "dbg_ostream dbg_streams[NUM_DBG_LEVELS];" and 00383 // we'd suffer a whole load of redundant dbg_ostream constructions. 00384 /* 00385 typedef dbg_ostream array_type[NUM_DBG_LEVELS]; 00386 dbg_ostream *dbg_streams; 00387 unsigned char raw_dbg_streams[sizeof(array_type)]; 00388 */ 00389 struct array_type 00390 { 00391 // I wrap this up in an enclosing struct to make it obvious 00392 // how to destroy the array "in place". To be honest I couldn't 00393 // figure the syntax for the corresponding delete for a 00394 // placement new array constrution. 00395 dbg_ostream dbg_streams[NUM_DBG_LEVELS]; 00396 }; 00397 dbg_ostream *dbg_streams; 00398 unsigned char raw_dbg_streams[sizeof(array_type)]; 00399 00400 array_type &raw_cast() 00401 { 00402 return *reinterpret_cast<array_type*>(raw_dbg_streams); 00403 } 00404 const array_type &raw_cast() const 00405 { 00406 return *reinterpret_cast<const array_type*>(raw_dbg_streams); 00407 } 00408 }; 00409 00420 class source_map_type 00421 { 00422 public: 00423 00424 typedef std::map<std::string, source_info> map_type; 00425 typedef map_type::iterator iterator; 00426 typedef map_type::key_type key_type; 00427 #ifndef _MSC_VER 00428 // Replaced data_type by mapped_type, since there is no 00429 // more data_type, data_type was remove in g++ 3.3. 00430 typedef map_type::mapped_type data_type; 00431 #else 00432 // MSVC. Just don't ask. 00433 typedef source_info data_type; 00434 #endif 00435 source_map_type() 00436 { 00437 // Insert the default_source into the map 00438 _map.insert( 00439 std::make_pair(dbg::default_source, 00440 source_info(source_info::ConstructTheDefaultSource))); 00441 // Insert the unnamed source into the map too 00442 _map.insert( 00443 std::make_pair(dbg::dbg_source(""), 00444 source_info(source_info::ConstructTheDefaultSource))); 00445 } 00446 iterator begin() { return _map.begin(); } 00447 iterator end() { return _map.end(); } 00448 data_type &operator[](key_type key) { return _map[key]; } 00449 00450 private: 00451 00452 map_type _map; 00453 }; 00454 00455 typedef std::map<dbg::source_pos, period_data, lt_sp> period_map_type; 00456 00457 /************************************************************************** 00458 * Internal variables 00459 *************************************************************************/ 00460 00461 // The stream to write to when no output is required. 00462 std::ostream null_ostream(new null_streambuf()); 00463 00464 dbg::assertion_behaviour behaviour[dbg::all+1] = 00465 { 00466 dbg::assertions_abort, 00467 dbg::assertions_abort, 00468 dbg::assertions_abort, 00469 dbg::assertions_abort, 00470 dbg::assertions_abort, 00471 dbg::assertions_abort, 00472 dbg::assertions_abort, 00473 dbg::assertions_abort 00474 }; 00475 00476 unsigned int indent_depth = 0; 00477 std::string indent_prefix = PREFIX; 00478 bool level_prefix = false; 00479 bool time_prefix = false; 00480 STDCLK::clock_t period = 0; 00481 source_map_type source_map; 00482 period_map_type period_map; 00483 00484 /************************************************************************** 00485 * Function declarations 00486 *************************************************************************/ 00487 00491 void print_pos(std::ostream &out, const dbg::source_pos &where); 00492 00497 void print_pos_short(std::ostream &out, const dbg::source_pos &where); 00498 00503 void print_period_info(std::ostream &out, const dbg::source_pos &where); 00504 00509 void do_assertion_behaviour(dbg::level lvl, constraint_type why, 00510 const dbg::source_pos &pos); 00511 00516 void do_prefix(dbg::level lvl, std::ostream &s); 00517 00521 bool period_allows_impl(const dbg::source_pos &where); 00522 00531 inline bool period_allows(const dbg::source_pos &where) 00532 { 00533 return !period || period_allows_impl(where); 00534 } 00535 00541 void determine_source(dbg::dbg_source &src, const dbg::source_pos &here); 00542 } 00543 00544 00545 /****************************************************************************** 00546 * Miscellaneous public bobbins 00547 *****************************************************************************/ 00548 00549 dbg::dbg_source dbg::default_source = "dbg::private::default_source"; 00550 00551 00552 /****************************************************************************** 00553 * Enable/disable dbg facilities 00554 *****************************************************************************/ 00555 00556 void dbg::enable(dbg::level lvl, bool enabled) 00557 { 00558 out(debug) << prefix(debug) << "dbg::enable(" << LEVEL_NAMES[lvl] 00559 << "," << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n"; 00560 00561 source_map[""].enable(lvl, enabled); 00562 } 00563 00564 00565 void dbg::enable(dbg::level lvl, dbg::dbg_source src, bool enabled) 00566 { 00567 out(debug) << prefix(debug) << "dbg::enable(" << LEVEL_NAMES[lvl] 00568 << ",\"" << src << "\"," 00569 << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n"; 00570 00571 source_map[src].enable(lvl, enabled); 00572 } 00573 00574 00575 void dbg::enable_all(dbg::level lvl, bool enabled) 00576 { 00577 out(debug) << prefix(debug) << "dbg::enable_all(" 00578 << LEVEL_NAMES[lvl] << "," 00579 << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n"; 00580 00581 source_map_type::iterator i = source_map.begin(); 00582 for ( ; i != source_map.end(); ++i) 00583 { 00584 (i->second).enable(lvl, enabled); 00585 } 00586 } 00587 00588 00589 /****************************************************************************** 00590 * Logging 00591 *****************************************************************************/ 00592 00593 std::ostream &dbg::out(dbg::level lvl, dbg::dbg_source src) 00594 { 00595 return source_map[src ? src : ""].out(lvl); 00596 } 00597 00598 00599 void dbg::attach_ostream(dbg::level lvl, std::ostream &o) 00600 { 00601 out(debug) << prefix(debug) << "dbg::attach_ostream(" 00602 << LEVEL_NAMES[lvl] << ",ostream)\n"; 00603 00604 source_map[""].add_ostream(lvl, o); 00605 } 00606 00607 00608 void dbg::attach_ostream(dbg::level lvl, dbg::dbg_source src, std::ostream &o) 00609 { 00610 out(debug) << prefix(debug) << "dbg::attach_ostream(" 00611 << LEVEL_NAMES[lvl] 00612 << ", \"" << src 00613 << "\" ,ostream)\n"; 00614 00615 source_map[src].add_ostream(lvl, o); 00616 } 00617 00618 00619 void dbg::detach_ostream(dbg::level lvl, std::ostream &o) 00620 { 00621 out(debug) << prefix(debug) << "dbg::detach_ostream(" 00622 << LEVEL_NAMES[lvl] << ")\n"; 00623 00624 source_map[""].remove_ostream(lvl, o); 00625 } 00626 00627 00628 void dbg::detach_ostream(dbg::level lvl, dbg::dbg_source src, std::ostream &o) 00629 { 00630 out(debug) << prefix(debug) << "dbg::detach_ostream(" 00631 << LEVEL_NAMES[lvl] 00632 << ", \"" << src 00633 << "\" ,ostream)\n"; 00634 00635 source_map[src].remove_ostream(lvl, o); 00636 } 00637 00638 00639 void dbg::detach_all_ostreams(dbg::level lvl) 00640 { 00641 out(debug) << prefix(debug) << "dbg::detach_all_ostreams(" 00642 << LEVEL_NAMES[lvl] 00643 << ")\n"; 00644 00645 source_map[""].clear_ostream(lvl); 00646 } 00647 00648 00649 void dbg::detach_all_ostreams(dbg::level lvl, dbg::dbg_source src) 00650 { 00651 out(debug) << prefix(debug) << "dbg::detach_all_ostreams(" 00652 << LEVEL_NAMES[lvl] 00653 << ", \"" << src << "\")\n"; 00654 00655 source_map[src].clear_ostream(lvl); 00656 } 00657 00658 00659 /****************************************************************************** 00660 * Output formatting 00661 *****************************************************************************/ 00662 00663 void dbg::set_prefix(const char *pfx) 00664 { 00665 out(debug) << prefix(debug) << "dbg::set_prefix(" << pfx << ")\n"; 00666 00667 indent_prefix = pfx; 00668 } 00669 00670 00671 void dbg::enable_level_prefix(bool enabled) 00672 { 00673 out(debug) << prefix(debug) << "dbg::enable_level_prefix(" 00674 << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n"; 00675 00676 level_prefix = enabled; 00677 } 00678 00679 00680 void dbg::enable_time_prefix(bool enabled) 00681 { 00682 out(debug) << prefix(debug) << "dbg::enable_time_prefix(" 00683 << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n"; 00684 00685 time_prefix = enabled; 00686 } 00687 00688 00689 std::ostream &dbg::operator<<(std::ostream &s, const prefix &p) 00690 { 00691 s << indent_prefix.c_str(); 00692 do_prefix(p.l, s); 00693 return s; 00694 } 00695 00696 00697 std::ostream &dbg::operator<<(std::ostream &s, const indent &i) 00698 { 00699 s << indent_prefix.c_str(); 00700 do_prefix(i.l, s); 00701 for (unsigned int n = 0; n < indent_depth; n++) s << INDENT; 00702 return s; 00703 } 00704 00705 00706 std::ostream &dbg::operator<<(std::ostream &s, const source_pos &pos) 00707 { 00708 print_pos(s, pos); 00709 return s; 00710 } 00711 00712 00713 /****************************************************************************** 00714 * Behaviour 00715 *****************************************************************************/ 00716 00717 void dbg::set_assertion_behaviour(level lvl, dbg::assertion_behaviour b) 00718 { 00719 out(debug) << prefix(debug) << "dbg::set_assertion_behaviour(" 00720 << LEVEL_NAMES[lvl] << "," << BEHAVIOUR_NAMES[b] << ")\n"; 00721 00722 if (lvl < dbg::all) 00723 { 00724 behaviour[lvl] = b; 00725 } 00726 else 00727 { 00728 for (int n = 0; n < dbg::all; n++) 00729 { 00730 behaviour[n] = b; 00731 } 00732 } 00733 } 00734 00735 00736 void dbg::set_assertion_period(dbgclock_t p) 00737 { 00738 out(debug) << prefix(debug) << "dbg::set_assertion_period(" 00739 << p << ")\n"; 00740 00741 if (!p && period) 00742 { 00743 period_map.clear(); 00744 } 00745 00746 period = p; 00747 00748 if (p && STDCLK::clock() == -1) 00749 { 00750 period = p; 00751 out(debug) << prefix(debug) 00752 << "*** WARNING ***\n" 00753 << "Platform does not support std::clock, and so\n" 00754 << "dbg::set_assertion_period is not supported.\n"; 00755 } 00756 } 00757 00758 00759 /****************************************************************************** 00760 * Assertion 00761 *****************************************************************************/ 00762 00763 void dbg::assertion(dbg::level lvl, dbg::dbg_source src, 00764 const assert_info &info) 00765 { 00766 determine_source(src, info); 00767 00768 if (source_map[src].enabled(lvl) && !info.asserted && period_allows(info)) 00769 { 00770 std::ostream &o = out(lvl, src); 00771 00772 o << indent(lvl) << "assertion \"" << info.text << "\" failed "; 00773 if (strcmp(src, "")) 00774 { 00775 o << "for \"" << src << "\" "; 00776 } 00777 o << "at "; 00778 print_pos(o, info); 00779 print_period_info(o, info); 00780 o << "\n"; 00781 00782 do_assertion_behaviour(lvl, why_assertion, info); 00783 } 00784 } 00785 00786 00787 /****************************************************************************** 00788 * Sentinel 00789 *****************************************************************************/ 00790 00791 void dbg::sentinel(dbg::level lvl, dbg::dbg_source src, const source_pos &here) 00792 { 00793 determine_source(src, here); 00794 00795 if (source_map[src].enabled(lvl) && period_allows(here)) 00796 { 00797 std::ostream &o = out(lvl, src); 00798 o << indent(lvl) << "sentinel reached at "; 00799 print_pos(o, here); 00800 print_period_info(o, here); 00801 o << "\n"; 00802 00803 do_assertion_behaviour(lvl, why_sentinel, here); 00804 } 00805 } 00806 00807 00808 /****************************************************************************** 00809 * Unimplemented 00810 *****************************************************************************/ 00811 00812 void dbg::unimplemented(dbg::level lvl, dbg::dbg_source src, 00813 const source_pos &here) 00814 { 00815 determine_source(src, here); 00816 00817 if (source_map[src].enabled(lvl) && period_allows(here)) 00818 { 00819 std::ostream &o = out(lvl, src); 00820 o << indent(lvl) << "behaviour not yet implemented at "; 00821 print_pos(o, here); 00822 print_period_info(o, here); 00823 o << "\n"; 00824 00825 do_assertion_behaviour(lvl, why_unimplemented, here); 00826 } 00827 } 00828 00829 00830 /****************************************************************************** 00831 * Pointer checking 00832 *****************************************************************************/ 00833 00834 void dbg::check_ptr(dbg::level lvl, dbg::dbg_source src, 00835 const void *p, const source_pos &here) 00836 { 00837 determine_source(src, here); 00838 00839 if (source_map[src].enabled(lvl) && p == 0 && period_allows(here)) 00840 { 00841 std::ostream &o = out(lvl, src); 00842 o << indent(lvl) << "pointer is zero at "; 00843 print_pos(o, here); 00844 print_period_info(o, here); 00845 o << "\n"; 00846 00847 do_assertion_behaviour(lvl, why_check_ptr, here); 00848 } 00849 } 00850 00851 00852 /****************************************************************************** 00853 * Bounds checking 00854 *****************************************************************************/ 00855 00856 void dbg::check_bounds(dbg::level lvl, dbg::dbg_source src, 00857 int index, int bound, const source_pos &here) 00858 { 00859 determine_source(src, here); 00860 00861 if (source_map[src].enabled(lvl) 00862 && index >= 0 && index >= bound 00863 && period_allows(here)) 00864 { 00865 std::ostream &o = out(lvl, src); 00866 o << indent(lvl) << "index " << index << " is out of bounds (" 00867 << bound << ") at "; 00868 print_pos(o, here); 00869 print_period_info(o, here); 00870 o << "\n"; 00871 00872 do_assertion_behaviour(lvl, why_check_ptr, here); 00873 } 00874 } 00875 00876 00877 /****************************************************************************** 00878 * Tracing 00879 *****************************************************************************/ 00880 00881 dbg::trace::trace(func_name_t name) 00882 : m_src(0), m_name(name), m_pos(DBG_HERE), m_triggered(false) 00883 { 00884 determine_source(m_src, m_pos); 00885 00886 if (source_map[m_src].enabled(dbg::tracing)) 00887 { 00888 trace_begin(); 00889 } 00890 } 00891 00892 00893 dbg::trace::trace(dbg_source src, func_name_t name) 00894 : m_src(src), m_name(name), m_pos(DBG_HERE), m_triggered(false) 00895 { 00896 determine_source(m_src, m_pos); 00897 00898 if (source_map[m_src].enabled(dbg::tracing)) 00899 { 00900 trace_begin(); 00901 } 00902 } 00903 00904 00905 dbg::trace::trace(const source_pos &where) 00906 : m_src(0), m_name(0), m_pos(where), m_triggered(false) 00907 { 00908 determine_source(m_src, m_pos); 00909 00910 if (source_map[m_src].enabled(dbg::tracing)) 00911 { 00912 trace_begin(); 00913 } 00914 } 00915 00916 00917 dbg::trace::trace(dbg_source src, const source_pos &where) 00918 : m_src(src), m_name(0), m_pos(where), m_triggered(false) 00919 { 00920 determine_source(m_src, m_pos); 00921 00922 if (source_map[src].enabled(dbg::tracing)) 00923 { 00924 trace_begin(); 00925 } 00926 } 00927 00928 00929 dbg::trace::~trace() 00930 { 00931 if (m_triggered) 00932 { 00933 trace_end(); 00934 } 00935 } 00936 00937 00938 void dbg::trace::trace_begin() 00939 { 00940 std::ostream &o = out(dbg::tracing, m_src); 00941 o << indent(tracing); 00942 indent_depth++; 00943 o << TRACE_IN; 00944 if (m_name) 00945 { 00946 o << m_name; 00947 } 00948 else 00949 { 00950 print_pos_short(o, m_pos); 00951 } 00952 if (m_src && strcmp(m_src, "")) 00953 { 00954 o << " (for \"" << m_src << "\")"; 00955 } 00956 o << std::endl; 00957 00958 m_triggered = true; 00959 } 00960 00961 00962 void dbg::trace::trace_end() 00963 { 00964 std::ostream &o = out(dbg::tracing, m_src); 00965 indent_depth--; 00966 o << indent(tracing); 00967 o << TRACE_OUT; 00968 if (m_name) 00969 { 00970 o << m_name; 00971 } 00972 else 00973 { 00974 print_pos_short(o, m_pos); 00975 } 00976 if (m_src && strcmp(m_src, "")) 00977 { 00978 o << " (for \"" << m_src << "\")"; 00979 } 00980 o << std::endl; 00981 } 00982 00983 00984 /****************************************************************************** 00985 * Internal implementation 00986 *****************************************************************************/ 00987 00988 namespace 00989 { 00990 /************************************************************************** 00991 * dbg_streambuf 00992 *************************************************************************/ 00993 00994 dbg_streambuf::dbg_streambuf(std::vector<std::ostream*> &o, int bsize) 00995 : ostreams(o) 00996 { 00997 if (bsize) 00998 { 00999 char *ptr = new char[bsize]; 01000 setp(ptr, ptr + bsize); 01001 } 01002 else 01003 { 01004 setp(0, 0); 01005 } 01006 setg(0, 0, 0); 01007 } 01008 01009 dbg_streambuf::~dbg_streambuf() 01010 { 01011 sync(); 01012 delete [] pbase(); 01013 } 01014 01015 int dbg_streambuf::overflow(int c) 01016 { 01017 put_buffer(); 01018 if (c != EOF) 01019 { 01020 if (pbase() == epptr()) 01021 { 01022 put_char(c); 01023 } 01024 else 01025 { 01026 sputc(c); 01027 } 01028 } 01029 return 0; 01030 } 01031 01032 int dbg_streambuf::sync() 01033 { 01034 put_buffer(); 01035 return 0; 01036 } 01037 01038 void dbg_streambuf::put_buffer(void) 01039 { 01040 if (pbase() != pptr()) 01041 { 01042 std::vector<std::ostream *>::iterator i = ostreams.begin(); 01043 while (i != ostreams.end()) 01044 { 01045 (*i)->write(pbase(), pptr() - pbase()); 01046 ++i; 01047 } 01048 setp(pbase(), epptr()); 01049 } 01050 } 01051 01052 void dbg_streambuf::put_char(int c) 01053 { 01054 std::vector<std::ostream *>::iterator i = ostreams.begin(); 01055 while (i != ostreams.end()) 01056 { 01057 (**i) << static_cast<char>(c); 01058 ++i; 01059 } 01060 } 01061 01062 01063 /************************************************************************** 01064 * dbg_ostream 01065 *************************************************************************/ 01066 01067 void dbg_ostream::add(std::ostream &o) 01068 { 01069 if (std::find(streams.begin(), streams.end(), &o) == streams.end()) 01070 { 01071 streams.push_back(&o); 01072 } 01073 } 01074 01075 void dbg_ostream::remove(std::ostream &o) 01076 { 01077 stream_vec_type::iterator i 01078 = std::find(streams.begin(), streams.end(), &o); 01079 if (i != streams.end()) 01080 { 01081 streams.erase(i); 01082 } 01083 } 01084 01085 void dbg_ostream::clear() 01086 { 01087 streams.clear(); 01088 } 01089 01090 01091 /************************************************************************** 01092 * source_info 01093 *************************************************************************/ 01094 01095 source_info::source_info(ConstructionStyle cs) 01096 : levels(cs ? source_map[dbg::default_source].levels : 0), 01097 dbg_streams(raw_cast().dbg_streams) 01098 { 01099 if (cs) 01100 { 01101 new (raw_dbg_streams) 01102 array_type(source_map[dbg::default_source].raw_cast()); 01103 } 01104 else 01105 { 01106 new (raw_dbg_streams) array_type; 01107 // add cerr to the error and fatal levels. 01108 add_ostream(dbg::error, std::cerr); 01109 add_ostream(dbg::fatal, std::cerr); 01110 } 01111 } 01112 01113 source_info::source_info(const source_info &rhs) 01114 : levels(rhs.levels), dbg_streams(raw_cast().dbg_streams) 01115 { 01116 new (raw_dbg_streams) array_type(rhs.raw_cast()); 01117 } 01118 01119 source_info::~source_info() 01120 { 01121 raw_cast().~array_type(); 01122 } 01123 01124 void source_info::enable(dbg::level lvl, bool status) 01125 { 01126 levels &= ~dbg_source_mask(lvl); 01127 if (status) 01128 { 01129 levels |= dbg_source_mask(lvl); 01130 } 01131 } 01132 01133 void source_info::add_ostream(dbg::level lvl, std::ostream &o) 01134 { 01135 if (lvl == dbg::all) 01136 { 01137 for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n) 01138 { 01139 dbg_streams[n].add(o); 01140 } 01141 } 01142 else 01143 { 01144 dbg_streams[lvl].add(o); 01145 } 01146 } 01147 01148 void source_info::remove_ostream(dbg::level lvl, std::ostream &o) 01149 { 01150 if (lvl == dbg::all) 01151 { 01152 for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n) 01153 { 01154 dbg_streams[n].remove(o); 01155 } 01156 } 01157 else 01158 { 01159 dbg_streams[lvl].remove(o); 01160 } 01161 } 01162 01163 void source_info::clear_ostream(dbg::level lvl) 01164 { 01165 if (lvl == dbg::all) 01166 { 01167 for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n) 01168 { 01169 dbg_streams[n].clear(); 01170 } 01171 } 01172 else 01173 { 01174 dbg_streams[lvl].clear(); 01175 } 01176 } 01177 01178 std::ostream &source_info::out(dbg::level lvl) 01179 { 01180 if (lvl == dbg::none || !enabled(lvl)) 01181 { 01182 return null_ostream; 01183 } 01184 else 01185 { 01186 return dbg_streams[lvl]; 01187 } 01188 } 01189 01190 01191 /************************************************************************** 01192 * period_data 01193 *************************************************************************/ 01194 01195 period_data::period_data() 01196 : no_triggers(0), triggered_at(STDCLK::clock() - period*2) 01197 { 01198 } 01199 01200 01201 /************************************************************************** 01202 * Functions 01203 *************************************************************************/ 01204 01205 void print_pos(std::ostream &out, const dbg::source_pos &where) 01206 { 01207 if (where.file) 01208 { 01209 if (where.func) 01210 { 01211 out << "function: " << where.func << ", "; 01212 } 01213 out << "line: " << where.line << ", file: " << where.file; 01214 } 01215 } 01216 01217 void print_pos_short(std::ostream &out, const dbg::source_pos &where) 01218 { 01219 if (where.file) 01220 { 01221 if (where.func) 01222 { 01223 out << where.func << " (" << where.line 01224 << " in " << where.file << ")"; 01225 } 01226 else 01227 { 01228 out << "function at (" << where.line 01229 << " in " << where.file << ")"; 01230 } 01231 } 01232 } 01233 01234 void print_period_info(std::ostream &out, const dbg::source_pos &where) 01235 { 01236 if (period) 01237 { 01238 size_t no_triggers = period_map[where].no_triggers; 01239 out << " (triggered " << no_triggers << " time"; 01240 if (no_triggers > 1) 01241 { 01242 out << "s)"; 01243 } 01244 else 01245 { 01246 out << ")"; 01247 } 01248 } 01249 } 01250 01251 void do_assertion_behaviour(dbg::level lvl, constraint_type why, 01252 const dbg::source_pos &pos) 01253 { 01254 switch (lvl != dbg::fatal ? behaviour[lvl] : dbg::assertions_abort) 01255 { 01256 case dbg::assertions_abort: 01257 { 01258 abort(); 01259 break; 01260 } 01261 case dbg::assertions_throw: 01262 { 01263 switch (why) 01264 { 01265 default: 01266 case why_assertion: 01267 { 01268 throw dbg::assertion_exception(pos); 01269 break; 01270 } 01271 case why_sentinel: 01272 { 01273 throw dbg::sentinel_exception(pos); 01274 break; 01275 } 01276 case why_unimplemented: 01277 { 01278 throw dbg::unimplemented_exception(pos); 01279 break; 01280 } 01281 case why_check_ptr: 01282 { 01283 throw dbg::check_ptr_exception(pos); 01284 break; 01285 } 01286 } 01287 break; 01288 } 01289 case dbg::assertions_continue: 01290 default: 01291 { 01292 break; 01293 } 01294 } 01295 } 01296 01297 void do_prefix(dbg::level lvl, std::ostream &s) 01298 { 01299 if (time_prefix) 01300 { 01301 STDCLK::time_t t = STDCLK::time(0); 01302 if (t != -1) 01303 { 01304 s << std::string(STDCLK::ctime(&t), 24) << ": "; 01305 } 01306 } 01307 if (level_prefix) 01308 { 01309 switch (lvl) 01310 { 01311 case dbg::info: { s << " info: "; break; } 01312 case dbg::warning: { s << "warning: "; break; } 01313 case dbg::error: { s << " error: "; break; } 01314 case dbg::fatal: { s << " fatal: "; break; } 01315 case dbg::tracing: { s << " trace: "; break; } 01316 case dbg::debug: { s << " debug: "; break; } 01317 case dbg::none: { break; } 01318 case dbg::all: { s << " all: "; break; } 01319 } 01320 } 01321 } 01322 01323 bool period_allows_impl(const dbg::source_pos &where) 01324 { 01325 period_data &data = period_map[where]; 01326 data.no_triggers++; 01327 if (data.triggered_at < STDCLK::clock() - period) 01328 { 01329 data.triggered_at = STDCLK::clock(); 01330 return true; 01331 } 01332 else 01333 { 01334 return false; 01335 } 01336 } 01337 01338 void determine_source(dbg::dbg_source &src, const dbg::source_pos &here) 01339 { 01340 if (!src) src = ""; 01341 if (src == "" && here.src) 01342 { 01343 src = here.src; 01344 } 01345 } 01346 }