1999-10-07 18:44:17 +00:00
|
|
|
|
// Created by Lars Gullik Bj<42>nnes
|
|
|
|
|
// Copyright 1999 Lars Gullik Bj<42>nnes (larsbj@lyx.org)
|
|
|
|
|
// Released into the public domain.
|
|
|
|
|
|
|
|
|
|
// Primarily developed for use in the LyX Project http://www.lyx.org/
|
|
|
|
|
// but should be adaptable to any project.
|
|
|
|
|
|
|
|
|
|
//#define TEST_DEBUGSTREAM
|
|
|
|
|
|
1999-11-03 14:11:17 +00:00
|
|
|
|
#include <config.h>
|
|
|
|
|
|
1999-10-07 18:44:17 +00:00
|
|
|
|
//#include "DebugStream.h"
|
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
|
|
//<2F>Since the current C++ lib in egcs does not have a standard implementation
|
|
|
|
|
// of basic_streambuf and basic_filebuf we don't have to include this
|
|
|
|
|
// header.
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
#include <fstream>
|
|
|
|
|
#endif
|
|
|
|
|
|
1999-10-22 09:45:25 +00:00
|
|
|
|
using std::streambuf;
|
|
|
|
|
using std::streamsize;
|
|
|
|
|
using std::filebuf;
|
|
|
|
|
using std::cerr;
|
|
|
|
|
using std::ios;
|
|
|
|
|
|
1999-10-07 18:44:17 +00:00
|
|
|
|
ostream & operator<<(ostream & o, Debug::type t)
|
|
|
|
|
{
|
|
|
|
|
return o << int(t);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** This is a streambuffer that never prints out anything, at least
|
|
|
|
|
that is the intention. You can call it a no-op streambuffer, and
|
|
|
|
|
the ostream that uses it will be a no-op stream.
|
|
|
|
|
*/
|
1999-10-13 10:34:07 +00:00
|
|
|
|
class nullbuf : public streambuf {
|
1999-10-07 18:44:17 +00:00
|
|
|
|
protected:
|
|
|
|
|
///
|
|
|
|
|
virtual int sync() { return 0; }
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
virtual streamsize xsputn(char const *, streamsize n) {
|
1999-10-07 18:44:17 +00:00
|
|
|
|
// fakes a purge of the buffer by returning n
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
///
|
|
|
|
|
virtual int overflow(int c = EOF) {
|
|
|
|
|
// fakes success by returning c
|
|
|
|
|
return c == EOF ? ' ' : c;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** A streambuf that sends the output to two different streambufs. These
|
|
|
|
|
can be any kind of streambufs.
|
|
|
|
|
*/
|
1999-10-13 10:34:07 +00:00
|
|
|
|
class teebuf : public streambuf {
|
1999-10-07 18:44:17 +00:00
|
|
|
|
public:
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
teebuf(streambuf * b1, streambuf * b2)
|
|
|
|
|
: streambuf(), sb1(b1), sb2(b2) {}
|
1999-10-07 18:44:17 +00:00
|
|
|
|
protected:
|
|
|
|
|
///
|
|
|
|
|
virtual int sync() {
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
sb2->pubsync();
|
|
|
|
|
return sb1->pubsync();
|
|
|
|
|
#else
|
|
|
|
|
sb2->sync();
|
|
|
|
|
return sb1->sync();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
virtual streamsize xsputn(char const * p, streamsize n) {
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
sb2->sputn(p, n);
|
|
|
|
|
return sb1->sputn(p, n);
|
|
|
|
|
#else
|
|
|
|
|
sb2->xsputn(p, n);
|
|
|
|
|
return sb1->xsputn(p, n);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
///
|
|
|
|
|
virtual int overflow(int c = EOF) {
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
sb2->sputc(c);
|
|
|
|
|
return sb1->sputc(c);
|
|
|
|
|
#else
|
|
|
|
|
sb2->overflow(c);
|
|
|
|
|
return sb1->overflow(c);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
streambuf * sb1;
|
1999-10-07 18:44:17 +00:00
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
streambuf * sb2;
|
1999-10-07 18:44:17 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
class debugbuf : public streambuf {
|
1999-10-07 18:44:17 +00:00
|
|
|
|
public:
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
debugbuf(streambuf * b)
|
|
|
|
|
: streambuf(), sb(b) {}
|
1999-10-07 18:44:17 +00:00
|
|
|
|
protected:
|
|
|
|
|
///
|
|
|
|
|
virtual int sync() {
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
return sb->pubsync();
|
|
|
|
|
#else
|
|
|
|
|
return sb->sync();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
virtual streamsize xsputn(char const * p, streamsize n) {
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
return sb->sputn(p, n);
|
|
|
|
|
#else
|
|
|
|
|
return sb->xsputn(p, n);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
///
|
|
|
|
|
virtual int overflow(int c = EOF) {
|
1999-10-22 09:45:25 +00:00
|
|
|
|
#ifdef MODERN_STL_STREAMS
|
1999-10-07 18:44:17 +00:00
|
|
|
|
return sb->sputc(c);
|
|
|
|
|
#else
|
|
|
|
|
return sb->overflow(c);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
private:
|
|
|
|
|
///
|
1999-10-13 10:34:07 +00:00
|
|
|
|
streambuf * sb;
|
1999-10-07 18:44:17 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// So that public parts of DebugStream does not need to know about filebuf
|
|
|
|
|
struct DebugStream::debugstream_internal {
|
|
|
|
|
/// Used when logging to file.
|
1999-10-13 10:34:07 +00:00
|
|
|
|
filebuf fbuf;
|
1999-10-07 18:44:17 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Constructor, sets the debug level to t.
|
|
|
|
|
DebugStream::DebugStream(Debug::type t)
|
1999-10-13 10:34:07 +00:00
|
|
|
|
: ostream(new debugbuf(cerr.rdbuf())),
|
1999-10-07 18:44:17 +00:00
|
|
|
|
dt(t), nullstream(new nullbuf), internal(0) {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Constructor, sets the log file to f, and the debug level to t.
|
|
|
|
|
DebugStream::DebugStream(char const * f, Debug::type t)
|
1999-10-13 10:34:07 +00:00
|
|
|
|
: ostream(new debugbuf(cerr.rdbuf())),
|
1999-10-07 18:44:17 +00:00
|
|
|
|
dt(t), nullstream(new nullbuf),
|
|
|
|
|
internal(new debugstream_internal)
|
|
|
|
|
{
|
1999-10-13 10:34:07 +00:00
|
|
|
|
internal->fbuf.open(f, ios::out|ios::app);
|
|
|
|
|
delete rdbuf(new teebuf(cerr.rdbuf(),
|
1999-10-07 18:44:17 +00:00
|
|
|
|
&internal->fbuf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DebugStream::~DebugStream()
|
|
|
|
|
{
|
|
|
|
|
delete nullstream.rdbuf(0); // Without this we leak
|
|
|
|
|
delete rdbuf(0); // Without this we leak
|
|
|
|
|
if (internal)
|
|
|
|
|
delete internal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Sets the debugstreams' logfile to f.
|
|
|
|
|
void DebugStream::logFile(char const * f)
|
|
|
|
|
{
|
|
|
|
|
if (internal) {
|
|
|
|
|
internal->fbuf.close();
|
|
|
|
|
} else {
|
|
|
|
|
internal = new debugstream_internal;
|
|
|
|
|
}
|
1999-10-13 10:34:07 +00:00
|
|
|
|
internal->fbuf.open(f, ios::out|ios::app);
|
|
|
|
|
delete rdbuf(new teebuf(cerr.rdbuf(),
|
1999-10-07 18:44:17 +00:00
|
|
|
|
&internal->fbuf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TEST_DEBUGSTREAM
|
|
|
|
|
|
|
|
|
|
// Example debug stream
|
|
|
|
|
DebugStream debugstream;
|
|
|
|
|
|
|
|
|
|
int main(int, char **)
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
I have been running some tests on this to see how much overhead
|
|
|
|
|
this kind of permanent debug code has. My conclusion is: not
|
|
|
|
|
much. In all, but the most time critical code, this will have
|
|
|
|
|
close to no impact at all.
|
|
|
|
|
|
|
|
|
|
In the tests that I have run the use of
|
|
|
|
|
if (debugstream.debugging(DebugStream::INFO))
|
|
|
|
|
debugstream << "some debug\n";
|
|
|
|
|
has close to no overhead when the debug level is not
|
|
|
|
|
DebugStream::INFO.
|
|
|
|
|
|
|
|
|
|
The overhead for
|
|
|
|
|
debugstream.debug(DebugStream::INFO) << "some debug\n";
|
|
|
|
|
is also very small when the debug level is not
|
|
|
|
|
DebugStream::INFO. However the overhead for this will increase
|
|
|
|
|
if complex debugging information is output.
|
|
|
|
|
|
|
|
|
|
The overhead when the debug level is DebugStream::INFO can be
|
|
|
|
|
significant, but since we then are running in debug mode it is
|
|
|
|
|
of no concern.
|
|
|
|
|
|
|
|
|
|
Why should we use this instead of the class Error that we already
|
|
|
|
|
have? First of all it uses C++ iostream and constructs, secondly
|
|
|
|
|
it will be a lot easier to output the debug info that we need
|
|
|
|
|
without a lot of manual conversions, thirdly we can now use
|
|
|
|
|
iomanipulators and the complete iostream formatting functions.
|
|
|
|
|
pluss it will work for all types that have a operator<<
|
|
|
|
|
defined, and can be used in functors that take a ostream & as
|
|
|
|
|
parameter. And there should be less need for temporary objects.
|
|
|
|
|
And one nice bonus is that we get a log file almost for
|
|
|
|
|
free.
|
|
|
|
|
|
|
|
|
|
Some of the names are of course open to modifications. I will try
|
|
|
|
|
to use the names we already use in LyX.
|
|
|
|
|
*/
|
|
|
|
|
// Just a few simple debugs to show how it can work.
|
|
|
|
|
debugstream << "Debug level set to Debug::NONE\n";
|
|
|
|
|
if (debugstream.debugging()) {
|
|
|
|
|
debugstream << "Something must be debugged\n";
|
|
|
|
|
}
|
|
|
|
|
debugstream.debug(Debug::WARN) << "more debug(WARN)\n";
|
|
|
|
|
debugstream.debug(Debug::INFO) << "even more debug(INFO)\n";
|
|
|
|
|
debugstream.debug(Debug::CRIT) << "even more debug(CRIT)\n";
|
|
|
|
|
debugstream.level(Debug::value("INFO"));
|
|
|
|
|
debugstream << "Setting debug level to Debug::INFO\n";
|
|
|
|
|
if (debugstream.debugging()) {
|
|
|
|
|
debugstream << "Something must be debugged\n";
|
|
|
|
|
}
|
|
|
|
|
debugstream.debug(Debug::WARN) << "more debug(WARN)\n";
|
|
|
|
|
debugstream.debug(Debug::INFO) << "even more debug(INFO)\n";
|
|
|
|
|
debugstream.debug(Debug::CRIT) << "even more debug(CRIT)\n";
|
|
|
|
|
debugstream.addLevel(Debug::type(Debug::CRIT |
|
|
|
|
|
Debug::WARN));
|
|
|
|
|
debugstream << "Adding Debug::CRIT and Debug::WARN\n";
|
|
|
|
|
debugstream[Debug::WARN] << "more debug(WARN)\n";
|
|
|
|
|
debugstream[Debug::INFO] << "even more debug(INFO)\n";
|
|
|
|
|
debugstream[Debug::CRIT] << "even more debug(CRIT)\n";
|
|
|
|
|
debugstream.delLevel(Debug::INFO);
|
|
|
|
|
debugstream << "Removing Debug::INFO\n";
|
|
|
|
|
debugstream[Debug::WARN] << "more debug(WARN)\n";
|
|
|
|
|
debugstream[Debug::INFO] << "even more debug(INFO)\n";
|
|
|
|
|
debugstream[Debug::CRIT] << "even more debug(CRIT)\n";
|
|
|
|
|
debugstream.logFile("logfile");
|
|
|
|
|
debugstream << "Setting logfile to \"logfile\"\n";
|
|
|
|
|
debugstream << "Value: " << 123 << " " << "12\n";
|
|
|
|
|
int i = 0;
|
|
|
|
|
int * p = new int;
|
|
|
|
|
// note: the (void*) is needed on g++ 2.7.x since it does not
|
|
|
|
|
// support partial specialization. In egcs this should not be
|
|
|
|
|
// needed.
|
|
|
|
|
debugstream << "automatic " << &i
|
1999-10-13 10:34:07 +00:00
|
|
|
|
<< ", free store " << p << endl;
|
1999-10-07 18:44:17 +00:00
|
|
|
|
delete p;
|
|
|
|
|
/*
|
|
|
|
|
for (int j = 0; j < 200000; ++j) {
|
|
|
|
|
DebugStream tmp;
|
1999-10-13 10:34:07 +00:00
|
|
|
|
tmp << "Test" << endl;
|
1999-10-07 18:44:17 +00:00
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
#endif
|