kdecore Library API Documentation

kdebug.cpp

00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
00003                   2002 Holger Freyther (freyther@kde.org)
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018     Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include "kdebug.h"
00022 
00023 #ifdef NDEBUG
00024 #undef kdDebug
00025 #undef kdBacktrace
00026 #undef kdWarning
00027 #endif
00028 
00029 #include "kdebugdcopiface.h"
00030 
00031 #include "kapplication.h"
00032 #include "kglobal.h"
00033 #include "kinstance.h"
00034 #include "kstandarddirs.h"
00035 
00036 #include <qmessagebox.h>
00037 #include <klocale.h>
00038 #include <qfile.h>
00039 #include <qintdict.h>
00040 #include <qstring.h>
00041 #include <qdatetime.h>
00042 #include <qpoint.h>
00043 #include <qrect.h>
00044 #include <qregion.h>
00045 #include <qstringlist.h>
00046 #include <qpen.h>
00047 #include <qbrush.h>
00048 #include <qsize.h>
00049 
00050 #include <kurl.h>
00051 
00052 #include <stdlib.h> // abort
00053 #include <unistd.h> // getpid
00054 #include <stdarg.h> // vararg stuff
00055 #include <ctype.h>      // isprint
00056 #include <syslog.h>
00057 #include <errno.h>
00058 #include <string.h>
00059 #include <kconfig.h>
00060 #include "kstaticdeleter.h"
00061 #include <config.h>
00062 
00063 #ifdef HAVE_BACKTRACE
00064 #include <execinfo.h>
00065 #endif
00066 
00067 class KDebugEntry;
00068 
00069 class KDebugEntry
00070 {
00071 public:
00072     KDebugEntry (int n, const QCString& d) {number=n; descr=d;}
00073     unsigned int number;
00074     QCString descr;
00075 };
00076 
00077 static QIntDict<KDebugEntry> *KDebugCache;
00078 
00079 static KStaticDeleter< QIntDict<KDebugEntry> > kdd;
00080 
00081 static QCString getDescrFromNum(unsigned int _num)
00082 {
00083   if (!KDebugCache) {
00084     kdd.setObject(KDebugCache, new QIntDict<KDebugEntry>( 601 ));
00085     // Do not call this deleter from ~KApplication
00086     KGlobal::unregisterStaticDeleter(&kdd);
00087     KDebugCache->setAutoDelete(true);
00088   }
00089 
00090   KDebugEntry *ent = KDebugCache->find( _num );
00091   if ( ent )
00092     return ent->descr;
00093 
00094   if ( !KDebugCache->isEmpty() ) // areas already loaded
00095     return QCString();
00096 
00097   QString filename(locate("config","kdebug.areas"));
00098   if (filename.isEmpty())
00099       return QCString();
00100 
00101   QFile file(filename);
00102   if (!file.open(IO_ReadOnly)) {
00103     qWarning("Couldn't open %s", filename.local8Bit().data());
00104     file.close();
00105     return QCString();
00106   }
00107 
00108   uint lineNumber=0;
00109   QCString line(1024);
00110   int len;
00111 
00112   while (( len = file.readLine(line.data(),line.size()-1) ) > 0) {
00113       int i=0;
00114       ++lineNumber;
00115 
00116       while (line[i] && line[i] <= ' ')
00117         i++;
00118 
00119       unsigned char ch=line[i];
00120 
00121       if ( !ch || ch =='#' || ch =='\n')
00122           continue; // We have an eof, a comment or an empty line
00123 
00124       if (ch < '0' && ch > '9') {
00125           qWarning("Syntax error: no number (line %u)",lineNumber);
00126           continue;
00127       }
00128 
00129       const int numStart=i;
00130       do {
00131           ch=line[++i];
00132       } while ( ch >= '0' && ch <= '9');
00133 
00134       const Q_ULONG number =line.mid(numStart,i).toULong();
00135 
00136       while (line[i] && line[i] <= ' ')
00137         i++;
00138 
00139       KDebugCache->insert(number, new KDebugEntry(number, line.mid(i, len-i-1)));
00140   }
00141   file.close();
00142 
00143   ent = KDebugCache->find( _num );
00144   if ( ent )
00145       return ent->descr;
00146 
00147   return QCString();
00148 }
00149 
00150 enum DebugLevels {
00151     KDEBUG_INFO=    0,
00152     KDEBUG_WARN=    1,
00153     KDEBUG_ERROR=   2,
00154     KDEBUG_FATAL=   3
00155 };
00156 
00157 
00158 struct kDebugPrivate {
00159   kDebugPrivate() :
00160     oldarea(0), config(0) { }
00161 
00162   ~kDebugPrivate() { delete config; }
00163 
00164   QCString aAreaName;
00165   unsigned int oldarea;
00166   KConfig *config;
00167 };
00168 
00169 static kDebugPrivate *kDebug_data = 0;
00170 static KStaticDeleter<kDebugPrivate> pcd;
00171 static KStaticDeleter<KDebugDCOPIface> dcopsd;
00172 static KDebugDCOPIface* kDebugDCOPIface = 0;
00173 
00174 static void kDebugBackend( unsigned short nLevel, unsigned int nArea, const char *data)
00175 {
00176   if ( !kDebug_data )
00177   {
00178       pcd.setObject(kDebug_data, new kDebugPrivate());
00179       // Do not call this deleter from ~KApplication
00180       KGlobal::unregisterStaticDeleter(&pcd);
00181 
00182       // create the dcop interface if it has not been created yet
00183       if (!kDebugDCOPIface)
00184       {
00185           kDebugDCOPIface = dcopsd.setObject(kDebugDCOPIface, new KDebugDCOPIface);
00186       }
00187   }
00188 
00189   if (!kDebug_data->config && KGlobal::_instance )
00190   {
00191       kDebug_data->config = new KConfig("kdebugrc", false, false);
00192       kDebug_data->config->setGroup("0");
00193 
00194       //AB: this is necessary here, otherwise all output with area 0 won't be
00195       //prefixed with anything, unless something with area != 0 is called before
00196       if ( KGlobal::_instance )
00197         kDebug_data->aAreaName = KGlobal::instance()->instanceName();
00198   }
00199 
00200   if (kDebug_data->config && kDebug_data->oldarea != nArea) {
00201     kDebug_data->config->setGroup( QString::number(static_cast<int>(nArea)) );
00202     kDebug_data->oldarea = nArea;
00203     if ( nArea > 0 && KGlobal::_instance )
00204       kDebug_data->aAreaName = getDescrFromNum(nArea);
00205     if ((nArea == 0) || kDebug_data->aAreaName.isEmpty())
00206       if ( KGlobal::_instance )
00207         kDebug_data->aAreaName = KGlobal::instance()->instanceName();
00208   }
00209 
00210   int nPriority = 0;
00211   QString aCaption;
00212 
00213     /* Determine output */
00214 
00215   QString key;
00216   switch( nLevel )
00217   {
00218   case KDEBUG_INFO:
00219       key = "InfoOutput";
00220       aCaption = "Info";
00221       nPriority = LOG_INFO;
00222       break;
00223   case KDEBUG_WARN:
00224       key = "WarnOutput";
00225       aCaption = "Warning";
00226       nPriority = LOG_WARNING;
00227     break;
00228   case KDEBUG_FATAL:
00229       key = "FatalOutput";
00230       aCaption = "Fatal Error";
00231       nPriority = LOG_CRIT;
00232       break;
00233   case KDEBUG_ERROR:
00234   default:
00235       /* Programmer error, use "Error" as default */
00236       key = "ErrorOutput";
00237       aCaption = "Error";
00238       nPriority = LOG_ERR;
00239       break;
00240   }
00241 
00242   short nOutput = kDebug_data->config ? kDebug_data->config->readNumEntry(key, 2) : 2;
00243 
00244   // If the application doesn't have a QApplication object it can't use
00245   // a messagebox.
00246   if (!kapp && (nOutput == 1))
00247     nOutput = 2;
00248   else if ( nOutput == 4 && nLevel != KDEBUG_FATAL )
00249       return;
00250 
00251   const int BUFSIZE = 4096;
00252   char buf[BUFSIZE];
00253   int nSize;
00254   if ( !kDebug_data->aAreaName.isEmpty() ) {
00255       strlcpy( buf, kDebug_data->aAreaName.data(), BUFSIZE );
00256       strlcat( buf, ": ", BUFSIZE );
00257       strlcat( buf, data, BUFSIZE );
00258       nSize = strlen( buf );
00259   }
00260   else
00261       nSize = strlcpy( buf, data, BUFSIZE );
00262 
00263 
00264   // Output
00265   switch( nOutput )
00266   {
00267   case 0: // File
00268   {
00269       const char* aKey;
00270       switch( nLevel )
00271       {
00272       case KDEBUG_INFO:
00273           aKey = "InfoFilename";
00274           break;
00275       case KDEBUG_WARN:
00276           aKey = "WarnFilename";
00277           break;
00278       case KDEBUG_FATAL:
00279           aKey = "FatalFilename";
00280           break;
00281       case KDEBUG_ERROR:
00282       default:
00283           aKey = "ErrorFilename";
00284           break;
00285       }
00286       QFile aOutputFile( kDebug_data->config->readPathEntry(aKey, "kdebug.dbg") );
00287       aOutputFile.open( IO_WriteOnly | IO_Append | IO_Raw );
00288       if ( ( nSize == -1 ) || ( nSize >= BUFSIZE ) )
00289           aOutputFile.writeBlock( buf, BUFSIZE-1 );
00290       else
00291           aOutputFile.writeBlock( buf, nSize );
00292       aOutputFile.close();
00293       break;
00294   }
00295   case 1: // Message Box
00296   {
00297       // Since we are in kdecore here, we cannot use KMsgBox and use
00298       // QMessageBox instead
00299       if ( !kDebug_data->aAreaName.isEmpty() )
00300           aCaption += QString("(%1)").arg( kDebug_data->aAreaName );
00301       QMessageBox::warning( 0L, aCaption, data, i18n("&OK") );
00302       break;
00303   }
00304   case 2: // Shell
00305   {
00306       write( 2, buf, nSize ); //fputs( buf, stderr );
00307       break;
00308   }
00309   case 3: // syslog
00310   {
00311       syslog( nPriority, "%s", buf);
00312       break;
00313   }
00314   }
00315 
00316   // check if we should abort
00317   if( ( nLevel == KDEBUG_FATAL )
00318       && ( !kDebug_data->config || kDebug_data->config->readNumEntry( "AbortFatal", 1 ) ) )
00319         abort();
00320 }
00321 
00322 kdbgstream &perror( kdbgstream &s) { return s << QString::fromLocal8Bit(strerror(errno)); }
00323 kdbgstream kdDebug(int area) { return kdbgstream(area, KDEBUG_INFO); }
00324 kdbgstream kdDebug(bool cond, int area) { if (cond) return kdbgstream(area, KDEBUG_INFO); else return kdbgstream(0, 0, false); }
00325 
00326 kdbgstream kdError(int area) { return kdbgstream("ERROR: ", area, KDEBUG_ERROR); }
00327 kdbgstream kdError(bool cond, int area) { if (cond) return kdbgstream("ERROR: ", area, KDEBUG_ERROR); else return kdbgstream(0,0,false); }
00328 kdbgstream kdWarning(int area) { return kdbgstream("WARNING: ", area, KDEBUG_WARN); }
00329 kdbgstream kdWarning(bool cond, int area) { if (cond) return kdbgstream("WARNING: ", area, KDEBUG_WARN); else return kdbgstream(0,0,false); }
00330 kdbgstream kdFatal(int area) { return kdbgstream("FATAL: ", area, KDEBUG_FATAL); }
00331 kdbgstream kdFatal(bool cond, int area) { if (cond) return kdbgstream("FATAL: ", area, KDEBUG_FATAL); else return kdbgstream(0,0,false); }
00332 
00333 kdbgstream::kdbgstream(kdbgstream &str)
00334  : output(str.output), area(str.area), level(str.level), print(str.print) 
00335 { 
00336     str.output.truncate(0); 
00337 }
00338 
00339 void kdbgstream::flush() {
00340     if (output.isEmpty() || !print)
00341     return;
00342     kDebugBackend( level, area, output.local8Bit().data() );
00343     output = QString::null;
00344 }
00345 
00346 kdbgstream &kdbgstream::form(const char *format, ...)
00347 {
00348     char buf[4096];
00349     va_list arguments;
00350     va_start( arguments, format );
00351     vsnprintf( buf, sizeof(buf), format, arguments );
00352     va_end(arguments);
00353     *this << buf;
00354     return *this;
00355 }
00356 
00357 kdbgstream::~kdbgstream() {
00358     if (!output.isEmpty()) {
00359     fprintf(stderr, "ASSERT: debug output not ended with \\n\n");
00360         fprintf(stderr, "%s", kdBacktrace().latin1());
00361     *this << "\n";
00362     }
00363 }
00364 
00365 kdbgstream& kdbgstream::operator << (char ch)
00366 {
00367   if (!print) return *this;
00368   if (!isprint(ch))
00369     output += "\\x" + QString::number( static_cast<uint>( ch ), 16 ).rightJustify(2, '0');
00370   else {
00371     output += ch;
00372     if (ch == '\n') flush();
00373   }
00374   return *this;
00375 }
00376 
00377 kdbgstream& kdbgstream::operator << (QChar ch)
00378 {
00379   if (!print) return *this;
00380   if (!ch.isPrint())
00381     output += "\\x" + QString::number( ch.unicode(), 16 ).rightJustify(2, '0');
00382   else {
00383     output += ch;
00384     if (ch == '\n') flush();
00385   }
00386   return *this;
00387 }
00388 
00389 kdbgstream& kdbgstream::operator << (QWidget* widget)
00390 {
00391     return *this << const_cast< const QWidget* >( widget );
00392 }
00393 
00394 kdbgstream& kdbgstream::operator << (const QWidget* widget)
00395 {
00396   QString string, temp;
00397   // -----
00398   if(widget==0)
00399     {
00400       string=(QString)"[Null pointer]";
00401     } else {
00402       temp.setNum((ulong)widget, 16);
00403       string=(QString)"["+widget->className()+" pointer "
00404     + "(0x" + temp + ")";
00405       if(widget->name(0)==0)
00406     {
00407       string += " to unnamed widget, ";
00408     } else {
00409       string += (QString)" to widget " + widget->name() + ", ";
00410     }
00411       string += "geometry="
00412     + QString().setNum(widget->width())
00413     + "x"+QString().setNum(widget->height())
00414     + "+"+QString().setNum(widget->x())
00415     + "+"+QString().setNum(widget->y())
00416     + "]";
00417     }
00418   if (!print)
00419     {
00420       return *this;
00421     }
00422   output += string;
00423   if (output.at(output.length() -1 ) == '\n')
00424     {
00425       flush();
00426     }
00427   return *this;
00428 }
00429 /*
00430  * either use 'output' directly and do the flush if needed
00431  * or use the QString operator which calls the char* operator
00432  *
00433  */
00434 kdbgstream& kdbgstream::operator<<( const QDateTime& time) {
00435     *this << time.toString();
00436     return *this;
00437 }
00438 kdbgstream& kdbgstream::operator<<( const QDate& date) {
00439     *this << date.toString();
00440 
00441     return *this;
00442 }
00443 kdbgstream& kdbgstream::operator<<( const QTime& time ) {
00444     *this << time.toString();
00445     return *this;
00446 }
00447 kdbgstream& kdbgstream::operator<<( const QPoint& p ) {
00448     *this << "(" << p.x() << ", " << p.y() << ")";
00449     return *this;
00450 }
00451 kdbgstream& kdbgstream::operator<<( const QSize& s ) {
00452     *this << "[" << s.width() << "x" << s.height() << "]";
00453     return *this;
00454 }
00455 kdbgstream& kdbgstream::operator<<( const QRect& r ) {
00456     *this << "[" << r.x() << "," << r.y() << " - " << r.width() << "x" << r.height() << "]";
00457     return *this;
00458 }
00459 kdbgstream& kdbgstream::operator<<( const QRegion& reg ) {
00460     *this<< "[ ";
00461 
00462     QMemArray<QRect>rs=reg.rects();
00463     for (uint i=0;i<rs.size();++i)
00464         *this << QString("[%1,%2 - %3x%4] ").arg(rs[i].x()).arg(rs[i].y()).arg(rs[i].width()).arg(rs[i].height() ) ;
00465 
00466     *this <<"]";
00467     return *this;
00468 }
00469 kdbgstream& kdbgstream::operator<<( const KURL& u ) {
00470     *this << u.prettyURL();
00471     return *this;
00472 }
00473 kdbgstream& kdbgstream::operator<<( const QStringList& l ) {
00474     *this << "(";
00475     *this << l.join(",");
00476     *this << ")";
00477 
00478     return *this;
00479 }
00480 kdbgstream& kdbgstream::operator<<( const QColor& c ) {
00481     if ( c.isValid() )
00482         *this <<c.name();
00483     else
00484         *this << "(invalid/default)";
00485     return *this;
00486 }
00487 kdbgstream& kdbgstream::operator<<( const QPen& p ) {
00488     static const char* const s_penStyles[] = {
00489         "NoPen", "SolidLine", "DashLine", "DotLine", "DashDotLine",
00490         "DashDotDotLine" };
00491     static const char* const s_capStyles[] = {
00492         "FlatCap", "SquareCap", "RoundCap" };
00493     *this << "[ style:";
00494     *this << s_penStyles[ p.style() ];
00495     *this << " width:";
00496     *this << p.width();
00497     *this << " color:";
00498     if ( p.color().isValid() )
00499         *this << p.color().name();
00500     else
00501         *this <<"(invalid/default)";
00502     if ( p.width() > 0 ) // cap style doesn't matter, otherwise
00503     {
00504         *this << " capstyle:";
00505         *this << s_capStyles[ p.capStyle() >> 4 ];
00506         // join style omitted
00507     }
00508     *this <<" ]";
00509     return *this;
00510 }
00511 kdbgstream& kdbgstream::operator<<( const QBrush& b) {
00512     static const char* const s_brushStyles[] = {
00513         "NoBrush", "SolidPattern", "Dense1Pattern", "Dense2Pattern", "Dense3Pattern",
00514         "Dense4Pattern", "Dense5Pattern", "Dense6Pattern", "Dense7Pattern",
00515         "HorPattern", "VerPattern", "CrossPattern", "BDiagPattern", "FDiagPattern",
00516         "DiagCrossPattern" };
00517 
00518     *this <<"[ style: ";
00519     *this <<s_brushStyles[ b.style() ];
00520     *this <<" color: ";
00521     // can't use operator<<(str, b.color()) because that terminates a kdbgstream (flushes)
00522     if ( b.color().isValid() )
00523         *this <<b.color().name() ;
00524     else
00525         *this <<"(invalid/default)";
00526     if ( b.pixmap() )
00527         *this <<" has a pixmap";
00528     *this <<" ]";
00529     return *this;
00530 }
00531 
00532 kdbgstream& kdbgstream::operator<<( const QVariant& v) {
00533     *this << "[variant: ";
00534     *this << v.typeName();
00535     // For now we just attempt a conversion to string.
00536     // Feel free to switch(v.type()) and improve the output.
00537     *this << " toString=";
00538     *this << v.toString();
00539     *this << "]";
00540     return *this;
00541 }
00542 
00543 kdbgstream& kdbgstream::operator<<( const QByteArray& data) {
00544     if (!print) return *this;
00545     output += '[';
00546     unsigned int i = 0;
00547     unsigned int sz = QMIN( data.size(), 64 );
00548     for ( ; i < sz ; ++i ) {
00549         output += QString::number( data[i], 16 ).rightJustify(2, '0');
00550         if ( i < sz )
00551             output += ' ';
00552     }
00553     if ( sz < data.size() )
00554         output += "...";
00555     output += ']';
00556     return *this;
00557 }
00558 
00559 QString kdBacktrace(int levels)
00560 {
00561     QString s;
00562 #ifdef HAVE_BACKTRACE
00563     void* trace[256];
00564     int n = backtrace(trace, 256);
00565     if (!n)
00566     return s;
00567     char** strings = backtrace_symbols (trace, n);
00568 
00569     if ( levels != -1 )
00570         n = QMIN( n, levels );
00571     s = "[\n";
00572 
00573     for (int i = 0; i < n; ++i)
00574         s += QString::number(i) +
00575              QString::fromLatin1(": ") +
00576              QString::fromLatin1(strings[i]) + QString::fromLatin1("\n");
00577     s += "]\n";
00578     if (strings)
00579         free (strings);
00580 #endif
00581     return s;
00582 }
00583 
00584 QString kdBacktrace()
00585 {
00586     return kdBacktrace(-1 /*all*/);
00587 }
00588 
00589 void kdClearDebugConfig()
00590 {
00591     delete kDebug_data->config;
00592     kDebug_data->config = 0;
00593 }
00594 
00595 
00596 // Needed for --enable-final
00597 #ifdef NDEBUG
00598 #define kdDebug kndDebug
00599 #endif
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Mar 30 10:09:39 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003