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 I(imp);
193 return imp->debug;
194}
195
196bool
197sanity::quiet_p()
198{
199 I(imp);
200 return imp->quiet;
201}
202
203// These functions can be called before sanity::initialize() if there
204// is a bug, and therefore must not use I() if imp is unavailable, as
205// that will cause infinite recursion (invariant_failure calls log).
206
207void
208sanity::log(plain_format const & fmt,
209 char const * file, int line)
210{
211 if (!imp)
212 throw std::logic_error("sanity::log called before sanity::initialize");
213
214 string str = do_format(fmt, file, line);
215
216 if (str.size() > constants::log_line_sz)
217 {
218 str.resize(constants::log_line_sz);
219 if (str.at(str.size() - 1) != '\n')
220 str.at(str.size() - 1) = '\n';
221 }
222 copy(str.begin(), str.end(), back_inserter(imp->logbuf));
223 if (str[str.size() - 1] != '\n')
224 imp->logbuf.push_back('\n');
225 if (imp->debug)
226 inform_log(str);
227}
228
229void
230sanity::progress(i18n_format const & i18nfmt,
231 char const * file, int line)
232{
233 if (!imp)
234 throw std::logic_error("sanity::progress called before sanity::initialize");
235
236 string str = do_format(i18nfmt, file, line);
237
238 if (str.size() > constants::log_line_sz)
239 {
240 str.resize(constants::log_line_sz);
241 if (str.at(str.size() - 1) != '\n')
242 str.at(str.size() - 1) = '\n';
243 }
244 copy(str.begin(), str.end(), back_inserter(imp->logbuf));
245 if (str[str.size() - 1] != '\n')
246 imp->logbuf.push_back('\n');
247 if (! imp->quiet)
248 inform_message(str);
249}
250
251void
252sanity::warning(i18n_format const & i18nfmt,
253 char const * file, int line)
254{
255 if (!imp)
256 throw std::logic_error("sanity::warning called before sanity::initialize");
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 if (! imp->reallyquiet)
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 log(FL("%s:%d: usage constraint '%s' violated") % file % line % expr,
280 file, line);
281 prefix_lines_with(_("misuse: "), do_format(explain, file, line), message);
282 gasp();
283 throw informative_failure(message);
284}
285
286void
287sanity::error_failure(char const * expr, i18n_format const & explain,
288 char const * file, int line)
289{
290 string message;
291 log(FL("%s:%d: detected error '%s' violated") % file % line % expr,
292 file, line);
293 gasp();
294 prefix_lines_with(_("error: "), do_format(explain, file, line), message);
295 throw informative_failure(message);
296}
297
298void
299sanity::invariant_failure(char const * expr, char const * file, int line)
300{
301 char const * pattern = N_("%s:%d: invariant '%s' violated");
302 log(FL(pattern) % file % line % expr, file, line);
303 gasp();
304 throw logic_error((F(pattern) % file % line % expr).str());
305}
306
307void
308sanity::index_failure(char const * vec_expr,
309 char const * idx_expr,
310 unsigned long sz,
311 unsigned long idx,
312 char const * file, int line)
313{
314 char const * pattern
315 = N_("%s:%d: index '%s' = %d overflowed vector '%s' with size %d");
316 log(FL(pattern) % file % line % idx_expr % idx % vec_expr % sz,
317 file, line);
318 gasp();
319 throw logic_error((F(pattern)
320 % file % line % idx_expr % idx % vec_expr % sz).str());
321}
322
323// Last gasp dumps
324
325void
326sanity::push_musing(MusingI const *musing)
327{
328 I(imp);
329 if (!imp->already_dumping)
330 imp->musings.push_back(musing);
331}
332
333void
334sanity::pop_musing(MusingI const *musing)
335{
336 I(imp);
337 if (!imp->already_dumping)
338 {
339 I(imp->musings.back() == musing);
340 imp->musings.pop_back();
341 }
342}
343
344
345void
346sanity::gasp()
347{
348 if (!imp)
349 return;
350 if (imp->already_dumping)
351 {
352 L(FL("ignoring request to give last gasp; already in process of dumping"));
353 return;
354 }
355 imp->already_dumping = true;
356 L(FL("saving current work set: %i items") % imp->musings.size());
357 ostringstream out;
358 out << (F("Current work set: %i items") % imp->musings.size())
359 << '\n'; // final newline is kept out of the translation
360 for (vector<MusingI const *>::const_iterator
361 i = imp->musings.begin(); i != imp->musings.end(); ++i)
362 {
363 string tmp;
364 try
365 {
366 (*i)->gasp(tmp);
367 out << tmp;
368 }
369 catch (logic_error)
370 {
371 out << tmp;
372 out << "<caught logic_error>\n";
373 L(FL("ignoring error trigged by saving work set to debug log"));
374 }
375 catch (informative_failure)
376 {
377 out << tmp;
378 out << "<caught informative_failure>\n";
379 L(FL("ignoring error trigged by saving work set to debug log"));
380 }
381 }
382 imp->gasp_dump = out.str();
383 L(FL("finished saving work set"));
384 if (imp->debug)
385 {
386 inform_log("contents of work set:");
387 inform_log(imp->gasp_dump);
388 }
389 imp->already_dumping = false;
390}
391
392template <> void
393dump(string const & obj, string & out)
394{
395 out = obj;
396}
397
398void
399print_var(std::string const & value, char const * var,
400 char const * file, int const line, char const * func)
401{
402 std::cout << (FL("----- begin '%s' (in %s, at %s:%d)\n")
403 % var % func % file % line)
404 << value
405 << (FL("\n----- end '%s' (in %s, at %s:%d)\n\n")
406 % var % func % file % line);
407}
408
409void MusingBase::gasp_head(string & out) const
410{
411 out = (boost::format("----- begin '%s' (in %s, at %s:%d)\n")
412 % name % func % file % line
413 ).str();
414}
415
416void MusingBase::gasp_body(const string & objstr, string & out) const
417{
418 out += (boost::format("%s%s"
419 "----- end '%s' (in %s, at %s:%d)\n")
420 % objstr
421 % (*(objstr.end() - 1) == '\n' ? "" : "\n")
422 % name % func % file % line
423 ).str();
424}
425
426const locale &
427get_user_locale()
428{
429 // this is awkward because if LC_CTYPE is set to something the
430 // runtime doesn't know about, it will fail. in that case,
431 // the default will have to do.
432 static bool init = false;
433 static locale user_locale;
434 if (!init)
435 {
436 init = true;
437 try
438 {
439 user_locale = locale("");
440 }
441 catch( ... )
442 {}
443 }
444 return user_locale;
445}
446
447struct
448format_base::impl
449{
450 format fmt;
451 ostringstream oss;
452
453 impl(impl const & other) : fmt(other.fmt)
454 {}
455
456 impl & operator=(impl const & other)
457 {
458 if (&other != this)
459 {
460 fmt = other.fmt;
461 oss.str(string());
462 }
463 return *this;
464 }
465
466 impl(char const * pattern)
467 : fmt(pattern)
468 {}
469 impl(string const & pattern)
470 : fmt(pattern)
471 {}
472 impl(char const * pattern, locale const & loc)
473 : fmt(pattern, loc)
474 {}
475 impl(string const & pattern, locale const & loc)
476 : fmt(pattern, loc)
477 {}
478};
479
480format_base::format_base(format_base const & other)
481 : pimpl(other.pimpl ? new impl(*(other.pimpl)) : NULL)
482{
483
484}
485
486format_base::~format_base()
487{
488 delete pimpl;
489}
490
491format_base &
492format_base::operator=(format_base const & other)
493{
494 if (&other != this)
495 {
496 impl * tmp = NULL;
497
498 try
499 {
500 if (other.pimpl)
501 tmp = new impl(*(other.pimpl));
502 }
503 catch (...)
504 {
505 if (tmp)
506 delete tmp;
507 }
508
509 if (pimpl)
510 delete pimpl;
511
512 pimpl = tmp;
513 }
514 return *this;
515}
516
517format_base::format_base(char const * pattern, bool use_locale)
518 : pimpl(use_locale ? new impl(pattern, get_user_locale())
519 : new impl(pattern))
520{}
521
522format_base::format_base(std::string const & pattern, bool use_locale)
523 : pimpl(use_locale ? new impl(pattern, get_user_locale())
524 : new impl(pattern))
525{}
526
527ostream &
528format_base::get_stream() const
529{
530 return pimpl->oss;
531}
532
533void
534format_base::flush_stream() const
535{
536 pimpl->fmt % pimpl->oss.str();
537 pimpl->oss.str(string());
538}
539
540void format_base::put_and_flush_signed(s64 const & s) const { pimpl->fmt % s; }
541void format_base::put_and_flush_signed(s32 const & s) const { pimpl->fmt % s; }
542void format_base::put_and_flush_signed(s16 const & s) const { pimpl->fmt % s; }
543void format_base::put_and_flush_signed(s8 const & s) const { pimpl->fmt % s; }
544
545void format_base::put_and_flush_unsigned(u64 const & u) const { pimpl->fmt % u; }
546void format_base::put_and_flush_unsigned(u32 const & u) const { pimpl->fmt % u; }
547void format_base::put_and_flush_unsigned(u16 const & u) const { pimpl->fmt % u; }
548void format_base::put_and_flush_unsigned(u8 const & u) const { pimpl->fmt % u; }
549
550void format_base::put_and_flush_float(float const & f) const { pimpl->fmt % f; }
551void format_base::put_and_flush_double(double const & d) const { pimpl->fmt % d; }
552
553std::string
554format_base::str() const
555{
556 return pimpl->fmt.str();
557}
558
559ostream &
560operator<<(ostream & os, format_base const & fmt)
561{
562 return os << fmt.str();
563}
564
565i18n_format F(const char * str)
566{
567 return i18n_format(gettext(str));
568}
569
570
571i18n_format FP(const char * str1, const char * strn, unsigned long count)
572{
573 return i18n_format(ngettext(str1, strn, count));
574}
575
576plain_format FL(const char * str)
577{
578 return plain_format(str);
579}
580
581// Local Variables:
582// mode: C++
583// fill-column: 76
584// c-file-style: "gnu"
585// indent-tabs-mode: nil
586// End:
587// 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