monotone

monotone Mtn Source Tree

Root/src/sanity.hh

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#ifndef __SANITY_HH__
11#define __SANITY_HH__
12
13#include <stdexcept>
14#include <ostream>
15#include <cstdio>
16
17#include "boost/current_function.hpp"
18
19#include "numeric_vocab.hh"
20#include "origin_type.hh"
21
22// our assertion / sanity / error logging system *was* based on GNU Nana,
23// but we're only using a small section of it, and have anyways rewritten
24// that to use typesafe boost-formatter stuff.
25
26// this is for error messages where we want a clean and inoffensive error
27// message to make it to the user, not a diagnostic error indicating
28// internal failure but a suggestion that they do something differently.
29
30namespace origin {
31 std::string type_to_string(type t);
32}
33
34// An error that may have had an external source.
35class recoverable_failure : public std::runtime_error {
36 origin::type _caused_by;
37public:
38 recoverable_failure(origin::type o, std::string const & s)
39 : std::runtime_error(s), _caused_by(o) {};
40 origin::type caused_by() const { return _caused_by; }
41 virtual ~recoverable_failure() throw() {}
42};
43
44// An error that indicates either an immediate logic bug or
45// a corrupt database. You don't want to catch these.
46class unrecoverable_failure : public std::logic_error {
47 origin::type _caused_by;
48public:
49 unrecoverable_failure(origin::type o, std::string const & s)
50 : std::logic_error(s), _caused_by(o) {};
51 origin::type caused_by() const { return _caused_by; }
52 virtual ~unrecoverable_failure() throw() {}
53};
54
55class MusingI;
56
57class format_base;
58struct plain_format;
59struct i18n_format;
60
61struct sanity {
62 sanity();
63 virtual ~sanity();
64 virtual void initialize(int, char **, char const *);
65 void dump_buffer();
66 int set_verbosity(int level, bool allow_debug_change);
67 int get_verbosity() const;
68 void set_debug(); // wrapper on set_verbosity
69 // This takes a bare std::string because we don't want to expose vocab.hh
70 // or paths.hh here.
71 void set_dump_path(std::string const & path);
72
73 // set out of band handler (e.g. for automate stdio)
74 void set_out_of_band_handler(void (*out_of_band_function)(char channel,
75 std::string const& text,
76 void *opaque)=NULL,
77 void *opaque_data=NULL);
78
79 // if such an out of band handler is set, this directly writes to it
80 bool maybe_write_to_out_of_band_handler(char channel, std::string const& str);
81
82 // A couple of places need to look at the debug flag to avoid doing
83 // expensive logging if it's off.
84 bool debug_p();
85
86 void log(plain_format const & fmt,
87 char const * file, int line);
88 void progress(i18n_format const & fmt,
89 char const * file, int line);
90 void warning(i18n_format const & fmt,
91 char const * file, int line);
92 NORETURN(void generic_failure(char const * expr,
93 origin::type caused_by,
94 i18n_format const & explain,
95 char const * file, int line));
96 NORETURN(void index_failure(char const * vec_expr,
97 char const * idx_expr,
98 unsigned long sz,
99 unsigned long idx,
100 char const * file, int line));
101 void gasp();
102 void push_musing(MusingI const *musing);
103 void pop_musing(MusingI const *musing);
104
105 // debugging aid, see DUMP() below
106 void print_var(std::string const & value,
107 char const * var,
108 char const * file,
109 int const line,
110 char const * func);
111
112private:
113 std::string do_format(format_base const & fmt,
114 char const * file, int line);
115 virtual void inform_log(std::string const &msg) = 0;
116 virtual void inform_message(std::string const &msg) = 0;
117 virtual void inform_warning(std::string const &msg) = 0;
118 virtual void inform_error(std::string const &msg) = 0;
119
120 struct impl;
121 impl * imp;
122};
123
124extern sanity & global_sanity;
125// we think this is less ugly than any available tricks with references
126extern std::string const * prog_name_ptr;
127#define prog_name (*prog_name_ptr)
128
129typedef std::runtime_error oops;
130
131// This hides boost::format from infecting every source file. Instead, we
132// implement a single very small formatter.
133
134class
135format_base
136{
137protected:
138 struct impl;
139 impl *pimpl;
140
141 format_base() : pimpl(NULL) {}
142 ~format_base();
143 format_base(format_base const & other);
144 format_base & operator=(format_base const & other);
145
146 explicit format_base(char const * pattern, bool use_locale);
147 explicit format_base(std::string const & pattern, bool use_locale);
148public:
149 // It is a lie that these are const; but then, everything about this
150 // class is a lie.
151 std::ostream & get_stream() const;
152 void flush_stream() const;
153 void put_and_flush_signed(s64 const & s) const;
154 void put_and_flush_signed(s32 const & s) const;
155 void put_and_flush_signed(s16 const & s) const;
156 void put_and_flush_signed(s8 const & s) const;
157 void put_and_flush_unsigned(u64 const & u) const;
158 void put_and_flush_unsigned(u32 const & u) const;
159 void put_and_flush_unsigned(u16 const & u) const;
160 void put_and_flush_unsigned(u8 const & u) const;
161 void put_and_flush_float(float const & f) const;
162 void put_and_flush_double(double const & d) const;
163
164 std::string str() const;
165};
166
167
168struct
169plain_format
170 : public format_base
171{
172 plain_format()
173 {}
174
175 explicit plain_format(char const * pattern)
176 : format_base(pattern, false)
177 {}
178
179 explicit plain_format(std::string const & pattern)
180 : format_base(pattern, false)
181 {}
182};
183
184template<typename T> inline plain_format const &
185operator %(plain_format const & f, T const & t)
186{
187 f.get_stream() << t;
188 f.flush_stream();
189 return f;
190}
191
192template<typename T> inline plain_format const &
193operator %(const plain_format & f, T & t)
194{
195 f.get_stream() << t;
196 f.flush_stream();
197 return f;
198}
199
200template<typename T> inline plain_format &
201operator %(plain_format & f, T const & t)
202{
203 f.get_stream() << t;
204 f.flush_stream();
205 return f;
206}
207
208template<typename T> inline plain_format &
209operator %(plain_format & f, T & t)
210{
211 f.get_stream() << t;
212 f.flush_stream();
213 return f;
214}
215
216#define SPECIALIZED_OP(format_ty, specialize_arg_ty, overload_arg_ty, s) \
217template <> inline format_ty & \
218operator %<specialize_arg_ty>(format_ty & f, overload_arg_ty & a) \
219{ \
220 f.put_and_flush_ ## s (a); \
221 return f; \
222}
223
224#define ALL_CONST_VARIANTS(fmt_ty, arg_ty, stem) \
225SPECIALIZED_OP( fmt_ty, arg_ty, arg_ty, stem) \
226SPECIALIZED_OP( fmt_ty, arg_ty, const arg_ty, stem) \
227SPECIALIZED_OP(const fmt_ty, arg_ty, arg_ty, stem) \
228SPECIALIZED_OP(const fmt_ty, arg_ty, const arg_ty, stem)
229
230ALL_CONST_VARIANTS(plain_format, s64, signed)
231ALL_CONST_VARIANTS(plain_format, s32, signed)
232ALL_CONST_VARIANTS(plain_format, s16, signed)
233ALL_CONST_VARIANTS(plain_format, s8, signed)
234
235ALL_CONST_VARIANTS(plain_format, u64, unsigned)
236ALL_CONST_VARIANTS(plain_format, u32, unsigned)
237ALL_CONST_VARIANTS(plain_format, u16, unsigned)
238ALL_CONST_VARIANTS(plain_format, u8, unsigned)
239
240ALL_CONST_VARIANTS(plain_format, float, float)
241ALL_CONST_VARIANTS(plain_format, double, double)
242
243
244struct
245i18n_format
246 : public format_base
247{
248 i18n_format()
249 {}
250
251 explicit i18n_format(const char * localized_pattern)
252 : format_base(localized_pattern, true)
253 {}
254
255 explicit i18n_format(std::string const & localized_pattern)
256 : format_base(localized_pattern, true)
257 {}
258};
259
260template<typename T> inline i18n_format const &
261operator %(i18n_format const & f, T const & t)
262{
263 f.get_stream() << t;
264 f.flush_stream();
265 return f;
266}
267
268template<typename T> inline i18n_format const &
269operator %(i18n_format const & f, T & t)
270{
271 f.get_stream() << t;
272 f.flush_stream();
273 return f;
274}
275
276template<typename T> inline i18n_format &
277operator %(i18n_format & f, T const & t)
278{
279 f.get_stream() << t;
280 f.flush_stream();
281 return f;
282}
283
284template<typename T> inline i18n_format &
285operator %(i18n_format & f, T & t)
286{
287 f.get_stream() << t;
288 f.flush_stream();
289 return f;
290}
291
292ALL_CONST_VARIANTS(i18n_format, s64, signed)
293ALL_CONST_VARIANTS(i18n_format, s32, signed)
294ALL_CONST_VARIANTS(i18n_format, s16, signed)
295ALL_CONST_VARIANTS(i18n_format, s8, signed)
296
297ALL_CONST_VARIANTS(i18n_format, u64, unsigned)
298ALL_CONST_VARIANTS(i18n_format, u32, unsigned)
299ALL_CONST_VARIANTS(i18n_format, u16, unsigned)
300ALL_CONST_VARIANTS(i18n_format, u8, unsigned)
301
302ALL_CONST_VARIANTS(i18n_format, float, float)
303ALL_CONST_VARIANTS(i18n_format, double, double)
304
305#undef ALL_CONST_VARIANTS
306#undef SPECIALIZED_OP
307
308std::ostream & operator<<(std::ostream & os, format_base const & fmt);
309
310// F is for when you want to build a boost formatter for display
311i18n_format F(const char * str);
312
313// FP is for when you want to build a boost formatter for displaying a plural
314i18n_format FP(const char * str1, const char * strn, unsigned long count);
315
316// FL is for when you want to build a boost formatter for the developers -- it
317// is not gettextified. Think of the L as "literal" or "log".
318plain_format FL(const char * str);
319
320// L is for logging, you can log all you want
321#define L(fmt) \
322do { \
323 if (global_sanity.debug_p()) \
324 global_sanity.log(fmt, __FILE__, __LINE__); \
325} while (0)
326
327// P is for progress, log only stuff which the user might
328// normally like to see some indication of progress of
329#define P(fmt) \
330do { \
331 if (global_sanity.get_verbosity() > -1) \
332 global_sanity.progress(fmt, __FILE__, __LINE__); \
333} while (0)
334
335// W is for warnings, which are handled like progress only
336// they are only issued once and are prefixed with "warning: "
337#define W(fmt) \
338do { \
339 if (global_sanity.get_verbosity() > -2) \
340 global_sanity.warning(fmt, __FILE__, __LINE__); \
341} while (0)
342
343
344// invariants and assertions
345
346#ifdef __GNUC__
347#define LIKELY(zz) (__builtin_expect((zz), 1))
348#define UNLIKELY(zz) (__builtin_expect((zz), 0))
349#else
350#define LIKELY(zz) (zz)
351#define UNLIKELY(zz) (zz)
352#endif
353
354struct bad_decode {
355 bad_decode(i18n_format const & fmt) : what(fmt.str()) {}
356 std::string what;
357};
358
359// I is for invariants that "should" always be true
360// (if they are wrong, there is a *bug*)
361
362#define FILE_LINE_INNER(line) __FILE__ ":" #line
363#define FILE_LINE_MIDDLE(line) FILE_LINE_INNER(line)
364#define FILE_LINE FILE_LINE_MIDDLE(__LINE__)
365
366#define I(e) \
367 do { \
368 if (UNLIKELY(!(e))) \
369 { \
370 global_sanity.generic_failure("I("#e")", origin::internal, \
371 F("%s") % FILE_LINE": I("#e")", \
372 __FILE__, __LINE__); \
373 } \
374 } while (0)
375
376// E is for errors; they are normal (i.e., not a bug), but not necessarily
377// attributable to user naughtiness
378#define E(e, whence, explain) \
379 do { \
380 if (UNLIKELY(!(e))) \
381 { \
382 global_sanity.generic_failure("E("#e")", (whence), \
383 (explain), \
384 __FILE__, __LINE__); \
385 } \
386 } while (0)
387
388// Last gasp dumps
389
390class MusingI
391{
392public:
393 MusingI() { global_sanity.push_musing(this); }
394 virtual ~MusingI() { global_sanity.pop_musing(this); }
395 virtual void gasp(std::string & out) const = 0;
396};
397
398
399class MusingBase
400{
401 char const * name;
402 char const * file;
403 char const * func;
404 int line;
405
406protected:
407 MusingBase(char const * name, char const * file, int line, char const * func)
408 : name(name), file(file), func(func), line(line) {}
409
410 void gasp_head(std::string & out) const;
411 void gasp_body(const std::string & objstr, std::string & out) const;
412};
413
414
415// remove_reference is a workaround for C++ defect #106.
416template <typename T>
417struct remove_reference {
418 typedef T type;
419};
420
421template <typename T>
422struct remove_reference <T &> {
423 typedef typename remove_reference<T>::type type;
424};
425
426
427template <typename T>
428class Musing : public MusingI, private MusingBase
429{
430public:
431 Musing(typename remove_reference<T>::type const & obj, char const * name, char const * file, int line, char const * func)
432 : MusingBase(name, file, line, func), obj(obj) {}
433 virtual void gasp(std::string & out) const;
434private:
435 typename remove_reference<T>::type const & obj;
436};
437
438// The header line must be printed into the "out" string before
439// dump() is called.
440// This is so that even if the call to dump() throws an error,
441// the header line ("----- begin ...") will be printed.
442// If these calls are collapsed into one, then *no* identifying
443// information will be printed in the case of dump() throwing.
444// Having the header line without the body is still useful, as it
445// provides some semblance of a backtrace.
446template <typename T> void
447Musing<T>::gasp(std::string & out) const
448{
449 std::string tmp;
450 MusingBase::gasp_head(out);
451 dump(obj, tmp);
452
453 MusingBase::gasp_body(tmp, out);
454}
455
456// Yes, this is insane. No, it doesn't work if you do something more sane.
457// ## explicitly skips macro argument expansion on the things passed to it.
458// Therefore, if we simply did foo ## __LINE__, we would get foo__LINE__ in
459// the output. In fact, even if we did real_M(obj, __LINE__), we would get
460// foo__LINE__ in the output. (## substitutes arguments, but does not expand
461// them.) However, while fake_M does nothing directly, it doesn't pass its
462// line argument to ##; therefore, its line argument is fully expanded before
463// being passed to real_M.
464#ifdef HAVE_TYPEOF
465// even worse, this is g++ only!
466#define real_M(obj, line) Musing<typeof(obj)> this_is_a_musing_fnord_object_ ## line (obj, #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
467#define fake_M(obj, line) real_M(obj, line)
468#define MM(obj) fake_M(obj, __LINE__)
469
470// This is to be used for objects that should stay on the musings list
471// even after the caller returns. Note that all PERM_MM objects must
472// be before all MM objects on the musings list, or you will get an
473// invariant failure. (In other words, don't use PERM_MM unless you
474// are sanity::initialize.)
475#define PERM_MM(obj) \
476 new Musing<typeof(obj)>(*(new remove_reference<typeof(obj)>::type(obj)), \
477 #obj, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
478
479#else
480#define MM(obj) /* */
481#define PERM_MM(obj) /* */
482#endif
483
484// debugging utility to dump out vars like MM but without requiring a crash
485
486template <typename T> void
487dump(T const & t, char const *var,
488 char const * file, int const line, char const * func)
489{
490 std::string value;
491 dump(t, value);
492 global_sanity.print_var(value, var, file, line, func);
493};
494
495#define DUMP(foo) dump(foo, #foo, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION)
496
497#endif // __SANITY_HH__
498
499// Local Variables:
500// mode: C++
501// fill-column: 76
502// c-file-style: "gnu"
503// indent-tabs-mode: nil
504// End:
505// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status