]> git.lyx.org Git - lyx.git/blob - src/support/DebugStream.C
Standard blurb.
[lyx.git] / src / support / DebugStream.C
1 /**
2  * \file DebugStream.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  *
8  * Full author contact details are available in file CREDITS
9  */
10
11 //#define TEST_DEBUGSTREAM
12
13 #include <config.h>
14
15 //#include "DebugStream.h"
16 #include "debug.h"
17
18 // Since the current C++ lib in egcs does not have a standard implementation
19 // of basic_streambuf and basic_filebuf we don't have to include this
20 // header.
21 //#define MODERN_STL_STREAMS
22 #ifdef MODERN_STL_STREAMS
23 #include <fstream>
24 #endif
25 #include <iostream>
26
27 using std::ostream;
28 using std::streambuf;
29 using std::streamsize;
30 using std::filebuf;
31 using std::cerr;
32 using std::ios;
33
34 ostream & operator<<(ostream & o, Debug::type t)
35 {
36         return o << int(t);
37 }
38
39 /** This is a streambuffer that never prints out anything, at least
40     that is the intention. You can call it a no-op streambuffer, and
41     the ostream that uses it will be a no-op stream.
42 */
43 class nullbuf : public streambuf {
44 protected:
45 #ifndef MODERN_STL_STREAMS
46         typedef char char_type;
47         typedef int int_type;
48         ///
49         virtual int sync() { return 0; }
50 #endif
51         ///
52         virtual streamsize xsputn(char_type const *, streamsize n) {
53                 // fakes a purge of the buffer by returning n
54                 return n;
55         }
56 #ifdef MODERN_STL_STREAMS
57         ///
58         virtual int_type overflow(int_type c = traits_type::eof()) {
59                 // fakes success by returning c
60                 return c == traits_type::eof() ? ' ' : c;
61         }
62 #else
63         ///
64         virtual int_type overflow(int_type c = EOF) {
65                 // fakes success by returning c
66                 return c == EOF ? ' ' : c;
67         }
68 #endif
69 };
70
71 /** A streambuf that sends the output to two different streambufs. These
72     can be any kind of streambufs.
73 */
74 class teebuf : public streambuf {
75 public:
76         ///
77         teebuf(streambuf * b1, streambuf * b2)
78                 : streambuf(), sb1(b1), sb2(b2) {}
79 protected:
80 #ifdef MODERN_STL_STREAMS
81         ///
82         virtual int sync() {
83                 sb2->pubsync();
84                 return sb1->pubsync();
85         }
86         ///
87         virtual streamsize xsputn(char_type const * p, streamsize n) {
88                 sb2->sputn(p, n);
89                 return sb1->sputn(p, n);
90         }
91         ///
92         virtual int_type overflow(int_type c = traits_type::eof()) {
93                 sb2->sputc(c);
94                 return sb1->sputc(c);
95         }
96 #else
97         typedef char char_type;
98         typedef int int_type;
99         ///
100         virtual int sync() {
101                 sb2->sync();
102                 return sb1->sync();
103         }
104         ///
105         virtual streamsize xsputn(char_type const * p, streamsize n) {
106                 sb2->xsputn(p, n);
107                 return sb1->xsputn(p, n);
108         }
109         ///
110         virtual int_type overflow(int_type c = EOF) {
111                 sb2->overflow(c);
112                 return sb1->overflow(c);
113         }
114 #endif
115 private:
116         ///
117         streambuf * sb1;
118         ///
119         streambuf * sb2;
120 };
121
122 ///
123 class debugbuf : public streambuf {
124 public:
125         ///
126         debugbuf(streambuf * b)
127                 : streambuf(), sb(b) {}
128 protected:
129 #ifdef MODERN_STL_STREAMS
130         ///
131         virtual int sync() {
132                 return sb->pubsync();
133         }
134         ///
135         virtual streamsize xsputn(char_type const * p, streamsize n) {
136                 return sb->sputn(p, n);
137         }
138         ///
139         virtual int_type overflow(int_type c = traits_type::eof()) {
140                 return sb->sputc(c);
141         }
142 #else
143         typedef char char_type;
144         typedef int int_type;
145         ///
146         virtual int sync() {
147                 return sb->sync();
148         }
149         ///
150         virtual streamsize xsputn(char_type const * p, streamsize n) {
151                 return sb->xsputn(p, n);
152         }
153         ///
154         virtual int_type overflow(int_type c = EOF) {
155                 return sb->overflow(c);
156         }
157 #endif
158 private:
159         ///
160         streambuf * sb;
161 };
162
163
164 /// So that public parts of DebugStream does not need to know about filebuf
165 struct DebugStream::debugstream_internal {
166         /// Used when logging to file.
167         filebuf fbuf;
168 };
169
170
171 /// Constructor, sets the debug level to t.
172 DebugStream::DebugStream(Debug::type t)
173         : ostream(new debugbuf(cerr.rdbuf())),
174           dt(t), nullstream(new nullbuf), internal(0) {}
175
176
177 /// Constructor, sets the log file to f, and the debug level to t.
178 DebugStream::DebugStream(char const * f, Debug::type t)
179         : ostream(new debugbuf(cerr.rdbuf())),
180           dt(t), nullstream(new nullbuf),
181           internal(new debugstream_internal)
182 {
183         internal->fbuf.open(f, ios::out|ios::app);
184         delete rdbuf(new teebuf(cerr.rdbuf(),
185                                 &internal->fbuf));
186 }
187
188
189 DebugStream::~DebugStream()
190 {
191         delete nullstream.rdbuf(0); // Without this we leak
192         delete rdbuf(0);            // Without this we leak
193         delete internal;
194 }
195
196
197 /// Sets the debugstreams' logfile to f.
198 void DebugStream::logFile(char const * f)
199 {
200         if (internal) {
201                 internal->fbuf.close();
202         } else {
203                 internal = new debugstream_internal;
204         }
205         internal->fbuf.open(f, ios::out|ios::app);
206         delete rdbuf(new teebuf(cerr.rdbuf(),
207                                 &internal->fbuf));
208 }
209
210
211 #ifdef TEST_DEBUGSTREAM
212
213 // Example debug stream
214 DebugStream debugstream;
215
216 int main(int, char **)
217 {
218         /**
219            I have been running some tests on this to see how much overhead
220            this kind of permanent debug code has. My conclusion is: not
221            much. In all, but the most time critical code, this will have
222            close to no impact at all.
223
224            In the tests that I have run the use of
225            if (debugstream.debugging(DebugStream::INFO))
226            debugstream << "some debug\n";
227            has close to no overhead when the debug level is not
228            DebugStream::INFO.
229
230            The overhead for
231            debugstream.debug(DebugStream::INFO) << "some debug\n";
232            is also very small when the debug level is not
233            DebugStream::INFO. However the overhead for this will increase
234            if complex debugging information is output.
235
236            The overhead when the debug level is DebugStream::INFO can be
237            significant, but since we then are running in debug mode it is
238            of no concern.
239
240            Why should we use this instead of the class Error that we already
241            have? First of all it uses C++ iostream and constructs, secondly
242            it will be a lot easier to output the debug info that we need
243            without a lot of manual conversions, thirdly we can now use
244            iomanipulators and the complete iostream formatting functions.
245            pluss it will work for all types that have a operator<<
246            defined, and can be used in functors that take a ostream & as
247            parameter. And there should be less need for temporary objects.
248            And one nice bonus is that we get a log file almost for
249            free.
250
251            Some of the names are of course open to modifications. I will try
252            to use the names we already use in LyX.
253         */
254         // Just a few simple debugs to show how it can work.
255         debugstream << "Debug level set to Debug::NONE\n";
256         if (debugstream.debugging()) {
257                 debugstream << "Something must be debugged\n";
258         }
259         debugstream.debug(Debug::WARN) << "more debug(WARN)\n";
260         debugstream.debug(Debug::INFO) << "even more debug(INFO)\n";
261         debugstream.debug(Debug::CRIT) << "even more debug(CRIT)\n";
262         debugstream.level(Debug::value("INFO"));
263         debugstream << "Setting debug level to Debug::INFO\n";
264         if (debugstream.debugging()) {
265                 debugstream << "Something must be debugged\n";
266         }
267         debugstream.debug(Debug::WARN) << "more debug(WARN)\n";
268         debugstream.debug(Debug::INFO) << "even more debug(INFO)\n";
269         debugstream.debug(Debug::CRIT) << "even more debug(CRIT)\n";
270         debugstream.addLevel(Debug::type(Debug::CRIT |
271                                          Debug::WARN));
272         debugstream << "Adding Debug::CRIT and Debug::WARN\n";
273         debugstream[Debug::WARN] << "more debug(WARN)\n";
274         debugstream[Debug::INFO] << "even more debug(INFO)\n";
275         debugstream[Debug::CRIT] << "even more debug(CRIT)\n";
276         debugstream.delLevel(Debug::INFO);
277         debugstream << "Removing Debug::INFO\n";
278         debugstream[Debug::WARN] << "more debug(WARN)\n";
279         debugstream[Debug::INFO] << "even more debug(INFO)\n";
280         debugstream[Debug::CRIT] << "even more debug(CRIT)\n";
281         debugstream.logFile("logfile");
282         debugstream << "Setting logfile to \"logfile\"\n";
283         debugstream << "Value: " << 123 << " " << "12\n";
284         int i = 0;
285         int * p = new int;
286         // note: the (void*) is needed on g++ 2.7.x since it does not
287         // support partial specialization. In egcs this should not be
288         // needed.
289         debugstream << "automatic " << &i
290                     << ", free store " << p << endl;
291         delete p;
292         /*
293         for (int j = 0; j < 200000; ++j) {
294                 DebugStream tmp;
295                 tmp << "Test" << endl;
296         }
297         */
298 }
299 #endif