/*
* File: dbg.cpp
* Author: Pete Goodliffe
* Version: 1.20
* Created: 20 July 2003
*
* Purpose: C++ debugging support library
*
* Copyright (c) Pete Goodliffe 2001-2002 (pete@cthree.org)
*
* This file is modifiable/redistributable under the terms of the GNU
* Lesser General Public License.
*
* You should have recieved a copy of the GNU General Public License along
* with this program; see the file COPYING. If not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 0211-1307, USA.
*/
#ifndef DBG_ENABLED
#define DBG_ENABLED
#endif
#include "dbg.h"
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <new>
/**********************************************************************
* Implementation notes
**********************************************************************
* Tested and found to work ok under
* - gcc 2.96
* - gcc 3.0
* - gcc 3.1
* - gcc 3.2
* - bcc32 5.5.1
* - MSVC 6.0
*
* MSVC v6.0
* - This platform makes me cry.
* - There are NUMEROUS hacks around it's deficient behaviour, just
* look for conditional complation based around _MSC_VER
* - The <ctime> header doesn't put all the definitions into the std
* namespace.
* - This means that we have to sacrifice our good namespace-based code
* for something more disgusting and primitve.
* - Where this has happened, and where in the future I'd really like to
* put the "std" namespace back in, I have instead used a STDCLK macro.
* See the implementation comment about this below for more grief.
* - A documented hack has been made in the dbg.h header file, of slightly
* less ghastly proportions. See dbgclock_t there.
* - Additionally, the dbg::array_size template utility could be (and was)
* more elegantly be written:
* template <class T, int size>
* inline unsigned int array_size(T (&array)[size])
* {
* return size;
* }
* Of course, MSVC doesn't like that. Sigh. The version in dbg.h also
* works, its just not quite so nice.
* - The map implentation of MSVC doesn't provide data_type, so I have to
* hack around that.
* - The compiler doesn't like the dbg_ostream calling it's parent
* constructor by the name "ostream", it doesn't recognise the typedef.
* Ugh.
*
* Other thoughts:
* - Break out to debugger facility?
* - Only works for ostreams, not all basic_ostreams
* - Post-conditions are a bit limited, this is more of a C++
* language limitation, really.
*********************************************************************/
/******************************************************************************
* Tedious compiler-specific issues
*****************************************************************************/
// Work around MSVC 6.0
#ifdef _MSC_VER
#define STDCLK
#pragma warning(disable:4786)
#else
// In an ideal world, the following line would be
// namespace STDCLK = std;
// However, gcc 2.96 doesn't seem to cope well with namespace aliases.
// Sigh.
#define STDCLK std
#endif
// Quieten tedius build warnings on Borland C++ compiler
#ifdef __BCPLUSPLUS__
#pragma warn -8066
#pragma warn -8071
#pragma warn -8070
#endif
/******************************************************************************
* General dbg library private declarations
*****************************************************************************/
namespace
{
/**************************************************************************
* Constants
*************************************************************************/
const char *LEVEL_NAMES[] =
{
"info",
"warning",
"error",
"fatal",
"tracing",
"debug",
"none",
"all"
};
const char *BEHAVIOUR_NAMES[] =
{
"assertions_abort",
"assertions_throw",
"assertions_continue"
};
enum constraint_type
{
why_assertion,
why_sentinel,
why_unimplemented,
why_check_ptr
};
const char *TRACE_IN = "->";
const char *TRACE_OUT = "<-";
const char *INDENT = " ";
const char *PREFIX = "*** ";
const char *TRUE_STRING = "true";
const char *FALSE_STRING = "false";
const unsigned int ALL_SOURCES_MASK = 0xff;
const unsigned int NUM_DBG_LEVELS = dbg::all-1;
/**************************************************************************
* Internal types
*************************************************************************/
/**
* Period information about a particular @ref source_pos. Used if
* assertion periods are enabled.
*
* @internal
*/
struct period_data
{
size_t no_triggers;
STDCLK::clock_t triggered_at;
period_data();
};
/**
* Functor to provide comparison of @ref dbg::source_pos structs.
*
* @internal
*/
struct lt_sp
{
bool operator()(const dbg::source_pos &a, const dbg::source_pos &b)
const
{
if (a.file == b.file)
{
if (a.func == b.func)
{
return a.line < b.line;
}
else
{
return a.func < b.func;
}
}
else
{
return a.file < b.file;
}
}
};
/**
* A handy std::streambuf that sends its input to multiple output streams.
*
* This is the cornerstone of the dbg output stream magic.
*
* The multiple streams are recorded in a std::vector. The vector is
* external to this class, and maintained by the @ref dbg_ostream which
* uses this streambuf.
*
* @internal
*/
class dbg_streambuf : public std::streambuf
{
public:
dbg_streambuf(std::vector<std::ostream*> &ostreams, int bsize = 0);
~dbg_streambuf();
int pubsync() { return sync(); }
protected:
int overflow(int);
int sync();
private:
void put_buffer(void);
void put_char(int);
std::vector<std::ostream *> &ostreams;
};
/**
* A handy streambuf that swallows its output and doesn't burp.
*
* This is used for disabled diagnostic output streams.
*
* There is a single stream object created using this streambuf,
* null_ostream.
*
* @internal
*/
class null_streambuf : public std::streambuf
{
public:
null_streambuf() {}
~null_streambuf() {}
protected:
int overflow(int) { return 0; }
int sync() { return 0; }
};
/**
* This class provides an ostream using the @ref dbg_streambuf. It manages
* the vector on which the @ref dbg_streambuf relies. It provides methods
* to add/remove and clear streams.
*
* There are some minor hacks to support MSVC which cause small headaches.
*
* @internal
*/
class dbg_ostream : public std::ostream
{
public:
#ifndef _MSC_VER
dbg_ostream() : std::ostream(&dbg_buf), dbg_buf(streams) {}
dbg_ostream(const dbg_ostream &rhs)
: std::ostream(&dbg_buf), streams(rhs.streams),
dbg_buf(streams) {}
#else
// MSVC workaround. Sigh. It won't let us call the parent ctor as
// "ostream" - it doesn't like the use of a typedef. On the other
// hand gcc 2.96 doesn't provide basic_ostream, so I can't call the
// base basic_ostream<> class there.
dbg_ostream()
: std::basic_ostream<char>(&dbg_buf), dbg_buf(streams) {}
dbg_ostream(const dbg_ostream &rhs)
: std::basic_ostream<char>(&dbg_buf), streams(rhs.streams),
dbg_buf(streams) {}
#endif
~dbg_ostream() { dbg_buf.pubsync(); }
void add(std::ostream &o);
void remove(std::ostream &o);
void clear();
private:
dbg_ostream &operator=(const dbg_ostream&);
typedef std::vector<std::ostream*> stream_vec_type;
stream_vec_type streams;
dbg_streambuf dbg_buf;
};
/**
* The source_info class holds all the information associated with a
* debugging source. That is, the level enable settings and the
* set of output streams for each level.
*
* source_info objects are held in the @ref source_map. The default
* constructor magically takes settings from the @ref dbg::default_source.
* This actually makes the constructor code a little tortuous, but it's
* worth it.
*
* @internal
*/
class source_info
{
public:
/**
* The source_info can either be constructed as a copy of the
* default_source or not. The only time we choose not to is
* when constructing default_source!
*/
enum ConstructionStyle
{
ConstructTheDefaultSource = 0,
ConstructCopyOfDefaultSource = 1
};
source_info(ConstructionStyle cs = ConstructCopyOfDefaultSource);
source_info(const source_info &rhs);
~source_info();
/**
* Enable or disable the given level, depending on the value
* of the boolean.
*/
void enable(dbg::level lvl, bool enable);
/**
* Returns whether or not the level is enabled.
*/
bool enabled(dbg::level lvl) const
{
return (levels & dbg_source_mask(lvl)) != 0;
}
/**
* Add an ostream to catch output at the specified level.
*/
void add_ostream(dbg::level lvl, std::ostream &o);
/**
* Remove an ostream from the specified level.
*/
void remove_ostream(dbg::level lvl, std::ostream &o);
/**
* Clear all ostreams from the given level.
*/
void clear_ostream(dbg::level lvl);
/**
* Return a stream to write to for the given level, or the
* @ref null_ostream if the level is not enabled for this source.
*/
std::ostream &out(dbg::level lvl);
private:
/**
* Creates a unsigned int mask that is used as the second value
* of the sources map.
*/
static unsigned int dbg_source_mask(dbg::level lvl)
{
return (lvl != dbg::all) ? 1 << lvl : ALL_SOURCES_MASK;
}
unsigned int levels;
// We do a placement new of the dbg_streams array.
// It looks somewhat tacky, but it allows us to have a single
// constructor, which simplifies the client interface of this class.
// It specifically avoids tonnes of grotesque unused dbg_ostream
// constructions, as you'd create an array, and then copy the
// default_source elements directly over these freshly constructed
// elements. dbg_ostream is complex enough that this matters.
// If we didn't have a clever cloning constructor, these lines
// would just be "dbg_ostream dbg_streams[NUM_DBG_LEVELS];" and
// we'd suffer a whole load of redundant dbg_ostream constructions.
/*
typedef dbg_ostream array_type[NUM_DBG_LEVELS];
dbg_ostream *dbg_streams;
unsigned char raw_dbg_streams[sizeof(array_type)];
*/
struct array_type
{
// I wrap this up in an enclosing struct to make it obvious
// how to destroy the array "in place". To be honest I couldn't
// figure the syntax for the corresponding delete for a
// placement new array constrution.
dbg_ostream dbg_streams[NUM_DBG_LEVELS];
};
dbg_ostream *dbg_streams;
unsigned char raw_dbg_streams[sizeof(array_type)];
array_type &raw_cast()
{
return *reinterpret_cast<array_type*>(raw_dbg_streams);
}
const array_type &raw_cast() const
{
return *reinterpret_cast<const array_type*>(raw_dbg_streams);
}
};
/**
* This class provides a "map" type for the source_map. It's a thin veneer
* around a std::map. The only reason for it's existance is the ctor which
* creates the default source_info, and inserts it into the map.
*
* Only the member functions that are needed below have been implemented,
* each as forwards to the std::map methods.
*
* @internal
*/
class source_map_type
{
public:
typedef std::map<std::string, source_info> map_type;
typedef map_type::iterator iterator;
typedef map_type::key_type key_type;
#ifndef _MSC_VER
typedef map_type::data_type data_type; // pre 3.3
typedef map_type::mapped_type data_type; // gcc 3.3
#else
// MSVC. Just don't ask.
typedef source_info data_type;
#endif
source_map_type()
{
// Insert the default_source into the map
_map.insert(
std::make_pair(dbg::default_source,
source_info(source_info::ConstructTheDefaultSource)));
// Insert the unnamed source into the map too
_map.insert(
std::make_pair(dbg::dbg_source(""),
source_info(source_info::ConstructTheDefaultSource)));
}
iterator begin() { return _map.begin(); }
iterator end() { return _map.end(); }
data_type &operator[](key_type key) { return _map[key]; }
private:
map_type _map;
};
typedef std::map<dbg::source_pos, period_data, lt_sp> period_map_type;
/**************************************************************************
* Internal variables
*************************************************************************/
// The stream to write to when no output is required.
std::ostream null_ostream(new null_streambuf());
dbg::assertion_behaviour behaviour[dbg::all+1] =
{
dbg::assertions_abort,
dbg::assertions_abort,
dbg::assertions_abort,
dbg::assertions_abort,
dbg::assertions_abort,
dbg::assertions_abort,
dbg::assertions_abort,
dbg::assertions_abort
};
unsigned int indent_depth = 0;
std::string indent_prefix = PREFIX;
bool level_prefix = false;
bool time_prefix = false;
STDCLK::clock_t period = 0;
source_map_type source_map;
period_map_type period_map;
/**************************************************************************
* Function declarations
*************************************************************************/
/**
* Prints a source_pos to the given ostream.
*/
void print_pos(std::ostream &out, const dbg::source_pos &where);
/**
* Prints a source_pos to the given ostream in short format
* (suitable for trace).
*/
void print_pos_short(std::ostream &out, const dbg::source_pos &where);
/**
* Prints period information to the given ostream, if a period has been
* enabled.
*/
void print_period_info(std::ostream &out, const dbg::source_pos &where);
/**
* Does whatever the assertion_behaviour is set to. If an assertion
* is triggered, then this will be called.
*/
void do_assertion_behaviour(dbg::level lvl, constraint_type why,
const dbg::source_pos &pos);
/**
* Produces a level prefix for the specified level to the
* given ostream.
*/
void do_prefix(dbg::level lvl, std::ostream &s);
/**
* Used by period_allows below.
*/
bool period_allows_impl(const dbg::source_pos &where);
/**
* Returns whether the period allows the constraint at the specified
* @ref dbg::source_pos to trigger. This presumes that the assertion
* at this position has shown to be broken already.
*
* This is a small inline function to make code that uses the period
* implementation easier to read.
*/
inline bool period_allows(const dbg::source_pos &where)
{
return !period || period_allows_impl(where);
}
/**
* Given a dbg_source (which could be a zero pointer, or a source string)
* and the source_pos (which could contain a DBG_SOURCE defintion, or
* zero), work out what the dbg_source name to use is.
*/
void determine_source(dbg::dbg_source &src, const dbg::source_pos &here);
}
/******************************************************************************
* Miscellaneous public bobbins
*****************************************************************************/
dbg::dbg_source dbg::default_source = "dbg::private::default_source";
/******************************************************************************
* Enable/disable dbg facilities
*****************************************************************************/
void dbg::enable(dbg::level lvl, bool enabled)
{
out(debug) << prefix(debug) << "dbg::enable(" << LEVEL_NAMES[lvl]
<< "," << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
source_map[""].enable(lvl, enabled);
}
void dbg::enable(dbg::level lvl, dbg::dbg_source src, bool enabled)
{
out(debug) << prefix(debug) << "dbg::enable(" << LEVEL_NAMES[lvl]
<< ",\"" << src << "\","
<< (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
source_map[src].enable(lvl, enabled);
}
void dbg::enable_all(dbg::level lvl, bool enabled)
{
out(debug) << prefix(debug) << "dbg::enable_all("
<< LEVEL_NAMES[lvl] << ","
<< (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
source_map_type::iterator i = source_map.begin();
for ( ; i != source_map.end(); ++i)
{
(i->second).enable(lvl, enabled);
}
}
/******************************************************************************
* Logging
*****************************************************************************/
std::ostream &dbg::out(dbg::level lvl, dbg::dbg_source src)
{
return source_map[src ? src : ""].out(lvl);
}
void dbg::attach_ostream(dbg::level lvl, std::ostream &o)
{
out(debug) << prefix(debug) << "dbg::attach_ostream("
<< LEVEL_NAMES[lvl] << ",ostream)\n";
source_map[""].add_ostream(lvl, o);
}
void dbg::attach_ostream(dbg::level lvl, dbg::dbg_source src, std::ostream &o)
{
out(debug) << prefix(debug) << "dbg::attach_ostream("
<< LEVEL_NAMES[lvl]
<< ", \"" << src
<< "\" ,ostream)\n";
source_map[src].add_ostream(lvl, o);
}
void dbg::detach_ostream(dbg::level lvl, std::ostream &o)
{
out(debug) << prefix(debug) << "dbg::detach_ostream("
<< LEVEL_NAMES[lvl] << ")\n";
source_map[""].remove_ostream(lvl, o);
}
void dbg::detach_ostream(dbg::level lvl, dbg::dbg_source src, std::ostream &o)
{
out(debug) << prefix(debug) << "dbg::detach_ostream("
<< LEVEL_NAMES[lvl]
<< ", \"" << src
<< "\" ,ostream)\n";
source_map[src].remove_ostream(lvl, o);
}
void dbg::detach_all_ostreams(dbg::level lvl)
{
out(debug) << prefix(debug) << "dbg::detach_all_ostreams("
<< LEVEL_NAMES[lvl]
<< ")\n";
source_map[""].clear_ostream(lvl);
}
void dbg::detach_all_ostreams(dbg::level lvl, dbg::dbg_source src)
{
out(debug) << prefix(debug) << "dbg::detach_all_ostreams("
<< LEVEL_NAMES[lvl]
<< ", \"" << src << "\")\n";
source_map[src].clear_ostream(lvl);
}
/******************************************************************************
* Output formatting
*****************************************************************************/
void dbg::set_prefix(const char *pfx)
{
out(debug) << prefix(debug) << "dbg::set_prefix(" << pfx << ")\n";
indent_prefix = pfx;
}
void dbg::enable_level_prefix(bool enabled)
{
out(debug) << prefix(debug) << "dbg::enable_level_prefix("
<< (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
level_prefix = enabled;
}
void dbg::enable_time_prefix(bool enabled)
{
out(debug) << prefix(debug) << "dbg::enable_time_prefix("
<< (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
time_prefix = enabled;
}
std::ostream &dbg::operator<<(std::ostream &s, const prefix &p)
{
s << indent_prefix.c_str();
do_prefix(p.l, s);
return s;
}
std::ostream &dbg::operator<<(std::ostream &s, const indent &i)
{
s << indent_prefix.c_str();
do_prefix(i.l, s);
for (unsigned int n = 0; n < indent_depth; n++) s << INDENT;
return s;
}
std::ostream &dbg::operator<<(std::ostream &s, const source_pos &pos)
{
print_pos(s, pos);
return s;
}
/******************************************************************************
* Behaviour
*****************************************************************************/
void dbg::set_assertion_behaviour(level lvl, dbg::assertion_behaviour b)
{
out(debug) << prefix(debug) << "dbg::set_assertion_behaviour("
<< LEVEL_NAMES[lvl] << "," << BEHAVIOUR_NAMES[b] << ")\n";
if (lvl < dbg::all)
{
behaviour[lvl] = b;
}
else
{
for (int n = 0; n < dbg::all; n++)
{
behaviour[n] = b;
}
}
}
void dbg::set_assertion_period(dbgclock_t p)
{
out(debug) << prefix(debug) << "dbg::set_assertion_period("
<< p << ")\n";
if (!p && period)
{
period_map.clear();
}
period = p;
if (p && STDCLK::clock() == -1)
{
period = p;
out(debug) << prefix(debug)
<< "*** WARNING ***\n"
<< "Platform does not support std::clock, and so\n"
<< "dbg::set_assertion_period is not supported.\n";
}
}
/******************************************************************************
* Assertion
*****************************************************************************/
void dbg::assertion(dbg::level lvl, dbg::dbg_source src,
const assert_info &info)
{
determine_source(src, info);
if (source_map[src].enabled(lvl) && !info.asserted && period_allows(info))
{
std::ostream &o = out(lvl, src);
o << indent(lvl) << "assertion \"" << info.text << "\" failed ";
if (strcmp(src, ""))
{
o << "for \"" << src << "\" ";
}
o << "at ";
print_pos(o, info);
print_period_info(o, info);
o << "\n";
do_assertion_behaviour(lvl, why_assertion, info);
}
}
/******************************************************************************
* Sentinel
*****************************************************************************/
void dbg::sentinel(dbg::level lvl, dbg::dbg_source src, const source_pos &here)
{
determine_source(src, here);
if (source_map[src].enabled(lvl) && period_allows(here))
{
std::ostream &o = out(lvl, src);
o << indent(lvl) << "sentinel reached at ";
print_pos(o, here);
print_period_info(o, here);
o << "\n";
do_assertion_behaviour(lvl, why_sentinel, here);
}
}
/******************************************************************************
* Unimplemented
*****************************************************************************/
void dbg::unimplemented(dbg::level lvl, dbg::dbg_source src,
const source_pos &here)
{
determine_source(src, here);
if (source_map[src].enabled(lvl) && period_allows(here))
{
std::ostream &o = out(lvl, src);
o << indent(lvl) << "behaviour not yet implemented at ";
print_pos(o, here);
print_period_info(o, here);
o << "\n";
do_assertion_behaviour(lvl, why_unimplemented, here);
}
}
/******************************************************************************
* Pointer checking
*****************************************************************************/
void dbg::check_ptr(dbg::level lvl, dbg::dbg_source src,
const void *p, const source_pos &here)
{
determine_source(src, here);
if (source_map[src].enabled(lvl) && p == 0 && period_allows(here))
{
std::ostream &o = out(lvl, src);
o << indent(lvl) << "pointer is zero at ";
print_pos(o, here);
print_period_info(o, here);
o << "\n";
do_assertion_behaviour(lvl, why_check_ptr, here);
}
}
/******************************************************************************
* Bounds checking
*****************************************************************************/
void dbg::check_bounds(dbg::level lvl, dbg::dbg_source src,
int index, int bound, const source_pos &here)
{
determine_source(src, here);
if (source_map[src].enabled(lvl)
&& index >= 0 && index >= bound
&& period_allows(here))
{
std::ostream &o = out(lvl, src);
o << indent(lvl) << "index " << index << " is out of bounds ("
<< bound << ") at ";
print_pos(o, here);
print_period_info(o, here);
o << "\n";
do_assertion_behaviour(lvl, why_check_ptr, here);
}
}
/******************************************************************************
* Tracing
*****************************************************************************/
dbg::trace::trace(func_name_t name)
: m_src(0), m_name(name), m_pos(DBG_HERE), m_triggered(false)
{
determine_source(m_src, m_pos);
if (source_map[m_src].enabled(dbg::tracing))
{
trace_begin();
}
}
dbg::trace::trace(dbg_source src, func_name_t name)
: m_src(src), m_name(name), m_pos(DBG_HERE), m_triggered(false)
{
determine_source(m_src, m_pos);
if (source_map[m_src].enabled(dbg::tracing))
{
trace_begin();
}
}
dbg::trace::trace(const source_pos &where)
: m_src(0), m_name(0), m_pos(where), m_triggered(false)
{
determine_source(m_src, m_pos);
if (source_map[m_src].enabled(dbg::tracing))
{
trace_begin();
}
}
dbg::trace::trace(dbg_source src, const source_pos &where)
: m_src(src), m_name(0), m_pos(where), m_triggered(false)
{
determine_source(m_src, m_pos);
if (source_map[src].enabled(dbg::tracing))
{
trace_begin();
}
}
dbg::trace::~trace()
{
if (m_triggered)
{
trace_end();
}
}
void dbg::trace::trace_begin()
{
std::ostream &o = out(dbg::tracing, m_src);
o << indent(tracing);
indent_depth++;
o << TRACE_IN;
if (m_name)
{
o << m_name;
}
else
{
print_pos_short(o, m_pos);
}
if (m_src && strcmp(m_src, ""))
{
o << " (for \"" << m_src << "\")";
}
o << std::endl;
m_triggered = true;
}
void dbg::trace::trace_end()
{
std::ostream &o = out(dbg::tracing, m_src);
indent_depth--;
o << indent(tracing);
o << TRACE_OUT;
if (m_name)
{
o << m_name;
}
else
{
print_pos_short(o, m_pos);
}
if (m_src && strcmp(m_src, ""))
{
o << " (for \"" << m_src << "\")";
}
o << std::endl;
}
/******************************************************************************
* Internal implementation
*****************************************************************************/
namespace
{
/**************************************************************************
* dbg_streambuf
*************************************************************************/
dbg_streambuf::dbg_streambuf(std::vector<std::ostream*> &o, int bsize)
: ostreams(o)
{
if (bsize)
{
char *ptr = new char[bsize];
setp(ptr, ptr + bsize);
}
else
{
setp(0, 0);
}
setg(0, 0, 0);
}
dbg_streambuf::~dbg_streambuf()
{
sync();
delete [] pbase();
}
int dbg_streambuf::overflow(int c)
{
put_buffer();
if (c != EOF)
{
if (pbase() == epptr())
{
put_char(c);
}
else
{
sputc(c);
}
}
return 0;
}
int dbg_streambuf::sync()
{
put_buffer();
return 0;
}
void dbg_streambuf::put_buffer(void)
{
if (pbase() != pptr())
{
std::vector<std::ostream *>::iterator i = ostreams.begin();
while (i != ostreams.end())
{
(*i)->write(pbase(), pptr() - pbase());
++i;
}
setp(pbase(), epptr());
}
}
void dbg_streambuf::put_char(int c)
{
std::vector<std::ostream *>::iterator i = ostreams.begin();
while (i != ostreams.end())
{
(**i) << static_cast<char>(c);
++i;
}
}
/**************************************************************************
* dbg_ostream
*************************************************************************/
void dbg_ostream::add(std::ostream &o)
{
if (std::find(streams.begin(), streams.end(), &o) == streams.end())
{
streams.push_back(&o);
}
}
void dbg_ostream::remove(std::ostream &o)
{
stream_vec_type::iterator i
= std::find(streams.begin(), streams.end(), &o);
if (i != streams.end())
{
streams.erase(i);
}
}
void dbg_ostream::clear()
{
streams.clear();
}
/**************************************************************************
* source_info
*************************************************************************/
source_info::source_info(ConstructionStyle cs)
: levels(cs ? source_map[dbg::default_source].levels : 0),
dbg_streams(raw_cast().dbg_streams)
{
if (cs)
{
new (raw_dbg_streams)
array_type(source_map[dbg::default_source].raw_cast());
}
else
{
new (raw_dbg_streams) array_type;
// add cerr to the error and fatal levels.
add_ostream(dbg::error, std::cerr);
add_ostream(dbg::fatal, std::cerr);
}
}
source_info::source_info(const source_info &rhs)
: levels(rhs.levels), dbg_streams(raw_cast().dbg_streams)
{
new (raw_dbg_streams) array_type(rhs.raw_cast());
}
source_info::~source_info()
{
raw_cast().~array_type();
}
void source_info::enable(dbg::level lvl, bool status)
{
levels &= ~dbg_source_mask(lvl);
if (status)
{
levels |= dbg_source_mask(lvl);
}
}
void source_info::add_ostream(dbg::level lvl, std::ostream &o)
{
if (lvl == dbg::all)
{
for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n)
{
dbg_streams[n].add(o);
}
}
else
{
dbg_streams[lvl].add(o);
}
}
void source_info::remove_ostream(dbg::level lvl, std::ostream &o)
{
if (lvl == dbg::all)
{
for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n)
{
dbg_streams[n].remove(o);
}
}
else
{
dbg_streams[lvl].remove(o);
}
}
void source_info::clear_ostream(dbg::level lvl)
{
if (lvl == dbg::all)
{
for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n)
{
dbg_streams[n].clear();
}
}
else
{
dbg_streams[lvl].clear();
}
}
std::ostream &source_info::out(dbg::level lvl)
{
if (lvl == dbg::none || !enabled(lvl))
{
return null_ostream;
}
else
{
return dbg_streams[lvl];
}
}
/**************************************************************************
* period_data
*************************************************************************/
period_data::period_data()
: no_triggers(0), triggered_at(STDCLK::clock() - period*2)
{
}
/**************************************************************************
* Functions
*************************************************************************/
void print_pos(std::ostream &out, const dbg::source_pos &where)
{
if (where.file)
{
if (where.func)
{
out << "function: " << where.func << ", ";
}
out << "line: " << where.line << ", file: " << where.file;
}
}
void print_pos_short(std::ostream &out, const dbg::source_pos &where)
{
if (where.file)
{
if (where.func)
{
out << where.func << " (" << where.line
<< " in " << where.file << ")";
}
else
{
out << "function at (" << where.line
<< " in " << where.file << ")";
}
}
}
void print_period_info(std::ostream &out, const dbg::source_pos &where)
{
if (period)
{
size_t no_triggers = period_map[where].no_triggers;
out << " (triggered " << no_triggers << " time";
if (no_triggers > 1)
{
out << "s)";
}
else
{
out << ")";
}
}
}
void do_assertion_behaviour(dbg::level lvl, constraint_type why,
const dbg::source_pos &pos)
{
switch (lvl != dbg::fatal ? behaviour[lvl] : dbg::assertions_abort)
{
case dbg::assertions_abort:
{
abort();
break;
}
case dbg::assertions_throw:
{
switch (why)
{
default:
case why_assertion:
{
throw dbg::assertion_exception(pos);
break;
}
case why_sentinel:
{
throw dbg::sentinel_exception(pos);
break;
}
case why_unimplemented:
{
throw dbg::unimplemented_exception(pos);
break;
}
case why_check_ptr:
{
throw dbg::check_ptr_exception(pos);
break;
}
}
break;
}
case dbg::assertions_continue:
default:
{
break;
}
}
}
void do_prefix(dbg::level lvl, std::ostream &s)
{
if (time_prefix)
{
STDCLK::time_t t = STDCLK::time(0);
if (t != -1)
{
s << std::string(STDCLK::ctime(&t), 24) << ": ";
}
}
if (level_prefix)
{
switch (lvl)
{
case dbg::info: { s << " info: "; break; }
case dbg::warning: { s << "warning: "; break; }
case dbg::error: { s << " error: "; break; }
case dbg::fatal: { s << " fatal: "; break; }
case dbg::tracing: { s << " trace: "; break; }
case dbg::debug: { s << " debug: "; break; }
case dbg::none: { break; }
case dbg::all: { s << " all: "; break; }
}
}
}
bool period_allows_impl(const dbg::source_pos &where)
{
period_data &data = period_map[where];
data.no_triggers++;
if (data.triggered_at < STDCLK::clock() - period)
{
data.triggered_at = STDCLK::clock();
return true;
}
else
{
return false;
}
}
void determine_source(dbg::dbg_source &src, const dbg::source_pos &here)
{
if (!src) src = "";
if (src == "" && here.src)
{
src = here.src;
}
}
}