libdbg  1.2
dbg.cpp
1 /*
2  * File: dbg.cpp
3  * Author: Pete Goodliffe
4  * Version: 1.10
5  * Created: 7 June 2001
6  *
7  * Purpose: C++ debugging support library
8  *
9  * Copyright (c) Pete Goodliffe 2001-2002 (pete@cthree.org)
10  *
11  * This file is modifiable/redistributable under the terms of the GNU
12  * Lesser General Public License.
13  *
14  * You should have recieved a copy of the GNU General Public License along
15  * with this program; see the file COPYING. If not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 0211-1307, USA.
17  */
18 
19 #ifndef DBG_ENABLED
20 #define DBG_ENABLED
21 #endif
22 
23 #include "dbg.h"
24 
25 #include <iostream>
26 #include <cstdlib>
27 #include <cstring>
28 #include <cstdio>
29 #include <string>
30 #include <vector>
31 #include <map>
32 #include <algorithm>
33 #include <new>
34 
35  /**********************************************************************
36  * Implementation notes
37  **********************************************************************
38  * Tested and found to work ok under
39  * - gcc 2.96
40  * - gcc 3.0
41  * - gcc 3.1
42  * - gcc 3.2
43  * - bcc32 5.5.1
44  * - MSVC 6.0
45  *
46  * MSVC v6.0
47  * - This platform makes me cry.
48  * - There are NUMEROUS hacks around it's deficient behaviour, just
49  * look for conditional complation based around _MSC_VER
50  * - The <ctime> header doesn't put all the definitions into the std
51  * namespace.
52  * - This means that we have to sacrifice our good namespace-based code
53  * for something more disgusting and primitve.
54  * - Where this has happened, and where in the future I'd really like to
55  * put the "std" namespace back in, I have instead used a STDCLK macro.
56  * See the implementation comment about this below for more grief.
57  * - A documented hack has been made in the dbg.h header file, of slightly
58  * less ghastly proportions. See dbgclock_t there.
59  * - Additionally, the dbg::array_size template utility could be (and was)
60  * more elegantly be written:
61  * template <class T, int size>
62  * inline unsigned int array_size(T (&array)[size])
63  * {
64  * return size;
65  * }
66  * Of course, MSVC doesn't like that. Sigh. The version in dbg.h also
67  * works, its just not quite so nice.
68  * - The map implentation of MSVC doesn't provide data_type, so I have to
69  * hack around that.
70  * - The compiler doesn't like the dbg_ostream calling it's parent
71  * constructor by the name "ostream", it doesn't recognise the typedef.
72  * Ugh.
73  *
74  * Other thoughts:
75  * - Break out to debugger facility?
76  * - Only works for ostreams, not all basic_ostreams
77  * - Post-conditions are a bit limited, this is more of a C++
78  * language limitation, really.
79  *********************************************************************/
80 
81 /******************************************************************************
82  * Tedious compiler-specific issues
83  *****************************************************************************/
84 
85 // Work around MSVC 6.0
86 #ifdef _MSC_VER
87 #define STDCLK
88 #pragma warning(disable:4786)
89 #else
90 // In an ideal world, the following line would be
91 // namespace STDCLK = std;
92 // However, gcc 2.96 doesn't seem to cope well with namespace aliases.
93 // Sigh.
94 #define STDCLK std
95 #endif
96 
97 // Quieten tedius build warnings on Borland C++ compiler
98 #ifdef __BCPLUSPLUS__
99 #pragma warn -8066
100 #pragma warn -8071
101 #pragma warn -8070
102 #endif
103 
104 /******************************************************************************
105  * General dbg library private declarations
106  *****************************************************************************/
107 
108 namespace
109 {
110  /**************************************************************************
111  * Constants
112  *************************************************************************/
113 
114  const char *LEVEL_NAMES[] =
115  {
116  "info",
117  "warning",
118  "error",
119  "fatal",
120  "tracing",
121  "debug",
122  "none",
123  "all"
124  };
125  const char *BEHAVIOUR_NAMES[] =
126  {
127  "assertions_abort",
128  "assertions_throw",
129  "assertions_continue"
130  };
131  enum constraint_type
132  {
133  why_assertion,
134  why_sentinel,
135  why_unimplemented,
136  why_check_ptr
137  };
138 
139  const char *TRACE_IN = "->";
140  const char *TRACE_OUT = "<-";
141  const char *INDENT = " ";
142  const char *PREFIX = "*** ";
143  const char *TRUE_STRING = "true";
144  const char *FALSE_STRING = "false";
145  const unsigned int ALL_SOURCES_MASK = 0xff;
146  const unsigned int NUM_DBG_LEVELS = dbg::all-1;
147 
148  /**************************************************************************
149  * Internal types
150  *************************************************************************/
151 
158  struct period_data
159  {
160  size_t no_triggers;
161  STDCLK::clock_t triggered_at;
162 
163  period_data();
164  };
165 
171  struct lt_sp
172  {
173  bool operator()(const dbg::source_pos &a, const dbg::source_pos &b)
174  const
175  {
176  if (a.file == b.file)
177  {
178  if (a.func == b.func)
179  {
180  return a.line < b.line;
181  }
182  else
183  {
184  return a.func < b.func;
185  }
186  }
187  else
188  {
189  return a.file < b.file;
190  }
191  }
192  };
193 
205  class dbg_streambuf : public std::streambuf
206  {
207  public:
208 
209  dbg_streambuf(std::vector<std::ostream*> &ostreams, int bsize = 0);
210  ~dbg_streambuf();
211 
212  int pubsync() { return sync(); }
213 
214  protected:
215 
216  int overflow(int);
217  int sync();
218 
219  private:
220 
221  void put_buffer(void);
222  void put_char(int);
223 
224  std::vector<std::ostream *> &ostreams;
225  };
226 
237  class null_streambuf : public std::streambuf
238  {
239  public:
240 
241  null_streambuf() {}
242  ~null_streambuf() {}
243 
244  protected:
245 
246  int overflow(int) { return 0; }
247  int sync() { return 0; }
248  };
249 
259  class dbg_ostream : public std::ostream
260  {
261  public:
262 
263 #ifndef _MSC_VER
264  dbg_ostream() : std::ostream(&dbg_buf), dbg_buf(streams) {}
265  dbg_ostream(const dbg_ostream &rhs)
266  : std::ostream(&dbg_buf), streams(rhs.streams),
267  dbg_buf(streams) {}
268 #else
269  // MSVC workaround. Sigh. It won't let us call the parent ctor as
270  // "ostream" - it doesn't like the use of a typedef. On the other
271  // hand gcc 2.96 doesn't provide basic_ostream, so I can't call the
272  // base basic_ostream<> class there.
273  dbg_ostream()
274  : std::basic_ostream<char>(&dbg_buf), dbg_buf(streams) {}
275  dbg_ostream(const dbg_ostream &rhs)
276  : std::basic_ostream<char>(&dbg_buf), streams(rhs.streams),
277  dbg_buf(streams) {}
278 #endif
279  ~dbg_ostream() { dbg_buf.pubsync(); }
280 
281  void add(std::ostream &o);
282  void remove(std::ostream &o);
283  void clear();
284 
285  private:
286 
287  dbg_ostream &operator=(const dbg_ostream&);
288 
289  typedef std::vector<std::ostream*> stream_vec_type;
290 
291  stream_vec_type streams;
292  dbg_streambuf dbg_buf;
293  };
294 
307  class source_info
308  {
309  public:
310 
316  enum ConstructionStyle
317  {
318  ConstructTheDefaultSource = 0,
319  ConstructCopyOfDefaultSource = 1
320  };
321 
322  source_info(ConstructionStyle cs = ConstructCopyOfDefaultSource);
323  source_info(const source_info &rhs);
324  ~source_info();
325 
330  void enable(dbg::level lvl, bool enable);
331 
335  bool enabled(dbg::level lvl) const
336  {
337  return (levels & dbg_source_mask(lvl)) != 0;
338  }
339 
343  void add_ostream(dbg::level lvl, std::ostream &o);
344 
348  void remove_ostream(dbg::level lvl, std::ostream &o);
349 
353  void clear_ostream(dbg::level lvl);
354 
359  std::ostream &out(dbg::level lvl);
360 
361  private:
362 
367  static unsigned int dbg_source_mask(dbg::level lvl)
368  {
369  return (lvl != dbg::all) ? 1 << lvl : ALL_SOURCES_MASK;
370  }
371 
372  unsigned int levels;
373 
374  // We do a placement new of the dbg_streams array.
375  // It looks somewhat tacky, but it allows us to have a single
376  // constructor, which simplifies the client interface of this class.
377  // It specifically avoids tonnes of grotesque unused dbg_ostream
378  // constructions, as you'd create an array, and then copy the
379  // default_source elements directly over these freshly constructed
380  // elements. dbg_ostream is complex enough that this matters.
381  // If we didn't have a clever cloning constructor, these lines
382  // would just be "dbg_ostream dbg_streams[NUM_DBG_LEVELS];" and
383  // we'd suffer a whole load of redundant dbg_ostream constructions.
384  /*
385  typedef dbg_ostream array_type[NUM_DBG_LEVELS];
386  dbg_ostream *dbg_streams;
387  unsigned char raw_dbg_streams[sizeof(array_type)];
388  */
389  struct array_type
390  {
391  // I wrap this up in an enclosing struct to make it obvious
392  // how to destroy the array "in place". To be honest I couldn't
393  // figure the syntax for the corresponding delete for a
394  // placement new array constrution.
395  dbg_ostream dbg_streams[NUM_DBG_LEVELS];
396  };
397  dbg_ostream *dbg_streams;
398  unsigned char raw_dbg_streams[sizeof(array_type)];
399 
400  array_type &raw_cast()
401  {
402  return *reinterpret_cast<array_type*>(raw_dbg_streams);
403  }
404  const array_type &raw_cast() const
405  {
406  return *reinterpret_cast<const array_type*>(raw_dbg_streams);
407  }
408  };
409 
420  class source_map_type
421  {
422  public:
423 
424  typedef std::map<std::string, source_info> map_type;
425  typedef map_type::iterator iterator;
426  typedef map_type::key_type key_type;
427 #ifndef _MSC_VER
428  // Replaced data_type by mapped_type, since there is no
429  // more data_type, data_type was remove in g++ 3.3.
430  typedef map_type::mapped_type data_type;
431 #else
432  // MSVC. Just don't ask.
433  typedef source_info data_type;
434 #endif
435  source_map_type()
436  {
437  // Insert the default_source into the map
438  _map.insert(
439  std::make_pair(dbg::default_source,
440  source_info(source_info::ConstructTheDefaultSource)));
441  // Insert the unnamed source into the map too
442  _map.insert(
443  std::make_pair(dbg::dbg_source(""),
444  source_info(source_info::ConstructTheDefaultSource)));
445  }
446  iterator begin() { return _map.begin(); }
447  iterator end() { return _map.end(); }
448  data_type &operator[](key_type key) { return _map[key]; }
449 
450  private:
451 
452  map_type _map;
453  };
454 
455  typedef std::map<dbg::source_pos, period_data, lt_sp> period_map_type;
456 
457  /**************************************************************************
458  * Internal variables
459  *************************************************************************/
460 
461  // The stream to write to when no output is required.
462  std::ostream null_ostream(new null_streambuf());
463 
464  dbg::assertion_behaviour behaviour[dbg::all+1] =
465  {
466  dbg::assertions_abort,
467  dbg::assertions_abort,
468  dbg::assertions_abort,
469  dbg::assertions_abort,
470  dbg::assertions_abort,
471  dbg::assertions_abort,
472  dbg::assertions_abort,
473  dbg::assertions_abort
474  };
475 
476  unsigned int indent_depth = 0;
477  std::string indent_prefix = PREFIX;
478  bool level_prefix = false;
479  bool time_prefix = false;
480  STDCLK::clock_t period = 0;
481  source_map_type source_map;
482  period_map_type period_map;
483 
484  /**************************************************************************
485  * Function declarations
486  *************************************************************************/
487 
491  void print_pos(std::ostream &out, const dbg::source_pos &where);
492 
497  void print_pos_short(std::ostream &out, const dbg::source_pos &where);
498 
503  void print_period_info(std::ostream &out, const dbg::source_pos &where);
504 
509  void do_assertion_behaviour(dbg::level lvl, constraint_type why,
510  const dbg::source_pos &pos);
511 
516  void do_prefix(dbg::level lvl, std::ostream &s);
517 
521  bool period_allows_impl(const dbg::source_pos &where);
522 
531  inline bool period_allows(const dbg::source_pos &where)
532  {
533  return !period || period_allows_impl(where);
534  }
535 
541  void determine_source(dbg::dbg_source &src, const dbg::source_pos &here);
542 }
543 
544 
545 /******************************************************************************
546  * Miscellaneous public bobbins
547  *****************************************************************************/
548 
549 dbg::dbg_source dbg::default_source = "dbg::private::default_source";
550 
551 
552 /******************************************************************************
553  * Enable/disable dbg facilities
554  *****************************************************************************/
555 
556 void dbg::enable(dbg::level lvl, bool enabled)
557 {
558  out(debug) << prefix(debug) << "dbg::enable(" << LEVEL_NAMES[lvl]
559  << "," << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
560 
561  source_map[""].enable(lvl, enabled);
562 }
563 
564 
565 void dbg::enable(dbg::level lvl, dbg::dbg_source src, bool enabled)
566 {
567  out(debug) << prefix(debug) << "dbg::enable(" << LEVEL_NAMES[lvl]
568  << ",\"" << src << "\","
569  << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
570 
571  source_map[src].enable(lvl, enabled);
572 }
573 
574 
575 void dbg::enable_all(dbg::level lvl, bool enabled)
576 {
577  out(debug) << prefix(debug) << "dbg::enable_all("
578  << LEVEL_NAMES[lvl] << ","
579  << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
580 
581  source_map_type::iterator i = source_map.begin();
582  for ( ; i != source_map.end(); ++i)
583  {
584  (i->second).enable(lvl, enabled);
585  }
586 }
587 
588 
589 /******************************************************************************
590  * Logging
591  *****************************************************************************/
592 
593 std::ostream &dbg::out(dbg::level lvl, dbg::dbg_source src)
594 {
595  return source_map[src ? src : ""].out(lvl);
596 }
597 
598 
599 void dbg::attach_ostream(dbg::level lvl, std::ostream &o)
600 {
601  out(debug) << prefix(debug) << "dbg::attach_ostream("
602  << LEVEL_NAMES[lvl] << ",ostream)\n";
603 
604  source_map[""].add_ostream(lvl, o);
605 }
606 
607 
608 void dbg::attach_ostream(dbg::level lvl, dbg::dbg_source src, std::ostream &o)
609 {
610  out(debug) << prefix(debug) << "dbg::attach_ostream("
611  << LEVEL_NAMES[lvl]
612  << ", \"" << src
613  << "\" ,ostream)\n";
614 
615  source_map[src].add_ostream(lvl, o);
616 }
617 
618 
619 void dbg::detach_ostream(dbg::level lvl, std::ostream &o)
620 {
621  out(debug) << prefix(debug) << "dbg::detach_ostream("
622  << LEVEL_NAMES[lvl] << ")\n";
623 
624  source_map[""].remove_ostream(lvl, o);
625 }
626 
627 
628 void dbg::detach_ostream(dbg::level lvl, dbg::dbg_source src, std::ostream &o)
629 {
630  out(debug) << prefix(debug) << "dbg::detach_ostream("
631  << LEVEL_NAMES[lvl]
632  << ", \"" << src
633  << "\" ,ostream)\n";
634 
635  source_map[src].remove_ostream(lvl, o);
636 }
637 
638 
639 void dbg::detach_all_ostreams(dbg::level lvl)
640 {
641  out(debug) << prefix(debug) << "dbg::detach_all_ostreams("
642  << LEVEL_NAMES[lvl]
643  << ")\n";
644 
645  source_map[""].clear_ostream(lvl);
646 }
647 
648 
649 void dbg::detach_all_ostreams(dbg::level lvl, dbg::dbg_source src)
650 {
651  out(debug) << prefix(debug) << "dbg::detach_all_ostreams("
652  << LEVEL_NAMES[lvl]
653  << ", \"" << src << "\")\n";
654 
655  source_map[src].clear_ostream(lvl);
656 }
657 
658 
659 /******************************************************************************
660  * Output formatting
661  *****************************************************************************/
662 
663 void dbg::set_prefix(const char *pfx)
664 {
665  out(debug) << prefix(debug) << "dbg::set_prefix(" << pfx << ")\n";
666 
667  indent_prefix = pfx;
668 }
669 
670 
671 void dbg::enable_level_prefix(bool enabled)
672 {
673  out(debug) << prefix(debug) << "dbg::enable_level_prefix("
674  << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
675 
676  level_prefix = enabled;
677 }
678 
679 
680 void dbg::enable_time_prefix(bool enabled)
681 {
682  out(debug) << prefix(debug) << "dbg::enable_time_prefix("
683  << (enabled ? TRUE_STRING : FALSE_STRING) << ")\n";
684 
685  time_prefix = enabled;
686 }
687 
688 
689 std::ostream &dbg::operator<<(std::ostream &s, const prefix &p)
690 {
691  s << indent_prefix.c_str();
692  do_prefix(p.l, s);
693  return s;
694 }
695 
696 
697 std::ostream &dbg::operator<<(std::ostream &s, const indent &i)
698 {
699  s << indent_prefix.c_str();
700  do_prefix(i.l, s);
701  for (unsigned int n = 0; n < indent_depth; n++) s << INDENT;
702  return s;
703 }
704 
705 
706 std::ostream &dbg::operator<<(std::ostream &s, const source_pos &pos)
707 {
708  print_pos(s, pos);
709  return s;
710 }
711 
712 
713 /******************************************************************************
714  * Behaviour
715  *****************************************************************************/
716 
717 void dbg::set_assertion_behaviour(level lvl, dbg::assertion_behaviour b)
718 {
719  out(debug) << prefix(debug) << "dbg::set_assertion_behaviour("
720  << LEVEL_NAMES[lvl] << "," << BEHAVIOUR_NAMES[b] << ")\n";
721 
722  if (lvl < dbg::all)
723  {
724  behaviour[lvl] = b;
725  }
726  else
727  {
728  for (int n = 0; n < dbg::all; n++)
729  {
730  behaviour[n] = b;
731  }
732  }
733 }
734 
735 
736 void dbg::set_assertion_period(dbgclock_t p)
737 {
738  out(debug) << prefix(debug) << "dbg::set_assertion_period("
739  << p << ")\n";
740 
741  if (!p && period)
742  {
743  period_map.clear();
744  }
745 
746  period = p;
747 
748  if (p && STDCLK::clock() == -1)
749  {
750  period = p;
751  out(debug) << prefix(debug)
752  << "*** WARNING ***\n"
753  << "Platform does not support std::clock, and so\n"
754  << "dbg::set_assertion_period is not supported.\n";
755  }
756 }
757 
758 
759 /******************************************************************************
760  * Assertion
761  *****************************************************************************/
762 
763 void dbg::assertion(dbg::level lvl, dbg::dbg_source src,
764  const assert_info &info)
765 {
766  determine_source(src, info);
767 
768  if (source_map[src].enabled(lvl) && !info.asserted && period_allows(info))
769  {
770  std::ostream &o = out(lvl, src);
771 
772  o << indent(lvl) << "assertion \"" << info.text << "\" failed ";
773  if (strcmp(src, ""))
774  {
775  o << "for \"" << src << "\" ";
776  }
777  o << "at ";
778  print_pos(o, info);
779  print_period_info(o, info);
780  o << "\n";
781 
782  do_assertion_behaviour(lvl, why_assertion, info);
783  }
784 }
785 
786 
787 /******************************************************************************
788  * Sentinel
789  *****************************************************************************/
790 
791 void dbg::sentinel(dbg::level lvl, dbg::dbg_source src, const source_pos &here)
792 {
793  determine_source(src, here);
794 
795  if (source_map[src].enabled(lvl) && period_allows(here))
796  {
797  std::ostream &o = out(lvl, src);
798  o << indent(lvl) << "sentinel reached at ";
799  print_pos(o, here);
800  print_period_info(o, here);
801  o << "\n";
802 
803  do_assertion_behaviour(lvl, why_sentinel, here);
804  }
805 }
806 
807 
808 /******************************************************************************
809  * Unimplemented
810  *****************************************************************************/
811 
812 void dbg::unimplemented(dbg::level lvl, dbg::dbg_source src,
813  const source_pos &here)
814 {
815  determine_source(src, here);
816 
817  if (source_map[src].enabled(lvl) && period_allows(here))
818  {
819  std::ostream &o = out(lvl, src);
820  o << indent(lvl) << "behaviour not yet implemented at ";
821  print_pos(o, here);
822  print_period_info(o, here);
823  o << "\n";
824 
825  do_assertion_behaviour(lvl, why_unimplemented, here);
826  }
827 }
828 
829 
830 /******************************************************************************
831  * Pointer checking
832  *****************************************************************************/
833 
834 void dbg::check_ptr(dbg::level lvl, dbg::dbg_source src,
835  const void *p, const source_pos &here)
836 {
837  determine_source(src, here);
838 
839  if (source_map[src].enabled(lvl) && p == 0 && period_allows(here))
840  {
841  std::ostream &o = out(lvl, src);
842  o << indent(lvl) << "pointer is zero at ";
843  print_pos(o, here);
844  print_period_info(o, here);
845  o << "\n";
846 
847  do_assertion_behaviour(lvl, why_check_ptr, here);
848  }
849 }
850 
851 
852 /******************************************************************************
853  * Bounds checking
854  *****************************************************************************/
855 
856 void dbg::check_bounds(dbg::level lvl, dbg::dbg_source src,
857  int index, int bound, const source_pos &here)
858 {
859  determine_source(src, here);
860 
861  if (source_map[src].enabled(lvl)
862  && index >= 0 && index >= bound
863  && period_allows(here))
864  {
865  std::ostream &o = out(lvl, src);
866  o << indent(lvl) << "index " << index << " is out of bounds ("
867  << bound << ") at ";
868  print_pos(o, here);
869  print_period_info(o, here);
870  o << "\n";
871 
872  do_assertion_behaviour(lvl, why_check_ptr, here);
873  }
874 }
875 
876 
877 /******************************************************************************
878  * Tracing
879  *****************************************************************************/
880 
881 dbg::trace::trace(func_name_t name)
882 : m_src(0), m_name(name), m_pos(DBG_HERE), m_triggered(false)
883 {
884  determine_source(m_src, m_pos);
885 
886  if (source_map[m_src].enabled(dbg::tracing))
887  {
888  trace_begin();
889  }
890 }
891 
892 
893 dbg::trace::trace(dbg_source src, func_name_t name)
894 : m_src(src), m_name(name), m_pos(DBG_HERE), m_triggered(false)
895 {
896  determine_source(m_src, m_pos);
897 
898  if (source_map[m_src].enabled(dbg::tracing))
899  {
900  trace_begin();
901  }
902 }
903 
904 
905 dbg::trace::trace(const source_pos &where)
906 : m_src(0), m_name(0), m_pos(where), m_triggered(false)
907 {
908  determine_source(m_src, m_pos);
909 
910  if (source_map[m_src].enabled(dbg::tracing))
911  {
912  trace_begin();
913  }
914 }
915 
916 
917 dbg::trace::trace(dbg_source src, const source_pos &where)
918 : m_src(src), m_name(0), m_pos(where), m_triggered(false)
919 {
920  determine_source(m_src, m_pos);
921 
922  if (source_map[src].enabled(dbg::tracing))
923  {
924  trace_begin();
925  }
926 }
927 
928 
929 dbg::trace::~trace()
930 {
931  if (m_triggered)
932  {
933  trace_end();
934  }
935 }
936 
937 
938 void dbg::trace::trace_begin()
939 {
940  std::ostream &o = out(dbg::tracing, m_src);
941  o << indent(tracing);
942  indent_depth++;
943  o << TRACE_IN;
944  if (m_name)
945  {
946  o << m_name;
947  }
948  else
949  {
950  print_pos_short(o, m_pos);
951  }
952  if (m_src && strcmp(m_src, ""))
953  {
954  o << " (for \"" << m_src << "\")";
955  }
956  o << std::endl;
957 
958  m_triggered = true;
959 }
960 
961 
962 void dbg::trace::trace_end()
963 {
964  std::ostream &o = out(dbg::tracing, m_src);
965  indent_depth--;
966  o << indent(tracing);
967  o << TRACE_OUT;
968  if (m_name)
969  {
970  o << m_name;
971  }
972  else
973  {
974  print_pos_short(o, m_pos);
975  }
976  if (m_src && strcmp(m_src, ""))
977  {
978  o << " (for \"" << m_src << "\")";
979  }
980  o << std::endl;
981 }
982 
983 
984 /******************************************************************************
985  * Internal implementation
986  *****************************************************************************/
987 
988 namespace
989 {
990  /**************************************************************************
991  * dbg_streambuf
992  *************************************************************************/
993 
994  dbg_streambuf::dbg_streambuf(std::vector<std::ostream*> &o, int bsize)
995  : ostreams(o)
996  {
997  if (bsize)
998  {
999  char *ptr = new char[bsize];
1000  setp(ptr, ptr + bsize);
1001  }
1002  else
1003  {
1004  setp(0, 0);
1005  }
1006  setg(0, 0, 0);
1007  }
1008 
1009  dbg_streambuf::~dbg_streambuf()
1010  {
1011  sync();
1012  delete [] pbase();
1013  }
1014 
1015  int dbg_streambuf::overflow(int c)
1016  {
1017  put_buffer();
1018  if (c != EOF)
1019  {
1020  if (pbase() == epptr())
1021  {
1022  put_char(c);
1023  }
1024  else
1025  {
1026  sputc(c);
1027  }
1028  }
1029  return 0;
1030  }
1031 
1032  int dbg_streambuf::sync()
1033  {
1034  put_buffer();
1035  return 0;
1036  }
1037 
1038  void dbg_streambuf::put_buffer(void)
1039  {
1040  if (pbase() != pptr())
1041  {
1042  std::vector<std::ostream *>::iterator i = ostreams.begin();
1043  while (i != ostreams.end())
1044  {
1045  (*i)->write(pbase(), pptr() - pbase());
1046  ++i;
1047  }
1048  setp(pbase(), epptr());
1049  }
1050  }
1051 
1052  void dbg_streambuf::put_char(int c)
1053  {
1054  std::vector<std::ostream *>::iterator i = ostreams.begin();
1055  while (i != ostreams.end())
1056  {
1057  (**i) << static_cast<char>(c);
1058  ++i;
1059  }
1060  }
1061 
1062 
1063  /**************************************************************************
1064  * dbg_ostream
1065  *************************************************************************/
1066 
1067  void dbg_ostream::add(std::ostream &o)
1068  {
1069  if (std::find(streams.begin(), streams.end(), &o) == streams.end())
1070  {
1071  streams.push_back(&o);
1072  }
1073  }
1074 
1075  void dbg_ostream::remove(std::ostream &o)
1076  {
1077  stream_vec_type::iterator i
1078  = std::find(streams.begin(), streams.end(), &o);
1079  if (i != streams.end())
1080  {
1081  streams.erase(i);
1082  }
1083  }
1084 
1085  void dbg_ostream::clear()
1086  {
1087  streams.clear();
1088  }
1089 
1090 
1091  /**************************************************************************
1092  * source_info
1093  *************************************************************************/
1094 
1095  source_info::source_info(ConstructionStyle cs)
1096  : levels(cs ? source_map[dbg::default_source].levels : 0),
1097  dbg_streams(raw_cast().dbg_streams)
1098  {
1099  if (cs)
1100  {
1101  new (raw_dbg_streams)
1102  array_type(source_map[dbg::default_source].raw_cast());
1103  }
1104  else
1105  {
1106  new (raw_dbg_streams) array_type;
1107  // add cerr to the error and fatal levels.
1108  add_ostream(dbg::error, std::cerr);
1109  add_ostream(dbg::fatal, std::cerr);
1110  }
1111  }
1112 
1113  source_info::source_info(const source_info &rhs)
1114  : levels(rhs.levels), dbg_streams(raw_cast().dbg_streams)
1115  {
1116  new (raw_dbg_streams) array_type(rhs.raw_cast());
1117  }
1118 
1119  source_info::~source_info()
1120  {
1121  raw_cast().~array_type();
1122  }
1123 
1124  void source_info::enable(dbg::level lvl, bool status)
1125  {
1126  levels &= ~dbg_source_mask(lvl);
1127  if (status)
1128  {
1129  levels |= dbg_source_mask(lvl);
1130  }
1131  }
1132 
1133  void source_info::add_ostream(dbg::level lvl, std::ostream &o)
1134  {
1135  if (lvl == dbg::all)
1136  {
1137  for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n)
1138  {
1139  dbg_streams[n].add(o);
1140  }
1141  }
1142  else
1143  {
1144  dbg_streams[lvl].add(o);
1145  }
1146  }
1147 
1148  void source_info::remove_ostream(dbg::level lvl, std::ostream &o)
1149  {
1150  if (lvl == dbg::all)
1151  {
1152  for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n)
1153  {
1154  dbg_streams[n].remove(o);
1155  }
1156  }
1157  else
1158  {
1159  dbg_streams[lvl].remove(o);
1160  }
1161  }
1162 
1163  void source_info::clear_ostream(dbg::level lvl)
1164  {
1165  if (lvl == dbg::all)
1166  {
1167  for (unsigned int n = 0; n < NUM_DBG_LEVELS; ++n)
1168  {
1169  dbg_streams[n].clear();
1170  }
1171  }
1172  else
1173  {
1174  dbg_streams[lvl].clear();
1175  }
1176  }
1177 
1178  std::ostream &source_info::out(dbg::level lvl)
1179  {
1180  if (lvl == dbg::none || !enabled(lvl))
1181  {
1182  return null_ostream;
1183  }
1184  else
1185  {
1186  return dbg_streams[lvl];
1187  }
1188  }
1189 
1190 
1191  /**************************************************************************
1192  * period_data
1193  *************************************************************************/
1194 
1195  period_data::period_data()
1196  : no_triggers(0), triggered_at(STDCLK::clock() - period*2)
1197  {
1198  }
1199 
1200 
1201  /**************************************************************************
1202  * Functions
1203  *************************************************************************/
1204 
1205  void print_pos(std::ostream &out, const dbg::source_pos &where)
1206  {
1207  if (where.file)
1208  {
1209  if (where.func)
1210  {
1211  out << "function: " << where.func << ", ";
1212  }
1213  out << "line: " << where.line << ", file: " << where.file;
1214  }
1215  }
1216 
1217  void print_pos_short(std::ostream &out, const dbg::source_pos &where)
1218  {
1219  if (where.file)
1220  {
1221  if (where.func)
1222  {
1223  out << where.func << " (" << where.line
1224  << " in " << where.file << ")";
1225  }
1226  else
1227  {
1228  out << "function at (" << where.line
1229  << " in " << where.file << ")";
1230  }
1231  }
1232  }
1233 
1234  void print_period_info(std::ostream &out, const dbg::source_pos &where)
1235  {
1236  if (period)
1237  {
1238  size_t no_triggers = period_map[where].no_triggers;
1239  out << " (triggered " << no_triggers << " time";
1240  if (no_triggers > 1)
1241  {
1242  out << "s)";
1243  }
1244  else
1245  {
1246  out << ")";
1247  }
1248  }
1249  }
1250 
1251  void do_assertion_behaviour(dbg::level lvl, constraint_type why,
1252  const dbg::source_pos &pos)
1253  {
1254  switch (lvl != dbg::fatal ? behaviour[lvl] : dbg::assertions_abort)
1255  {
1256  case dbg::assertions_abort:
1257  {
1258  abort();
1259  break;
1260  }
1261  case dbg::assertions_throw:
1262  {
1263  switch (why)
1264  {
1265  default:
1266  case why_assertion:
1267  {
1268  throw dbg::assertion_exception(pos);
1269  break;
1270  }
1271  case why_sentinel:
1272  {
1273  throw dbg::sentinel_exception(pos);
1274  break;
1275  }
1276  case why_unimplemented:
1277  {
1278  throw dbg::unimplemented_exception(pos);
1279  break;
1280  }
1281  case why_check_ptr:
1282  {
1283  throw dbg::check_ptr_exception(pos);
1284  break;
1285  }
1286  }
1287  break;
1288  }
1289  case dbg::assertions_continue:
1290  default:
1291  {
1292  break;
1293  }
1294  }
1295  }
1296 
1297  void do_prefix(dbg::level lvl, std::ostream &s)
1298  {
1299  if (time_prefix)
1300  {
1301  STDCLK::time_t t = STDCLK::time(0);
1302  if (t != -1)
1303  {
1304  s << std::string(STDCLK::ctime(&t), 24) << ": ";
1305  }
1306  }
1307  if (level_prefix)
1308  {
1309  switch (lvl)
1310  {
1311  case dbg::info: { s << " info: "; break; }
1312  case dbg::warning: { s << "warning: "; break; }
1313  case dbg::error: { s << " error: "; break; }
1314  case dbg::fatal: { s << " fatal: "; break; }
1315  case dbg::tracing: { s << " trace: "; break; }
1316  case dbg::debug: { s << " debug: "; break; }
1317  case dbg::none: { break; }
1318  case dbg::all: { s << " all: "; break; }
1319  }
1320  }
1321  }
1322 
1323  bool period_allows_impl(const dbg::source_pos &where)
1324  {
1325  period_data &data = period_map[where];
1326  data.no_triggers++;
1327  if (data.triggered_at < STDCLK::clock() - period)
1328  {
1329  data.triggered_at = STDCLK::clock();
1330  return true;
1331  }
1332  else
1333  {
1334  return false;
1335  }
1336  }
1337 
1338  void determine_source(dbg::dbg_source &src, const dbg::source_pos &here)
1339  {
1340  if (!src) src = "";
1341  if (src == "" && here.src)
1342  {
1343  src = here.src;
1344  }
1345  }
1346 }
assertion_behaviour
This enum type describes what happens when a debugging assertion fails.
Definition: dbg.h:353
The type of exception thrown by unimplemented.
Definition: dbg.h:518
dbg library
Definition: dbg.h:292
const char * dbg_source
typedef for a string that describes the "source" of a diagnostic.
Definition: dbg.h:379
The type of exception thrown by check_ptr.
Definition: dbg.h:528
Data structure describing a position in the source file.
Definition: dbg.h:431
The type of exception thrown by sentinel.
Definition: dbg.h:508
level
The various predefined debugging levels.
Definition: dbg.h:325
The type of exception thrown by assertion.
Definition: dbg.h:498