monotone

monotone Mtn Source Tree

Root/sanity.cc

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#include "base.hh"
11#include <algorithm>
12#include <iterator>
13#include <iostream>
14#include <fstream>
15#include "vector.hh"
16#include <sstream>
17
18#include <boost/format.hpp>
19#include <boost/circular_buffer.hpp>
20
21#include "lexical_cast.hh"
22#include "constants.hh"
23#include "platform.hh"
24#include "sanity.hh"
25#include "simplestring_xform.hh"
26
27using std::exception;
28using std::locale;
29using std::logic_error;
30using std::ofstream;
31using std::ostream;
32using std::ostream_iterator;
33using std::ostringstream;
34using std::string;
35using std::vector;
36
37using boost::format;
38
39struct sanity::impl
40{
41 bool debug;
42 bool quiet;
43 bool reallyquiet;
44 boost::circular_buffer<char> logbuf;
45 std::string filename;
46 std::string gasp_dump;
47 bool already_dumping;
48 std::vector<MusingI const *> musings;
49
50 impl() :
51 debug(false), quiet(false), reallyquiet(false), logbuf(0xffff),
52 already_dumping(false)
53 {}
54};
55
56// debugging / logging system
57
58sanity::~sanity()
59{
60 if (imp)
61 delete imp;
62}
63
64void
65sanity::initialize(int argc, char ** argv, char const * lc_all)
66{
67 imp = new impl;
68
69 // set up some marked strings, so even if our logbuf overflows, we'll get
70 // this data in a crash. This (and subclass overrides) are probably the
71 // only place PERM_MM should ever be used.
72
73 string system_flavour;
74 get_system_flavour(system_flavour);
75 PERM_MM(system_flavour);
76 L(FL("started up on %s") % system_flavour);
77
78 string cmdline_string;
79 {
80 ostringstream cmdline_ss;
81 for (int i = 0; i < argc; ++i)
82 {
83 if (i)
84 cmdline_ss << ", ";
85 cmdline_ss << '\'' << argv[i] << '\'';
86 }
87 cmdline_string = cmdline_ss.str();
88 }
89 PERM_MM(cmdline_string);
90 L(FL("command line: %s") % cmdline_string);
91
92 if (!lc_all)
93 lc_all = "n/a";
94 PERM_MM(string(lc_all));
95 L(FL("set locale: LC_ALL=%s") % lc_all);
96}
97
98void
99sanity::dump_buffer()
100{
101 I(imp);
102 if (!imp->filename.empty())
103 {
104 ofstream out(imp->filename.c_str());
105 if (out)
106 {
107 copy(imp->logbuf.begin(), imp->logbuf.end(),
108 ostream_iterator<char>(out));
109 copy(imp->gasp_dump.begin(), imp->gasp_dump.end(),
110 ostream_iterator<char>(out));
111 inform_message((FL("wrote debugging log to %s\n"
112 "if reporting a bug, please include this file")
113 % imp->filename).str());
114 }
115 else
116 inform_message((FL("failed to write debugging log to %s")
117 % imp->filename).str());
118 }
119 else
120 inform_message("discarding debug log, because I have nowhere to write it\n"
121 "(maybe you want --debug or --dump?)");
122}
123
124void
125sanity::set_debug()
126{
127 I(imp);
128 imp->quiet = false;
129 imp->reallyquiet = false;
130 imp->debug = true;
131
132 // it is possible that some pre-setting-of-debug data
133 // accumulated in the log buffer (during earlier option processing)
134 // so we will dump it now
135 ostringstream oss;
136 vector<string> lines;
137 copy(imp->logbuf.begin(), imp->logbuf.end(), ostream_iterator<char>(oss));
138 split_into_lines(oss.str(), lines);
139 for (vector<string>::const_iterator i = lines.begin(); i != lines.end(); ++i)
140 inform_log((*i) + "\n");
141}
142
143void
144sanity::set_quiet()
145{
146 I(imp);
147 imp->debug = false;
148 imp->quiet = true;
149 imp->reallyquiet = false;
150}
151
152void
153sanity::set_reallyquiet()
154{
155 I(imp);
156 imp->debug = false;
157 imp->quiet = true;
158 imp->reallyquiet = true;
159}
160
161void
162sanity::set_dump_path(std::string const & path)
163{
164 I(imp);
165 if (imp->filename.empty())
166 {
167 L(FL("setting dump path to %s") % path);
168 imp->filename = path;
169 }
170}
171
172string
173sanity::do_format(format_base const & fmt, char const * file, int line)
174{
175 try
176 {
177 return fmt.str();
178 }
179 catch (exception & e)
180 {
181 inform_error((F("fatal: formatter failed on %s:%d: %s")
182 % file
183 % line
184 % e.what()).str());
185 throw;
186 }
187}
188
189bool
190sanity::debug_p()
191{
192 if (!imp)
193 throw std::logic_error("sanity::debug_p called "
194 "before sanity::initialize");
195 return imp->debug;
196}
197
198bool
199sanity::quiet_p()
200{
201 if (!imp)
202 throw std::logic_error("sanity::quiet_p called "
203 "before sanity::initialize");
204 return imp->quiet;
205}
206
207bool
208sanity::reallyquiet_p()
209{
210 if (!imp)
211 throw std::logic_error("sanity::reallyquiet_p called "
212 "before sanity::initialize");
213 return imp->reallyquiet;
214}
215
216void
217sanity::log(plain_format const & fmt,
218 char const * file, int line)
219{
220 string str = do_format(fmt, file, line);
221
222 if (str.size() > constants::log_line_sz)
223 {
224 str.resize(constants::log_line_sz);
225 if (str.at(str.size() - 1) != '\n')
226 str.at(str.size() - 1) = '\n';
227 }
228 copy(str.begin(), str.end(), back_inserter(imp->logbuf));
229 if (str[str.size() - 1] != '\n')
230 imp->logbuf.push_back('\n');
231
232 inform_log(str);
233}
234
235void
236sanity::progress(i18n_format const & i18nfmt,
237 char const * file, int line)
238{
239 string str = do_format(i18nfmt, file, line);
240
241 if (str.size() > constants::log_line_sz)
242 {
243 str.resize(constants::log_line_sz);
244 if (str.at(str.size() - 1) != '\n')
245 str.at(str.size() - 1) = '\n';
246 }
247 copy(str.begin(), str.end(), back_inserter(imp->logbuf));
248 if (str[str.size() - 1] != '\n')
249 imp->logbuf.push_back('\n');
250
251 inform_message(str);
252}
253
254void
255sanity::warning(i18n_format const & i18nfmt,
256 char const * file, int line)
257{
258 string str = do_format(i18nfmt, file, line);
259
260 if (str.size() > constants::log_line_sz)
261 {
262 str.resize(constants::log_line_sz);
263 if (str.at(str.size() - 1) != '\n')
264 str.at(str.size() - 1) = '\n';
265 }
266 string str2 = "warning: " + str;
267 copy(str2.begin(), str2.end(), back_inserter(imp->logbuf));
268 if (str[str.size() - 1] != '\n')
269 imp->logbuf.push_back('\n');
270
271 inform_warning(str);
272}
273
274void
275sanity::naughty_failure(char const * expr, i18n_format const & explain,
276 char const * file, int line)
277{
278 string message;
279 if (!imp)
280 throw std::logic_error("sanity::naughty_failure occured "
281 "before sanity::initialize");
282 if (imp->debug)
283 log(FL("%s:%d: usage constraint '%s' violated") % file % line % expr,
284 file, line);
285 prefix_lines_with(_("misuse: "), do_format(explain, file, line), message);
286 gasp();
287 throw informative_failure(message);
288}
289
290void
291sanity::error_failure(char const * expr, i18n_format const & explain,
292 char const * file, int line)
293{
294 string message;
295 if (!imp)
296 throw std::logic_error("sanity::error_failure occured "
297 "before sanity::initialize");
298 if (imp->debug)
299 log(FL("%s:%d: detected error '%s' violated") % file % line % expr,
300 file, line);
301 gasp();
302 prefix_lines_with(_("error: "), do_format(explain, file, line), message);
303 throw informative_failure(message);
304}
305
306void
307sanity::invariant_failure(char const * expr, char const * file, int line)
308{
309 char const * pattern = N_("%s:%d: invariant '%s' violated");
310 if (!imp)
311 throw std::logic_error("sanity::invariant_failure occured "
312 "before sanity::initialize");
313 if (imp->debug)
314 log(FL(pattern) % file % line % expr, file, line);
315 gasp();
316 throw logic_error((F(pattern) % file % line % expr).str());
317}
318
319void
320sanity::index_failure(char const * vec_expr,
321 char const * idx_expr,
322 unsigned long sz,
323 unsigned long idx,
324 char const * file, int line)
325{
326 char const * pattern
327 = N_("%s:%d: index '%s' = %d overflowed vector '%s' with size %d");
328 if (!imp)
329 throw std::logic_error("sanity::index_failure occured "
330 "before sanity::initialize");
331 if (imp->debug)
332 log(FL(pattern) % file % line % idx_expr % idx % vec_expr % sz,
333 file, line);
334 gasp();
335 throw logic_error((F(pattern)
336 % file % line % idx_expr % idx % vec_expr % sz).str());
337}
338
339// Last gasp dumps
340
341void
342sanity::push_musing(MusingI const *musing)
343{
344 I(imp);
345 if (!imp->already_dumping)
346 imp->musings.push_back(musing);
347}
348
349void
350sanity::pop_musing(MusingI const *musing)
351{
352 I(imp);
353 if (!imp->already_dumping)
354 {
355 I(imp->musings.back() == musing);
356 imp->musings.pop_back();
357 }
358}
359
360
361void
362sanity::gasp()
363{
364 if (!imp)
365 return;
366 if (imp->already_dumping)
367 {
368 L(FL("ignoring request to give last gasp; already in process of dumping"));
369 return;
370 }
371 imp->already_dumping = true;
372 L(FL("saving current work set: %i items") % imp->musings.size());
373 ostringstream out;
374 out << (F("Current work set: %i items") % imp->musings.size())
375 << '\n'; // final newline is kept out of the translation
376 for (vector<MusingI const *>::const_iterator
377 i = imp->musings.begin(); i != imp->musings.end(); ++i)
378 {
379 string tmp;
380 try
381 {
382 (*i)->gasp(tmp);
383 out << tmp;
384 }
385 catch (logic_error)
386 {
387 out << tmp;
388 out << "<caught logic_error>\n";
389 L(FL("ignoring error trigged by saving work set to debug log"));
390 }
391 catch (informative_failure)
392 {
393 out << tmp;
394 out << "<caught informative_failure>\n";
395 L(FL("ignoring error trigged by saving work set to debug log"));
396 }
397 }
398 imp->gasp_dump = out.str();
399 L(FL("finished saving work set"));
400 if (imp->debug)
401 {
402 inform_log("contents of work set:");
403 inform_log(imp->gasp_dump);
404 }
405 imp->already_dumping = false;
406}
407
408template <> void
409dump(string const & obj, string & out)
410{
411 out = obj;
412}
413
414void
415print_var(std::string const & value, char const * var,
416 char const * file, int const line, char const * func)
417{
418 std::cout << (FL("----- begin '%s' (in %s, at %s:%d)\n")
419 % var % func % file % line)
420 << value
421 << (FL("\n----- end '%s' (in %s, at %s:%d)\n\n")
422 % var % func % file % line);
423}
424
425void MusingBase::gasp_head(string & out) const
426{
427 out = (boost::format("----- begin '%s' (in %s, at %s:%d)\n")
428 % name % func % file % line
429 ).str();
430}
431
432void MusingBase::gasp_body(const string & objstr, string & out) const
433{
434 out += (boost::format("%s%s"
435 "----- end '%s' (in %s, at %s:%d)\n")
436 % objstr
437 % (*(objstr.end() - 1) == '\n' ? "" : "\n")
438 % name % func % file % line
439 ).str();
440}
441
442const locale &
443get_user_locale()
444{
445 // this is awkward because if LC_CTYPE is set to something the
446 // runtime doesn't know about, it will fail. in that case,
447 // the default will have to do.
448 static bool init = false;
449 static locale user_locale;
450 if (!init)
451 {
452 init = true;
453 try
454 {
455 user_locale = locale("");
456 }
457 catch( ... )
458 {}
459 }
460 return user_locale;
461}
462
463struct
464format_base::impl
465{
466 format fmt;
467 ostringstream oss;
468
469 impl(impl const & other) : fmt(other.fmt)
470 {}
471
472 impl & operator=(impl const & other)
473 {
474 if (&other != this)
475 {
476 fmt = other.fmt;
477 oss.str(string());
478 }
479 return *this;
480 }
481
482 impl(char const * pattern)
483 : fmt(pattern)
484 {}
485 impl(string const & pattern)
486 : fmt(pattern)
487 {}
488 impl(char const * pattern, locale const & loc)
489 : fmt(pattern, loc)
490 {}
491 impl(string const & pattern, locale const & loc)
492 : fmt(pattern, loc)
493 {}
494};
495
496format_base::format_base(format_base const & other)
497 : pimpl(other.pimpl ? new impl(*(other.pimpl)) : NULL)
498{
499
500}
501
502format_base::~format_base()
503{
504 delete pimpl;
505}
506
507format_base &
508format_base::operator=(format_base const & other)
509{
510 if (&other != this)
511 {
512 impl * tmp = NULL;
513
514 try
515 {
516 if (other.pimpl)
517 tmp = new impl(*(other.pimpl));
518 }
519 catch (...)
520 {
521 if (tmp)
522 delete tmp;
523 }
524
525 if (pimpl)
526 delete pimpl;
527
528 pimpl = tmp;
529 }
530 return *this;
531}
532
533format_base::format_base(char const * pattern, bool use_locale)
534 : pimpl(use_locale ? new impl(pattern, get_user_locale())
535 : new impl(pattern))
536{}
537
538format_base::format_base(std::string const & pattern, bool use_locale)
539 : pimpl(use_locale ? new impl(pattern, get_user_locale())
540 : new impl(pattern))
541{}
542
543ostream &
544format_base::get_stream() const
545{
546 return pimpl->oss;
547}
548
549void
550format_base::flush_stream() const
551{
552 pimpl->fmt % pimpl->oss.str();
553 pimpl->oss.str(string());
554}
555
556void format_base::put_and_flush_signed(s64 const & s) const { pimpl->fmt % s; }
557void format_base::put_and_flush_signed(s32 const & s) const { pimpl->fmt % s; }
558void format_base::put_and_flush_signed(s16 const & s) const { pimpl->fmt % s; }
559void format_base::put_and_flush_signed(s8 const & s) const { pimpl->fmt % s; }
560
561void format_base::put_and_flush_unsigned(u64 const & u) const { pimpl->fmt % u; }
562void format_base::put_and_flush_unsigned(u32 const & u) const { pimpl->fmt % u; }
563void format_base::put_and_flush_unsigned(u16 const & u) const { pimpl->fmt % u; }
564void format_base::put_and_flush_unsigned(u8 const & u) const { pimpl->fmt % u; }
565
566void format_base::put_and_flush_float(float const & f) const { pimpl->fmt % f; }
567void format_base::put_and_flush_double(double const & d) const { pimpl->fmt % d; }
568
569std::string
570format_base::str() const
571{
572 return pimpl->fmt.str();
573}
574
575ostream &
576operator<<(ostream & os, format_base const & fmt)
577{
578 return os << fmt.str();
579}
580
581i18n_format F(const char * str)
582{
583 return i18n_format(gettext(str));
584}
585
586
587i18n_format FP(const char * str1, const char * strn, unsigned long count)
588{
589 return i18n_format(ngettext(str1, strn, count));
590}
591
592plain_format FL(const char * str)
593{
594 return plain_format(str);
595}
596
597// Local Variables:
598// mode: C++
599// fill-column: 76
600// c-file-style: "gnu"
601// indent-tabs-mode: nil
602// End:
603// 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