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 
00004     This library is free software; you can redistribute it and/or
00005     modify it under the terms of the GNU Library General Public
00006     License as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017     Boston, MA 02111-1307, USA.
00018 */
00019 
00020 // Include our header without NDEBUG defined to avoid having the kDebugInfo
00021 // functions inlined to noops (which would then conflict with their definition
00022 // here).
00023 
00024 #include "kdebug.h"
00025 
00026 #ifdef NDEBUG
00027 #undef kdDebug
00028 #undef kdBacktrace
00029 #endif
00030 
00031 #include "kapplication.h"
00032 #include "kglobal.h"
00033 #include "kinstance.h"
00034 #include "kstandarddirs.h"
00035 #include <qmessagebox.h>
00036 #include <klocale.h>
00037 #include <qfile.h>
00038 #include <qintdict.h>
00039 #include <qstring.h>
00040 #include <qtextstream.h>
00041 
00042 #include <stdlib.h> // abort
00043 #include <unistd.h> // getpid
00044 #include <stdarg.h> // vararg stuff
00045 #include <ctype.h>      // isprint
00046 #include <syslog.h>
00047 #include <errno.h>
00048 #include <string.h>
00049 #include <kconfig.h>
00050 #include "kstaticdeleter.h"
00051 #include <config.h>
00052 
00053 #ifdef HAVE_BACKTRACE
00054 #include <execinfo.h>
00055 #endif
00056 
00057 class KDebugEntry;
00058 
00059 class KDebugEntry
00060 {
00061 public:
00062     KDebugEntry (int n, QString d) {number=n; descr=d;}
00063     unsigned int number;
00064     QString descr;
00065 };
00066 
00067 static QIntDict<KDebugEntry> *KDebugCache;
00068 
00069 static KStaticDeleter< QIntDict<KDebugEntry> > kdd;
00070 
00071 static QString getDescrFromNum(unsigned int _num)
00072 {
00073   if (!KDebugCache) {
00074     kdd.setObject(KDebugCache, new QIntDict<KDebugEntry>);
00075     // Do not call this deleter from ~KApplication
00076     KGlobal::unregisterStaticDeleter(&kdd);
00077     KDebugCache->setAutoDelete(true);
00078   }
00079 
00080   KDebugEntry *ent = KDebugCache->find( _num );
00081   if ( ent )
00082     return ent->descr;
00083 
00084   if ( !KDebugCache->isEmpty() ) // areas already loaded
00085     return QString::null;
00086 
00087   QString filename(locate("config","kdebug.areas"));
00088   QFile file(filename);
00089   if (!file.open(IO_ReadOnly)) {
00090     qWarning("Couldn't open %s", filename.local8Bit().data());
00091     file.close();
00092     return "";
00093   }
00094 
00095   unsigned long number = 0;
00096   bool longOK;
00097 
00098   QTextStream *ts = new QTextStream(&file);
00099   ts->setEncoding( QTextStream::Latin1 );
00100   while (!ts->eof()) {
00101     const QString data(ts->readLine());
00102     int i = 0;
00103     int len = data.length();
00104 
00105     QChar ch = data[0];
00106     if (ch == '#' || ch.isNull()) {
00107       continue;
00108     }
00109     while (ch.isSpace()) {
00110       if (!(i < len))
00111     continue;
00112       ++i;
00113       ch = data[i];
00114     }
00115     if (ch.isNumber()) {
00116     int numStart = i ;
00117     while (ch.isNumber())  {
00118       if (!(i < len))
00119         continue;
00120       ++i;
00121       ch = data[i];
00122     }
00123     number = data.mid(numStart,i).toULong(&longOK);
00124     }
00125     while (ch.isSpace()) {
00126       if (!(i < len))
00127     continue;
00128       ++i;
00129       ch = data[i];
00130     }
00131     const QString description(data.mid(i, len));
00132     //qDebug("number: [%i] description: [%s]", number, description.latin1());
00133 
00134     KDebugCache->insert(number, new KDebugEntry(number,description));
00135   }
00136 
00137   delete ts;
00138   file.close();
00139 
00140   ent = KDebugCache->find( _num );
00141   if ( ent )
00142       return ent->descr;
00143 
00144   return QString::null;
00145 }
00146 
00147 enum DebugLevels {
00148     KDEBUG_INFO=    0,
00149     KDEBUG_WARN=    1,
00150     KDEBUG_ERROR=   2,
00151     KDEBUG_FATAL=   3
00152 };
00153 
00154 
00155 struct kDebugPrivate {
00156   kDebugPrivate() : 
00157     oldarea(0), config(0) { }
00158     
00159   ~kDebugPrivate() { delete config; }
00160 
00161   QString aAreaName;
00162   unsigned int oldarea;
00163   KConfig *config;
00164 };
00165 
00166 static kDebugPrivate *kDebug_data = 0;
00167 static KStaticDeleter<kDebugPrivate> pcd;
00168 
00169 static void kDebugBackend( unsigned short nLevel, unsigned int nArea, const char *data)
00170 {
00171   if ( !kDebug_data )
00172   {
00173       pcd.setObject(kDebug_data, new kDebugPrivate());
00174       // Do not call this deleter from ~KApplication
00175       KGlobal::unregisterStaticDeleter(&pcd);
00176   }
00177 
00178   if (!kDebug_data->config && KGlobal::_instance )
00179   {
00180       kDebug_data->config = new KConfig("kdebugrc", false, false);
00181       kDebug_data->config->setGroup("0");
00182   }
00183 
00184   if (kDebug_data->config && kDebug_data->oldarea != nArea) {
00185     kDebug_data->config->setGroup( QString::number(static_cast<int>(nArea)) );
00186     kDebug_data->oldarea = nArea;
00187     if ( nArea > 0 && KGlobal::_instance )
00188       kDebug_data->aAreaName = getDescrFromNum(nArea);
00189     if ((nArea == 0) || kDebug_data->aAreaName.isEmpty())
00190       if ( KGlobal::_instance )
00191         kDebug_data->aAreaName = KGlobal::instance()->instanceName();
00192   }
00193 
00194   int nPriority = 0;
00195   QString aCaption;
00196 
00197     /* Determine output */
00198 
00199     QString key;
00200     switch( nLevel )
00201       {
00202       case KDEBUG_INFO:
00203     key = "InfoOutput";
00204     aCaption = "Info";
00205     nPriority = LOG_INFO;
00206     break;
00207       case KDEBUG_WARN:
00208     key = "WarnOutput";
00209     aCaption = "Warning";
00210     nPriority = LOG_WARNING;
00211     break;
00212       case KDEBUG_FATAL:
00213     key = "FatalOutput";
00214     aCaption = "Fatal Error";
00215     nPriority = LOG_CRIT;
00216     break;
00217       case KDEBUG_ERROR:
00218       default:
00219     /* Programmer error, use "Error" as default */
00220     key = "ErrorOutput";
00221     aCaption = "Error";
00222     nPriority = LOG_ERR;
00223     break;
00224       }
00225 
00226   short nOutput = kDebug_data->config ? kDebug_data->config->readNumEntry(key, 4) : 4;
00227 
00228   // If the application doesn't have a QApplication object it can't use
00229   // a messagebox.
00230   if (!kapp && (nOutput == 1))
00231     nOutput = 2;
00232 
00233   // Output
00234   switch( nOutput )
00235         {
00236         case 0: // File
00237           {
00238                 QString aKey;
00239                 switch( nLevel )
00240                 {
00241                     case KDEBUG_INFO:
00242                         aKey = "InfoFilename";
00243                         break;
00244                     case KDEBUG_WARN:
00245                         aKey = "WarnFilename";
00246                         break;
00247                     case KDEBUG_FATAL:
00248                         aKey = "FatalFilename";
00249                         break;
00250                     case KDEBUG_ERROR:
00251                     default:
00252                         aKey = "ErrorFilename";
00253                         break;
00254                 }
00255                 QString aOutputFileName = kDebug_data->config->readEntry(aKey, "kdebug.dbg");
00256 
00257                 const int BUFSIZE = 4096;
00258                 char buf[BUFSIZE] = "";
00259                 buf[BUFSIZE-1] = '\0';
00260         int nSize;
00261                 if ( !kDebug_data->aAreaName.isEmpty() )
00262             nSize = snprintf( buf, BUFSIZE-1, "%s: %s", kDebug_data->aAreaName.ascii(), data);
00263         else
00264             nSize = snprintf( buf, BUFSIZE-1, "%s", data);
00265 
00266                 QFile aOutputFile( aOutputFileName );
00267                 aOutputFile.open( IO_WriteOnly | IO_Append );
00268                 if ( ( nSize == -1 ) || ( nSize >= BUFSIZE ) )
00269                     aOutputFile.writeBlock( buf, BUFSIZE-1 );
00270                 else
00271                     aOutputFile.writeBlock( buf, nSize );
00272                 aOutputFile.close();
00273                 break;
00274           }
00275         case 1: // Message Box
00276           {
00277                 // Since we are in kdecore here, we cannot use KMsgBox and use
00278                 // QMessageBox instead
00279           if ( !kDebug_data->aAreaName.isEmpty() ) aCaption += QString("(")+kDebug_data->aAreaName+")";
00280           QMessageBox::warning( 0L, aCaption, data, i18n("&OK") );
00281           break;
00282           }
00283         case 2: // Shell
00284           {
00285               FILE *output;
00286               /* we used to use stdout for debug
00287               if (nPriority == LOG_INFO)
00288                   output = stderr;
00289               else */
00290                   output = stderr;
00291               // Uncomment this to get the pid of the app in the output (useful for e.g. kioslaves)
00292           // if ( !kDebug_data->aAreaName.isEmpty() ) fprintf( output, "%d %s: ", (int)getpid(), kDebug_data->aAreaName.ascii() );
00293           if ( !kDebug_data->aAreaName.isEmpty() ) fprintf( output, "%s: ", kDebug_data->aAreaName.ascii() );
00294           fputs(  data, output);
00295           break;
00296           }
00297         case 3: // syslog
00298           {
00299           syslog( nPriority, "%s", data);
00300           }
00301         case 4: // nothing
00302           {
00303           }
00304         }
00305 
00306   // check if we should abort
00307   if( ( nLevel == KDEBUG_FATAL )
00308       && ( !kDebug_data->config || kDebug_data->config->readNumEntry( "AbortFatal", 1 ) ) )
00309         abort();
00310 }
00311 
00312 kdbgstream &perror( kdbgstream &s) { return s << QString::fromLocal8Bit(strerror(errno)); }
00313 kdbgstream kdDebug(int area) { return kdbgstream(area, KDEBUG_INFO); }
00314 kdbgstream kdDebug(bool cond, int area) { if (cond) return kdbgstream(area, KDEBUG_INFO); else return kdbgstream(0, 0, false); }
00315 
00316 kdbgstream kdError(int area) { return kdbgstream("ERROR: ", area, KDEBUG_ERROR); }
00317 kdbgstream kdError(bool cond, int area) { if (cond) return kdbgstream("ERROR: ", area, KDEBUG_ERROR); else return kdbgstream(0,0,false); }
00318 kdbgstream kdWarning(int area) { return kdbgstream("WARNING: ", area, KDEBUG_WARN); }
00319 kdbgstream kdWarning(bool cond, int area) { if (cond) return kdbgstream("WARNING: ", area, KDEBUG_WARN); else return kdbgstream(0,0,false); }
00320 kdbgstream kdFatal(int area) { return kdbgstream("FATAL: ", area, KDEBUG_FATAL); }
00321 kdbgstream kdFatal(bool cond, int area) { if (cond) return kdbgstream("FATAL: ", area, KDEBUG_FATAL); else return kdbgstream(0,0,false); }
00322 
00323 void kdbgstream::flush() {
00324     if (output.isEmpty() || !print)
00325     return;
00326     kDebugBackend( level, area, output.local8Bit().data() );
00327     output = QString::null;
00328 }
00329 
00330 kdbgstream &kdbgstream::form(const char *format, ...)
00331 {
00332     char buf[4096];
00333     va_list arguments;
00334     va_start( arguments, format );
00335     buf[sizeof(buf)-1] = '\0';
00336     vsnprintf( buf, sizeof(buf)-1, format, arguments );
00337     va_end(arguments);
00338     *this << buf;
00339     return *this;
00340 }
00341 
00342 kdbgstream::~kdbgstream() {
00343     if (!output.isEmpty()) {
00344     fprintf(stderr, "ASSERT: debug output not ended with \\n\n");
00345     *this << "\n";
00346     }
00347 }
00348 
00349 kdbgstream& kdbgstream::operator << (char ch)
00350 {
00351   if (!print) return *this;
00352   if (!isprint(ch))
00353     output += "\\x" + QString::number( static_cast<uint>( ch ) + 0x100, 16 ).right(2);
00354   else {
00355     output += ch;
00356     if (ch == '\n') flush();
00357   }
00358   return *this;
00359 }
00360 
00361 kdbgstream& kdbgstream::operator << (QWidget* widget)
00362 {
00363   QString string, temp;
00364   // -----
00365   if(widget==0)
00366     {
00367       string=(QString)"[Null pointer]";
00368     } else {
00369       temp.setNum((ulong)widget, 16);
00370       string=(QString)"["+widget->className()+" pointer "
00371     + "(0x" + temp + ")";
00372       if(widget->name(0)==0)
00373     {
00374       string += " to unnamed widget, ";
00375     } else {
00376       string += (QString)" to widget " + widget->name() + ", ";
00377     }
00378       string += "geometry="
00379     + QString().setNum(widget->width())
00380     + "x"+QString().setNum(widget->height())
00381     + "+"+QString().setNum(widget->x())
00382     + "+"+QString().setNum(widget->y())
00383     + "]";
00384     }
00385   if (!print)
00386     {
00387       return *this;
00388     }
00389   output += string;
00390   if (output.at(output.length() -1 ) == '\n')
00391     {
00392       flush();
00393     }
00394   return *this;
00395 }
00396 
00397 QString kdBacktrace(int levels)
00398 {
00399     QString s;
00400 #ifdef HAVE_BACKTRACE
00401     void* trace[256];
00402     int n = backtrace(trace, 256);
00403     char** strings = backtrace_symbols (trace, n);
00404 
00405     if ( levels != -1 )
00406         n = QMIN( n, levels );
00407     s = "[\n";
00408 
00409     for (int i = 0; i < n; ++i)
00410         s += QString::number(i) +
00411              QString::fromLatin1(": ") +
00412              QString::fromLatin1(strings[i]) + QString::fromLatin1("\n");
00413     s += "]\n";
00414     free (strings);
00415 #endif
00416     return s;
00417 }
00418 
00419 QString kdBacktrace()
00420 {
00421     return kdBacktrace(-1 /*all*/);
00422 }
00423 
00424 // Needed for --enable-final
00425 #ifdef NDEBUG
00426 #define kdDebug kndDebug
00427 #endif
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Oct 8 12:20:40 2003 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2001