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