• Skip to content
  • Skip to link menu
KDE 4.3 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 #ifdef QT_NO_DEBUG
00185         DefaultOutput = NoOutput,
00186 #else
00187         DefaultOutput = QtOutput,
00188 #endif
00189         Unknown = 5
00190     };
00191 
00192     struct Area {
00193         inline Area() { clear(); }
00194         void clear(OutputMode set = Unknown)
00195         {
00196             for (int i = 0; i < 4; ++i) {
00197                 logFileName[i].clear();
00198                 mode[i] = set;
00199             }
00200         }
00201 
00202         QByteArray name;
00203         QString logFileName[4];
00204         OutputMode mode[4];
00205     };
00206     typedef QHash<unsigned int, Area> Cache;
00207 
00208     KDebugPrivate()
00209         : config(0), kDebugDBusIface(0)
00210     {
00211         Q_ASSERT(int(QtDebugMsg) == 0);
00212         Q_ASSERT(int(QtFatalMsg) == 3);
00213 
00214         // Create the dbus interface if it has not been created yet
00215         // But only register to DBus if we are in a process with a dbus event loop,
00216         // otherwise introspection will just hang.
00217         // Examples of processes without a dbus event loop: kioslaves and the main kdeinit process.
00218         //
00219         // How to know that we have a real event loop? That's tricky.
00220         // We could delay registration in kDebugDBusIface with a QTimer, but
00221         // it would still get triggered by kioslaves that use enterLoop/exitLoop
00222         // to run kio jobs synchronously.
00223         //
00224         // Solution: we have a bool that is set by KApplication
00225         // (kioslaves should use QCoreApplication but not KApplication).
00226         if (kde_kdebug_enable_dbus_interface) {
00227             kDebugDBusIface = new KDebugDBusIface;
00228         }
00229     }
00230 
00231     ~KDebugPrivate()
00232     {
00233         delete config;
00234         delete kDebugDBusIface;
00235     }
00236 
00237     void loadAreaNames()
00238     {
00239         cache.clear();
00240 
00241         Area &areaData = cache[0];
00242         areaData.clear();
00243 
00244         //AB: this is necessary here, otherwise all output with area 0 won't be
00245         //prefixed with anything, unless something with area != 0 is called before
00246         areaData.name = KGlobal::mainComponent().componentName().toUtf8();
00247 
00248         QString filename(KStandardDirs::locate("config", QLatin1String("kdebug.areas")));
00249         if (filename.isEmpty()) {
00250             return;
00251         }
00252         QFile file(filename);
00253         if (!file.open(QIODevice::ReadOnly)) {
00254             qWarning("Couldn't open %s", filename.toLocal8Bit().constData());
00255             file.close();
00256             return;
00257         }
00258 
00259         uint lineNumber=0;
00260 
00261         while (!file.atEnd()) {
00262             QByteArray line = file.readLine().trimmed();
00263             ++lineNumber;
00264             if (line.isEmpty())
00265                 continue;
00266 
00267             int i=0;
00268             unsigned char ch=line[i];
00269 
00270             if (ch =='#')
00271                 continue; // We have an eof, a comment or an empty line
00272 
00273             if (ch < '0' && ch > '9') {
00274                 qWarning("Syntax error: no number (line %u)",lineNumber);
00275                 continue;
00276             }
00277 
00278             do {
00279                 ch=line[++i];
00280             } while (ch >= '0' && ch <= '9' && i < line.length());
00281 
00282             unsigned int number = line.left(i).toUInt();
00283 
00284             while (i < line.length() && line[i] <= ' ')
00285                 i++;
00286 
00287             Area areaData;
00288             areaData.name = line.mid(i);
00289             cache.insert(number, areaData);
00290         }
00291         file.close();
00292     }
00293 
00294     inline int level(QtMsgType type)
00295     { return int(type) - int(QtDebugMsg); }
00296 
00297     OutputMode areaOutputMode(QtMsgType type, unsigned int area)
00298     {
00299         if (!config)
00300             return QtOutput;
00301 
00302         QString key;
00303         switch (type) {
00304         case QtDebugMsg:
00305             key = QLatin1String( "InfoOutput" );
00306             break;
00307         case QtWarningMsg:
00308             key = QLatin1String( "WarnOutput" );
00309             break;
00310         case QtFatalMsg:
00311             key = QLatin1String( "FatalOutput" );
00312             break;
00313         case QtCriticalMsg:
00314         default:
00315             /* Programmer error, use "Error" as default */
00316             key = QLatin1String( "ErrorOutput" );
00317             break;
00318         }
00319 
00320         KConfigGroup cg(config, QString::number(area));
00321         int mode = cg.readEntry(key, int(DefaultOutput));
00322         return OutputMode(mode);
00323     }
00324 
00325     QString logFileName(QtMsgType type, unsigned int area)
00326     {
00327         if (!config)
00328             return QString();
00329 
00330         const char* aKey;
00331         switch (type)
00332         {
00333         case QtDebugMsg:
00334             aKey = "InfoFilename";
00335             break;
00336         case QtWarningMsg:
00337             aKey = "WarnFilename";
00338             break;
00339         case QtFatalMsg:
00340             aKey = "FatalFilename";
00341             break;
00342         case QtCriticalMsg:
00343         default:
00344             aKey = "ErrorFilename";
00345             break;
00346         }
00347 
00348         KConfigGroup cg(config, QString::number(area));
00349         return cg.readPathEntry(aKey, QLatin1String("kdebug.dbg"));
00350     }
00351 
00352     Cache::Iterator areaData(QtMsgType type, unsigned int num)
00353     {
00354         if (!config) {
00355             if (!KGlobal::hasMainComponent()) {
00356                 // we don't have a config and we can't create one...
00357                 Area &area = cache[0]; // create a dummy entry
00358                 Q_UNUSED(area);
00359                 return cache.find(0);
00360             }
00361 
00362             config = new KConfig(QLatin1String("kdebugrc"), KConfig::NoGlobals);
00363             loadAreaNames();
00364         }
00365 
00366         if (!cache.contains(num)) {
00367             // unknown area
00368             return cache.find(0);
00369         }
00370 
00371         int l = level(type);
00372         Cache::Iterator it = cache.find(num);
00373         //qDebug() << "in cache for" << num << ":" << it->mode[l];
00374         if (it->mode[l] == Unknown)
00375             it->mode[l] = areaOutputMode(type, num);
00376         if (it->mode[l] == FileOutput && it->logFileName[l].isEmpty())
00377             it->logFileName[l] = logFileName(type, num);
00378 
00379         return it;
00380     }
00381 
00382     QDebug setupFileWriter(const QString &fileName)
00383     {
00384         QDebug result(&filewriter);
00385         result.nospace() << qPrintable(fileName) << '\0';
00386         return result;
00387     }
00388 
00389     QDebug setupMessageBoxWriter(QtMsgType type, const QByteArray &areaName)
00390     {
00391         QDebug result(&messageboxwriter);
00392         QByteArray header;
00393 
00394         switch (type) {
00395         case QtDebugMsg:
00396             header = "Info (";
00397             break;
00398         case QtWarningMsg:
00399             header = "Warning (";
00400             break;
00401         case QtFatalMsg:
00402             header = "Fatal Error (";
00403             break;
00404         case QtCriticalMsg:
00405         default:
00406             header = "Error (";
00407             break;
00408         }
00409 
00410         header += areaName;
00411         header += ')';
00412         result.nospace() << header.constData() << '\0';
00413         return result;
00414     }
00415 
00416     QDebug setupSyslogWriter(QtMsgType type)
00417     {
00418         QDebug result(&syslogwriter);
00419         int level = 0;
00420 
00421         switch (type) {
00422         case QtDebugMsg:
00423             level = LOG_INFO;
00424             break;
00425         case QtWarningMsg:
00426             level = LOG_WARNING;
00427             break;
00428         case QtFatalMsg:
00429             level = LOG_CRIT;
00430             break;
00431         case QtCriticalMsg:
00432         default:
00433             level = LOG_ERR;
00434             break;
00435         }
00436         result.nospace() << char(level);
00437         return result;
00438     }
00439 
00440     QDebug setupQtWriter(QtMsgType type)
00441     {
00442         if (type != QtDebugMsg)
00443             return QDebug(type);
00444         return QDebug(&lineendstrippingwriter);
00445     }
00446 
00447     QDebug printHeader(QDebug s, const QByteArray &areaName, const char *, int, const char *funcinfo, QtMsgType type, bool colored)
00448     {
00449 #ifdef KDE_EXTENDED_DEBUG_OUTPUT
00450         static bool printProcessInfo = (qgetenv("KDE_DEBUG_NOPROCESSINFO").isEmpty());
00451         static bool printAreaName = (qgetenv("KDE_DEBUG_NOAREANAME").isEmpty());
00452         static bool printMethodName = (qgetenv("KDE_DEBUG_NOMETHODNAME").isEmpty());
00453         QByteArray programName;
00454         s = s.nospace();
00455         if (printProcessInfo) {
00456             programName = cache.value(0).name;
00457             if (programName.isEmpty()) {
00458                 if (QCoreApplication::instance())
00459                     programName = QCoreApplication::instance()->applicationName().toLocal8Bit();
00460                 else
00461                     programName = "<unknown program name>";
00462             }
00463             s << programName.constData() << "(" << unsigned(getpid()) << ")";
00464         }
00465         if (printAreaName && (!printProcessInfo || areaName != programName)) {
00466             if (printProcessInfo)
00467                 s << "/";
00468             s << areaName.constData();
00469         }
00470 
00471         if (funcinfo && printMethodName) {
00472             if (colored) {
00473                 if (type <= QtDebugMsg)
00474                     s << "\033[0;34m"; //blue
00475                 else
00476                     s << "\033[0;31m"; //red
00477             }
00478 # ifdef Q_CC_GNU
00479             // strip the function info down to the base function name
00480             // note that this throws away the template definitions,
00481             // the parameter types (overloads) and any const/volatile qualifiers
00482             QByteArray info = funcinfo;
00483             int pos = info.indexOf('(');
00484             Q_ASSERT_X(pos != -1, "kDebug",
00485                        "Bug in kDebug(): I don't know how to parse this function name");
00486             while (info.at(pos - 1) == ' ')
00487                 // that '(' we matched was actually the opening of a function-pointer
00488                 pos = info.indexOf('(', pos + 1);
00489 
00490             info.truncate(pos);
00491             // gcc 4.1.2 don't put a space between the return type and
00492             // the function name if the function is in an anonymous namespace
00493             int index = 1;
00494             forever {
00495                 index = info.indexOf("<unnamed>::", index);
00496                 if ( index == -1 )
00497                     break;
00498 
00499                 if ( info.at(index-1) != ':' )
00500                     info.insert(index, ' ');
00501 
00502                 index += strlen("<unnamed>::");
00503             }
00504             pos = info.lastIndexOf(' ');
00505             if (pos != -1) {
00506                 int startoftemplate = info.lastIndexOf('<');
00507                 if (startoftemplate != -1 && pos > startoftemplate &&
00508                     pos < info.lastIndexOf(">::"))
00509                     // we matched a space inside this function's template definition
00510                     pos = info.lastIndexOf(' ', startoftemplate);
00511             }
00512 
00513             if (pos + 1 == info.length())
00514                 // something went wrong, so gracefully bail out
00515                 s << " " << funcinfo;
00516             else
00517                 s << " " << info.constData() + pos + 1;
00518 # else
00519             s << " " << funcinfo;
00520 # endif
00521            if(colored)
00522                s  << "\033[0m";
00523         }
00524 
00525         s << ":";
00526 #else
00527         Q_UNUSED(funcinfo);
00528         if (!areaName.isEmpty())
00529             s << areaName.constData() << ':';
00530 #endif
00531         return s.space();
00532     }
00533 
00534     QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line,
00535                   const char *funcinfo)
00536     {
00537         static bool env_colored = (!qgetenv("KDE_COLOR_DEBUG").isEmpty());
00538         Cache::Iterator it = areaData(type, area);
00539         OutputMode mode = it->mode[level(type)];
00540         QString file = it->logFileName[level(type)];
00541         QByteArray areaName = it->name;
00542 
00543         if (areaName.isEmpty())
00544             areaName = cache.value(0).name;
00545 
00546         bool colored=false;
00547 
00548         QDebug s(&devnull);
00549         switch (mode) {
00550         case FileOutput:
00551             s = setupFileWriter(file);
00552             break;
00553         case MessageBoxOutput:
00554             s = setupMessageBoxWriter(type, areaName);
00555             break;
00556         case SyslogOutput:
00557             s = setupSyslogWriter(type);
00558             break;
00559         case NoOutput:
00560             s = QDebug(&devnull);
00561             return s; //no need to take the time to "print header" if we don't want to output anyway
00562             break;
00563         default:                // QtOutput
00564             s = setupQtWriter(type);
00565 #ifndef Q_OS_WIN
00566             //only color if the debug goes to a tty.
00567             colored = env_colored && isatty(fileno(stderr));
00568 #endif
00569             break;
00570         }
00571 
00572         return printHeader(s, areaName, debugFile, line, funcinfo, type, colored);
00573     }
00574 
00575     QMutex mutex;
00576     KConfig *config;
00577     KDebugDBusIface *kDebugDBusIface;
00578     Cache cache;
00579 
00580     KNoDebugStream devnull;
00581     KSyslogDebugStream syslogwriter;
00582     KFileDebugStream filewriter;
00583     KMessageBoxDebugStream messageboxwriter;
00584     KLineEndStrippingDebugStream lineendstrippingwriter;
00585 };
00586 
00587 K_GLOBAL_STATIC(KDebugPrivate, kDebug_data)
00588 
00589 QString kRealBacktrace(int levels)
00590 {
00591     QString s;
00592 #ifdef HAVE_BACKTRACE
00593     void* trace[256];
00594     int n = backtrace(trace, 256);
00595     if (!n)
00596     return s;
00597     char** strings = backtrace_symbols (trace, n);
00598 
00599     if ( levels != -1 )
00600         n = qMin( n, levels );
00601     s = QLatin1String("[\n");
00602 
00603     for (int i = 0; i < n; ++i)
00604         s += QString::number(i) +
00605              QLatin1String(": ") +
00606              QLatin1String(strings[i]) + QLatin1String("\n");
00607     s += QLatin1String("]\n");
00608     if (strings)
00609         free (strings);
00610 #endif
00611     return s;
00612 }
00613 
00614 QDebug kDebugDevNull()
00615 {
00616     return QDebug(&kDebug_data->devnull);
00617 }
00618 
00619 QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
00620 {
00621     if (kDebug_data.isDestroyed()) {
00622         // we don't know what to return now...
00623         qCritical().nospace() << "kDebugStream called after destruction (from "
00624                               << (funcinfo ? funcinfo : "")
00625                               << (file ? " file " : " unknown file")
00626                               << (file ? file :"")
00627                               << " line " << line << ")";
00628         return QDebug(level);
00629     }
00630 
00631     QMutexLocker locker(&kDebug_data->mutex);
00632     return kDebug_data->stream(level, area, file, line, funcinfo);
00633 }
00634 
00635 QDebug perror(QDebug s, KDebugTag)
00636 {
00637     return s << QString::fromLocal8Bit(strerror(errno));
00638 }
00639 
00640 QDebug operator<<(QDebug s, const KDateTime &time)
00641 {
00642     if ( time.isDateOnly() )
00643         s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")";
00644     else
00645         s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")";
00646     return s.space();
00647 }
00648 
00649 QDebug operator<<(QDebug s, const KUrl &url)
00650 {
00651     s.nospace() << "KUrl(" << url.prettyUrl() << ")";
00652     return s.space();
00653 }
00654 
00655 void kClearDebugConfig()
00656 {
00657     if (!kDebug_data) return;
00658     QMutexLocker locker(&kDebug_data->mutex);
00659     delete kDebug_data->config;
00660     kDebug_data->config = 0;
00661 
00662     KDebugPrivate::Cache::Iterator it = kDebug_data->cache.begin(),
00663                                   end = kDebug_data->cache.end();
00664     for ( ; it != end; ++it)
00665         it->clear();
00666 }

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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