• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDECore

kdebug.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #define KDE_EXTENDED_DEBUG_OUTPUT
00022 
00023 #ifndef QT_NO_CAST_FROM_ASCII
00024 #define QT_NO_CAST_FROM_ASCII
00025 #endif
00026 #ifndef QT_NO_CAST_TO_ASCII
00027 #define QT_NO_CAST_TO_ASCII
00028 #endif
00029 #ifndef KDE3_SUPPORT
00030 #define KDE3_SUPPORT
00031 #endif
00032 
00033 #include "kdebug.h"
00034 
00035 #ifdef Q_WS_WIN
00036 #include <fcntl.h>
00037 #include <windows.h>
00038 #include <wincon.h>
00039 #else
00040 #include <unistd.h>
00041 #include <stdio.h>
00042 #endif
00043 
00044 #ifdef NDEBUG
00045 #undef kDebug
00046 #undef kBacktrace
00047 #endif
00048 
00049 #include <config.h>
00050 
00051 #ifdef HAVE_SYS_TIME_H
00052 #include <sys/time.h>
00053 #endif
00054 #ifdef HAVE_TIME_H
00055 #include <time.h>
00056 #endif
00057 
00058 #include "kglobal.h"
00059 #include "kstandarddirs.h"
00060 #include "kdatetime.h"
00061 #include "kcmdlineargs.h"
00062 
00063 #include <kmessage.h>
00064 #include <klocale.h>
00065 #include <kconfiggroup.h>
00066 #include <kurl.h>
00067 
00068 #include <QtCore/QFile>
00069 #include <QtCore/QHash>
00070 #include <QtCore/QObject>
00071 #include <QtCore/QChar>
00072 #include <QtCore/QCoreApplication>
00073 
00074 #include <stdlib.h> // abort
00075 #include <unistd.h> // getpid
00076 #include <stdarg.h> // vararg stuff
00077 #include <ctype.h>      // isprint
00078 #include <syslog.h>
00079 #include <errno.h>
00080 #include <string.h>
00081 #include <kconfig.h>
00082 #include "kcomponentdata.h"
00083 
00084 #ifdef HAVE_BACKTRACE
00085 #include <execinfo.h>
00086 #endif
00087 
00088 #include "kdebugdbusiface_p.h"
00089 #include <QMutex>
00090 
00091 
00092 
00093 KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface = false;
00094 
00095 class KNoDebugStream: public QIODevice
00096 {
00097     // Q_OBJECT
00098 public:
00099     KNoDebugStream() { open(WriteOnly); }
00100     bool isSequential() const { return true; }
00101     qint64 readData(char *, qint64) { return 0; /* eof */ }
00102     qint64 readLineData(char *, qint64) { return 0; /* eof */ }
00103     qint64 writeData(const char *, qint64 len) { return len; }
00104 };
00105 
00106 class KSyslogDebugStream: public KNoDebugStream
00107 {
00108     // Q_OBJECT
00109 public:
00110     qint64 writeData(const char *data, qint64 len)
00111         {
00112             if (len) {
00113                 int nPriority = *data++;
00114                 // not using fromRawData because we need a terminating NUL
00115                 QByteArray buf(data, len);
00116 
00117                 syslog(nPriority, "%s", buf.constData());
00118             }
00119             return len;
00120         }
00121 };
00122 
00123 class KFileDebugStream: public KNoDebugStream
00124 {
00125     // Q_OBJECT
00126 public:
00127     qint64 writeData(const char *data, qint64 len)
00128         {
00129             if (len) {
00130                 QByteArray buf = QByteArray::fromRawData(data, len);
00131                 int pos = buf.indexOf('\0');
00132                 Q_ASSERT(pos != -1);
00133 
00134                 QFile aOutputFile(QFile::decodeName(data));
00135                 aOutputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered);
00136                 aOutputFile.write(data + pos + 1, len - pos - 1);
00137                 aOutputFile.putChar('\n');
00138                 aOutputFile.close();
00139             }
00140             return len;
00141         }
00142 };
00143 
00144 class KMessageBoxDebugStream: public KNoDebugStream
00145 {
00146     // Q_OBJECT
00147 public:
00148     qint64 writeData(const char *data, qint64 len)
00149         {
00150             if (len) {
00151                 // Since we are in kdecore here, we cannot use KMsgBox
00152                 QString caption = QString::fromAscii(data, len);
00153                 int pos = caption.indexOf(QLatin1Char('\0'));
00154                 Q_ASSERT(pos != -1);
00155 
00156                 QString msg = caption.mid(pos + 1);
00157                 caption.truncate(pos);
00158                 KMessage::message(KMessage::Information, msg, caption);
00159             }
00160             return len;
00161         }
00162 };
00163 
00164 class KLineEndStrippingDebugStream: public KNoDebugStream
00165 {
00166     // Q_OBJECT
00167 public:
00168     qint64 writeData(const char *data, qint64 len)
00169         {
00170             QByteArray buf = QByteArray::fromRawData(data, len);
00171             qt_message_output(QtDebugMsg, buf.trimmed().constData());
00172             return len;
00173         }
00174 };
00175 
00176 struct KDebugPrivate
00177 {
00178     enum OutputMode {
00179         FileOutput = 0,
00180         MessageBoxOutput = 1,
00181         QtOutput = 2,
00182         SyslogOutput = 3,
00183         NoOutput = 4,
00184         Unknown = 5
00185     };
00186 
00187     struct Area {
00188         inline Area() { clear(); }
00189         void clear(OutputMode set = Unknown)
00190         {
00191             for (int i = 0; i < 4; ++i) {
00192                 logFileName[i].clear();
00193                 mode[i] = set;
00194             }
00195         }
00196 
00197         QByteArray name;
00198         QString logFileName[4];
00199         OutputMode mode[4];
00200     };
00201     typedef QHash<unsigned int, Area> Cache;
00202 
00203     KDebugPrivate()
00204         : config(0), kDebugDBusIface(0)
00205     {
00206         Q_ASSERT(int(QtDebugMsg) == 0);
00207         Q_ASSERT(int(QtFatalMsg) == 3);
00208 
00209         // Create the dbus interface if it has not been created yet
00210         // But only register to DBus if we are in a process with a dbus event loop,
00211         // otherwise introspection will just hang.
00212         // Examples of processes without a dbus event loop: kioslaves and the main kdeinit process.
00213         //
00214         // How to know that we have a real event loop? That's tricky.
00215         // We could delay registration in kDebugDBusIface with a QTimer, but
00216         // it would still get triggered by kioslaves that use enterLoop/exitLoop
00217         // to run kio jobs synchronously.
00218         //
00219         // Solution: we have a bool that is set by KApplication
00220         // (kioslaves should use QCoreApplication but not KApplication).
00221         if (kde_kdebug_enable_dbus_interface) {
00222             kDebugDBusIface = new KDebugDBusIface;
00223         }
00224     }
00225 
00226     ~KDebugPrivate()
00227     {
00228         delete config;
00229         delete kDebugDBusIface;
00230     }
00231 
00232     void loadAreaNames()
00233     {
00234         cache.clear();
00235 
00236         Area &areaData = cache[0];
00237         areaData.clear(QtOutput);
00238 
00239         //AB: this is necessary here, otherwise all output with area 0 won't be
00240         //prefixed with anything, unless something with area != 0 is called before
00241         areaData.name = KGlobal::mainComponent().componentName().toUtf8();
00242 
00243         QString filename(KStandardDirs::locate("config", QLatin1String("kdebug.areas")));
00244         if (filename.isEmpty()) {
00245             return;
00246         }
00247         QFile file(filename);
00248         if (!file.open(QIODevice::ReadOnly)) {
00249             qWarning("Couldn't open %s", filename.toLocal8Bit().constData());
00250             file.close();
00251             return;
00252         }
00253 
00254         uint lineNumber=0;
00255 
00256         while (!file.atEnd()) {
00257             QByteArray line = file.readLine().trimmed();
00258             ++lineNumber;
00259             if (line.isEmpty())
00260                 continue;
00261 
00262             int i=0;
00263             unsigned char ch=line[i];
00264 
00265             if (ch =='#')
00266                 continue; // We have an eof, a comment or an empty line
00267 
00268             if (ch < '0' && ch > '9') {
00269                 qWarning("Syntax error: no number (line %u)",lineNumber);
00270                 continue;
00271             }
00272 
00273             do {
00274                 ch=line[++i];
00275             } while (ch >= '0' && ch <= '9' && i < line.length());
00276 
00277             unsigned int number = line.left(i).toUInt();
00278 
00279             while (i < line.length() && line[i] <= ' ')
00280                 i++;
00281 
00282             Area areaData;
00283             areaData.name = line.mid(i);
00284             cache.insert(number, areaData);
00285         }
00286         file.close();
00287     }
00288 
00289     inline int level(QtMsgType type)
00290     { return int(type) - int(QtDebugMsg); }
00291 
00292     OutputMode areaOutputMode(QtMsgType type, unsigned int area)
00293     {
00294         if (!config)
00295             return QtOutput;
00296 
00297         QString key;
00298         switch (type) {
00299         case QtDebugMsg:
00300             key = QLatin1String( "InfoOutput" );
00301             break;
00302         case QtWarningMsg:
00303             key = QLatin1String( "WarnOutput" );
00304             break;
00305         case QtFatalMsg:
00306             key = QLatin1String( "FatalOutput" );
00307             break;
00308         case QtCriticalMsg:
00309         default:
00310             /* Programmer error, use "Error" as default */
00311             key = QLatin1String( "ErrorOutput" );
00312             break;
00313         }
00314 
00315         KConfigGroup cg(config, QString::number(area));
00316         int mode = cg.readEntry(key, 2);
00317         return OutputMode(mode);
00318     }
00319 
00320     QString logFileName(QtMsgType type, unsigned int area)
00321     {
00322         if (!config)
00323             return QString();
00324 
00325         const char* aKey;
00326         switch (type)
00327         {
00328         case QtDebugMsg:
00329             aKey = "InfoFilename";
00330             break;
00331         case QtWarningMsg:
00332             aKey = "WarnFilename";
00333             break;
00334         case QtFatalMsg:
00335             aKey = "FatalFilename";
00336             break;
00337         case QtCriticalMsg:
00338         default:
00339             aKey = "ErrorFilename";
00340             break;
00341         }
00342 
00343         KConfigGroup cg(config, QString::number(area));
00344         return cg.readPathEntry(aKey, QLatin1String("kdebug.dbg"));
00345     }
00346 
00347     Cache::Iterator areaData(QtMsgType type, unsigned int num)
00348     {
00349         if (!config) {
00350             if (!KGlobal::hasMainComponent()) {
00351                 // we don't have a config and we can't create one...
00352                 Area &area = cache[0]; // create a dummy entry
00353                 Q_UNUSED(area);
00354                 return cache.find(0);
00355             }
00356 
00357             config = new KConfig(QLatin1String("kdebugrc"), KConfig::NoGlobals);
00358             loadAreaNames();
00359         }
00360 
00361         if (!cache.contains(num))
00362             // unknown area
00363             return cache.find(0);
00364 
00365         int l = level(type);
00366         Cache::Iterator it = cache.find(num);
00367         if (it->mode[l] == Unknown)
00368             it->mode[l] = areaOutputMode(type, num);
00369         if (it->mode[l] == FileOutput && it->logFileName[l].isEmpty())
00370             it->logFileName[l] = logFileName(type, num);
00371 
00372         return it;
00373     }
00374 
00375     QDebug setupFileWriter(const QString &fileName)
00376     {
00377         QDebug result(&filewriter);
00378         result.nospace() << qPrintable(fileName) << '\0';
00379         return result;
00380     }
00381 
00382     QDebug setupMessageBoxWriter(QtMsgType type, const QByteArray &areaName)
00383     {
00384         QDebug result(&messageboxwriter);
00385         QByteArray header;
00386 
00387         switch (type) {
00388         case QtDebugMsg:
00389             header = "Info (";
00390             break;
00391         case QtWarningMsg:
00392             header = "Warning (";
00393             break;
00394         case QtFatalMsg:
00395             header = "Fatal Error (";
00396             break;
00397         case QtCriticalMsg:
00398         default:
00399             header = "Error (";
00400             break;
00401         }
00402 
00403         header += areaName;
00404         header += ')';
00405         result.nospace() << header.constData() << '\0';
00406         return result;
00407     }
00408 
00409     QDebug setupSyslogWriter(QtMsgType type)
00410     {
00411         QDebug result(&syslogwriter);
00412         int level = 0;
00413 
00414         switch (type) {
00415         case QtDebugMsg:
00416             level = LOG_INFO;
00417             break;
00418         case QtWarningMsg:
00419             level = LOG_WARNING;
00420             break;
00421         case QtFatalMsg:
00422             level = LOG_CRIT;
00423             break;
00424         case QtCriticalMsg:
00425         default:
00426             level = LOG_ERR;
00427             break;
00428         }
00429         result.nospace() << char(level);
00430         return result;
00431     }
00432 
00433     QDebug setupQtWriter(QtMsgType type)
00434     {
00435         if (type != QtDebugMsg)
00436             return QDebug(type);
00437         return QDebug(&lineendstrippingwriter);
00438     }
00439 
00440     QDebug printHeader(QDebug s, const QByteArray &areaName, const char *, int, const char *funcinfo, bool colored)
00441     {
00442 #ifdef KDE_EXTENDED_DEBUG_OUTPUT
00443         QByteArray programName = cache.value(0).name;
00444         if (programName.isEmpty())
00445             programName = "<unknown program name>";
00446         s.nospace() << programName.constData() << "(" << unsigned(getpid()) << ")";
00447         if (areaName != programName)
00448             s << "/" << areaName.constData();
00449 
00450         if (funcinfo) {
00451             if(colored)
00452                 s << "\033[0;34m"; //blue
00453 # ifdef Q_CC_GNU
00454             // strip the function info down to the base function name
00455             // note that this throws away the template definitions,
00456             // the parameter types (overloads) and any const/volatile qualifiers
00457             QByteArray info = funcinfo;
00458             int pos = info.indexOf('(');
00459             Q_ASSERT_X(pos != -1, "kDebug",
00460                        "Bug in kDebug(): I don't know how to parse this function name");
00461             while (info.at(pos - 1) == ' ')
00462                 // that '(' we matched was actually the opening of a function-pointer
00463                 pos = info.indexOf('(', pos + 1);
00464 
00465             info.truncate(pos);
00466             // gcc 4.1.2 don't put a space between the return type and
00467             // the function name if the function is in an anonymous namespace
00468             int index = 1;
00469             forever {
00470                 index = info.indexOf("<unnamed>::", index);
00471                 if ( index == -1 )
00472                     break;
00473 
00474                 if ( info.at(index-1) != ':' )
00475                     info.insert(index, ' ');
00476 
00477                 index += strlen("<unnamed>::");
00478             }
00479             pos = info.lastIndexOf(' ');
00480             if (pos != -1) {
00481                 int startoftemplate = info.lastIndexOf('<');
00482                 if (startoftemplate != -1 && pos > startoftemplate &&
00483                     pos < info.lastIndexOf(">::"))
00484                     // we matched a space inside this function's template definition
00485                     pos = info.lastIndexOf(' ', startoftemplate);
00486             }
00487 
00488             if (pos + 1 == info.length())
00489                 // something went wrong, so gracefully bail out
00490                 s << " " << funcinfo;
00491             else
00492                 s << " " << info.constData() + pos + 1;
00493 # else
00494             s << " " << funcinfo;
00495 # endif
00496            if(colored)
00497                s  << "\033[0m";
00498         }
00499 
00500         s << ":";
00501 #else
00502         Q_UNUSED(funcinfo);
00503         if (!areaName.isEmpty())
00504             s << areaName.constData() << ':';
00505 #endif
00506         return s.space();
00507     }
00508 
00509     QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line,
00510                   const char *funcinfo)
00511     {
00512         static bool env_colored = (!qgetenv("KDE_COLOR_DEBUG").isEmpty());
00513         Cache::Iterator it = areaData(type, area);
00514         OutputMode mode = it->mode[level(type)];
00515         QString file = it->logFileName[level(type)];
00516         QByteArray areaName = it->name;
00517 
00518         if (areaName.isEmpty())
00519             areaName = cache.value(0).name;
00520         
00521         bool colored=false;
00522 
00523         QDebug s(&devnull);
00524         switch (mode) {
00525         case FileOutput:
00526             s = setupFileWriter(file);
00527             break;
00528         case MessageBoxOutput:
00529             s = setupMessageBoxWriter(type, areaName);
00530             break;
00531         case SyslogOutput:
00532             s = setupSyslogWriter(type);
00533             break;
00534         case NoOutput:
00535             s = QDebug(&devnull);
00536             return s; //no need to take the time to "print header" if we don't want to output anyway
00537             break;
00538         default:                // QtOutput
00539             s = setupQtWriter(type);
00540 #ifndef Q_OS_WIN
00541             //only color if the debug goes to a tty.
00542             colored = env_colored && isatty(fileno(stderr));  
00543 #endif
00544             break;
00545         }
00546 
00547         return printHeader(s, areaName, debugFile, line, funcinfo, colored);
00548     }
00549 
00550     QMutex mutex;
00551     KConfig *config;
00552     KDebugDBusIface *kDebugDBusIface;
00553     Cache cache;
00554 
00555     KNoDebugStream devnull;
00556     KSyslogDebugStream syslogwriter;
00557     KFileDebugStream filewriter;
00558     KMessageBoxDebugStream messageboxwriter;
00559     KLineEndStrippingDebugStream lineendstrippingwriter;
00560 };
00561 
00562 K_GLOBAL_STATIC(KDebugPrivate, kDebug_data)
00563 
00564 QString kRealBacktrace(int levels)
00565 {
00566     QString s;
00567 #ifdef HAVE_BACKTRACE
00568     void* trace[256];
00569     int n = backtrace(trace, 256);
00570     if (!n)
00571     return s;
00572     char** strings = backtrace_symbols (trace, n);
00573 
00574     if ( levels != -1 )
00575         n = qMin( n, levels );
00576     s = QLatin1String("[\n");
00577 
00578     for (int i = 0; i < n; ++i)
00579         s += QString::number(i) +
00580              QLatin1String(": ") +
00581              QLatin1String(strings[i]) + QLatin1String("\n");
00582     s += QLatin1String("]\n");
00583     if (strings)
00584         free (strings);
00585 #endif
00586     return s;
00587 }
00588 
00589 QDebug kDebugDevNull()
00590 {
00591     return QDebug(&kDebug_data->devnull);
00592 }
00593 
00594 QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
00595 {
00596     if (kDebug_data.isDestroyed()) {
00597         // we don't know what to return now...
00598         qCritical().nospace() << "kDebugStream called after destruction (from "
00599                               << (funcinfo ? funcinfo : "")
00600                               << (file ? " file " : " unknown file")
00601                               << (file ? file :"")
00602                               << " line " << line << ")";
00603         return QDebug(level);
00604     }
00605 
00606     QMutexLocker locker(&kDebug_data->mutex);
00607     return kDebug_data->stream(level, area, file, line, funcinfo);
00608 }
00609 
00610 QDebug perror(QDebug s, KDebugTag)
00611 {
00612     return s << QString::fromLocal8Bit(strerror(errno));
00613 }
00614 
00615 QDebug operator<<(QDebug s, const KDateTime &time)
00616 {
00617     if ( time.isDateOnly() )
00618         s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")";
00619     else
00620         s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")";
00621     return s.space();
00622 }
00623 
00624 QDebug operator<<(QDebug s, const KUrl &url)
00625 {
00626     s.nospace() << "KUrl(" << url.prettyUrl() << ")";
00627     return s.space();
00628 }
00629 
00630 void kClearDebugConfig()
00631 {
00632     if (!kDebug_data) return;
00633     QMutexLocker locker(&kDebug_data->mutex);
00634     delete kDebug_data->config;
00635     kDebug_data->config = 0;
00636 
00637     KDebugPrivate::Cache::Iterator it = kDebug_data->cache.begin(),
00638                                   end = kDebug_data->cache.end();
00639     for ( ; it != end; ++it)
00640         it->clear();
00641 }

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal