libdbg 1.2
dbg.cpp
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 }