kdecore Library: kdebug.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

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

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:04 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003