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