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>
16#include <sstream>
17
18#include <boost/format.hpp>
19#include "lexical_cast.hh"
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(char const * expr, i18n_format const & explain,
214 char const * file, int line)
215{
216 string message;
217 log(FL("%s:%d: usage constraint '%s' violated") % file % line % expr,
218 file, line);
219 prefix_lines_with(_("misuse: "), do_format(explain, file, line), message);
220 gasp();
221 throw informative_failure(message);
222}
223
224void
225sanity::error_failure(char const * expr, i18n_format const & explain,
226 char const * file, int line)
227{
228 string message;
229 log(FL("%s:%d: detected error '%s' violated") % file % line % expr,
230 file, line);
231 gasp();
232 prefix_lines_with(_("error: "), do_format(explain, file, line), message);
233 throw informative_failure(message);
234}
235
236void
237sanity::invariant_failure(char const * expr, char const * file, int line)
238{
239 char const * pattern = N_("%s:%d: invariant '%s' violated");
240 log(FL(pattern) % file % line % expr, file, line);
241 gasp();
242 throw logic_error((F(pattern) % file % line % expr).str());
243}
244
245void
246sanity::index_failure(char const * vec_expr,
247 char const * idx_expr,
248 unsigned long sz,
249 unsigned long idx,
250 char const * file, int line)
251{
252 char const * pattern
253 = N_("%s:%d: index '%s' = %d overflowed vector '%s' with size %d");
254 log(FL(pattern) % file % line % idx_expr % idx % vec_expr % sz,
255 file, line);
256 gasp();
257 throw logic_error((F(pattern)
258 % file % line % idx_expr % idx % vec_expr % sz).str());
259}
260
261// Last gasp dumps
262
263void
264sanity::gasp()
265{
266 if (already_dumping)
267 {
268 L(FL("ignoring request to give last gasp; already in process of dumping"));
269 return;
270 }
271 already_dumping = true;
272 L(FL("saving current work set: %i items") % musings.size());
273 ostringstream out;
274 out << (F("Current work set: %i items") % musings.size())
275 << '\n'; // final newline is kept out of the translation
276 for (vector<MusingI const *>::const_iterator
277 i = musings.begin(); i != musings.end(); ++i)
278 {
279 string tmp;
280 try
281 {
282 (*i)->gasp(tmp);
283 out << tmp;
284 }
285 catch (logic_error)
286 {
287 out << tmp;
288 out << "<caught logic_error>\n";
289 L(FL("ignoring error trigged by saving work set to debug log"));
290 }
291 catch (informative_failure)
292 {
293 out << tmp;
294 out << "<caught informative_failure>\n";
295 L(FL("ignoring error trigged by saving work set to debug log"));
296 }
297 }
298 gasp_dump = out.str();
299 L(FL("finished saving work set"));
300 if (debug)
301 {
302 inform_log("contents of work set:");
303 inform_log(gasp_dump);
304 }
305 already_dumping = false;
306}
307
308MusingI::MusingI()
309{
310 if (!global_sanity.already_dumping)
311 global_sanity.musings.push_back(this);
312}
313
314MusingI::~MusingI()
315{
316 if (!global_sanity.already_dumping)
317 {
318 I(global_sanity.musings.back() == this);
319 global_sanity.musings.pop_back();
320 }
321}
322
323template <> void
324dump(string const & obj, string & out)
325{
326 out = obj;
327}
328
329void
330print_var(std::string const & value, char const * var,
331 char const * file, int const line, char const * func)
332{
333 std::cout << (FL("----- begin '%s' (in %s, at %s:%d)\n")
334 % var % func % file % line)
335 << value
336 << (FL("\n----- end '%s' (in %s, at %s:%d)\n\n")
337 % var % func % file % line);
338}
339
340void MusingBase::gasp_head(string & out) const
341{
342 out = (boost::format("----- begin '%s' (in %s, at %s:%d)\n")
343 % name % func % file % line
344 ).str();
345}
346
347void MusingBase::gasp_body(const string & objstr, string & out) const
348{
349 out += (boost::format("%s%s"
350 "----- end '%s' (in %s, at %s:%d)\n")
351 % objstr
352 % (*(objstr.end() - 1) == '\n' ? "" : "\n")
353 % name % func % file % line
354 ).str();
355}
356
357const locale &
358get_user_locale()
359{
360 // this is awkward because if LC_CTYPE is set to something the
361 // runtime doesn't know about, it will fail. in that case,
362 // the default will have to do.
363 static bool init = false;
364 static locale user_locale;
365 if (!init)
366 {
367 init = true;
368 try
369 {
370 user_locale = locale("");
371 }
372 catch( ... )
373 {}
374 }
375 return user_locale;
376}
377
378struct
379format_base::impl
380{
381 format fmt;
382 ostringstream oss;
383
384 impl(impl const & other) : fmt(other.fmt)
385 {}
386
387 impl & operator=(impl const & other)
388 {
389 if (&other != this)
390 {
391 fmt = other.fmt;
392 oss.str(string());
393 }
394 return *this;
395 }
396
397 impl(char const * pattern)
398 : fmt(pattern)
399 {}
400 impl(string const & pattern)
401 : fmt(pattern)
402 {}
403 impl(char const * pattern, locale const & loc)
404 : fmt(pattern, loc)
405 {}
406 impl(string const & pattern, locale const & loc)
407 : fmt(pattern, loc)
408 {}
409};
410
411format_base::format_base(format_base const & other)
412 : pimpl(other.pimpl ? new impl(*(other.pimpl)) : NULL)
413{
414
415}
416
417format_base::~format_base()
418{
419 delete pimpl;
420}
421
422format_base &
423format_base::operator=(format_base const & other)
424{
425 if (&other != this)
426 {
427 impl * tmp = NULL;
428
429 try
430 {
431 if (other.pimpl)
432 tmp = new impl(*(other.pimpl));
433 }
434 catch (...)
435 {
436 if (tmp)
437 delete tmp;
438 }
439
440 if (pimpl)
441 delete pimpl;
442
443 pimpl = tmp;
444 }
445 return *this;
446}
447
448format_base::format_base(char const * pattern)
449 : pimpl(new impl(pattern))
450{}
451
452format_base::format_base(std::string const & pattern)
453 : pimpl(new impl(pattern))
454{}
455
456format_base::format_base(char const * pattern, locale const & loc)
457 : pimpl(new impl(pattern, loc))
458{}
459
460format_base::format_base(string const & pattern, locale const & loc)
461 : pimpl(new impl(pattern, loc))
462{}
463
464ostream &
465format_base::get_stream() const
466{
467 return pimpl->oss;
468}
469
470void
471format_base::flush_stream() const
472{
473 pimpl->fmt % pimpl->oss.str();
474 pimpl->oss.str(string());
475}
476
477void format_base::put_and_flush_signed(s64 const & s) const { pimpl->fmt % s; }
478void format_base::put_and_flush_signed(s32 const & s) const { pimpl->fmt % s; }
479void format_base::put_and_flush_signed(s16 const & s) const { pimpl->fmt % s; }
480void format_base::put_and_flush_signed(s8 const & s) const { pimpl->fmt % s; }
481
482void format_base::put_and_flush_unsigned(u64 const & u) const { pimpl->fmt % u; }
483void format_base::put_and_flush_unsigned(u32 const & u) const { pimpl->fmt % u; }
484void format_base::put_and_flush_unsigned(u16 const & u) const { pimpl->fmt % u; }
485void format_base::put_and_flush_unsigned(u8 const & u) const { pimpl->fmt % u; }
486
487void format_base::put_and_flush_float(float const & f) const { pimpl->fmt % f; }
488void format_base::put_and_flush_double(double const & d) const { pimpl->fmt % d; }
489
490std::string
491format_base::str() const
492{
493 return pimpl->fmt.str();
494}
495
496i18n_format::i18n_format(const char * localized_pattern)
497 : format_base(localized_pattern, get_user_locale())
498{
499}
500
501i18n_format::i18n_format(std::string const & localized_pattern)
502 : format_base(localized_pattern, get_user_locale())
503{
504}
505
506ostream &
507operator<<(ostream & os, format_base const & fmt)
508{
509 return os << fmt.str();
510}
511
512i18n_format F(const char * str)
513{
514 return i18n_format(gettext(str));
515}
516
517
518i18n_format FP(const char * str1, const char * strn, unsigned long count)
519{
520 return i18n_format(ngettext(str1, strn, count));
521}
522
523plain_format FL(const char * str)
524{
525 return plain_format(str);
526}
527
528// Local Variables:
529// mode: C++
530// fill-column: 76
531// c-file-style: "gnu"
532// indent-tabs-mode: nil
533// End:
534// 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