File indexing completed on 2024-04-14 14:20:01

0001 /* This file is part of the KDE libraries
0002     Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
0003                   2002 Holger Freyther (freyther@kde.org)
0004                   2007-2011 David Faure (faure@kde.org)
0005 
0006     This library is free software; you can redistribute it and/or
0007     modify it under the terms of the GNU Library General Public
0008     License as published by the Free Software Foundation; either
0009     version 2 of the License, or (at your option) any later version.
0010 
0011     This library is distributed in the hope that it will be useful,
0012     but WITHOUT ANY WARRANTY; without even the implied warranty of
0013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014     Library General Public License for more details.
0015 
0016     You should have received a copy of the GNU Library General Public License
0017     along with this library; see the file COPYING.LIB.  If not, write to
0018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0019     Boston, MA 02110-1301, USA.
0020 */
0021 
0022 #define KDE_EXTENDED_DEBUG_OUTPUT
0023 
0024 #ifndef QT_NO_CAST_FROM_ASCII
0025 #define QT_NO_CAST_FROM_ASCII
0026 #endif
0027 #ifndef QT_NO_CAST_TO_ASCII
0028 #define QT_NO_CAST_TO_ASCII
0029 #endif
0030 #ifndef KDE3_SUPPORT
0031 #define KDE3_SUPPORT
0032 #endif
0033 
0034 #include "kdebug.h"
0035 #include <config-io.h>
0036 #include <QThreadStorage>
0037 
0038 #ifdef Q_OS_WIN
0039 #include <fcntl.h>
0040 #include <windows.h>
0041 #ifndef _WIN32_WCE
0042 #include <wincon.h>
0043 #endif
0044 #else
0045 #include <unistd.h>
0046 #include <stdio.h>
0047 #endif
0048 
0049 #ifdef NDEBUG
0050 #undef kDebug
0051 #undef kBacktrace
0052 #endif
0053 
0054 #if HAVE_SYS_TIME_H
0055 #include <sys/time.h>
0056 #endif
0057 #if HAVE_TIME_H
0058 #include <time.h>
0059 #endif
0060 
0061 #include "kdatetime.h"
0062 
0063 #include <kmessage.h>
0064 #include <klocalizedstring.h>
0065 #include <kconfiggroup.h>
0066 
0067 #include <QFile>
0068 #include <QHash>
0069 #include <QObject>
0070 #include <QChar>
0071 #include <QCoreApplication>
0072 #include <QUrl>
0073 #include <qstandardpaths.h>
0074 #include <qlogging.h>
0075 
0076 #include <stdlib.h> // abort
0077 #include <unistd.h> // getpid
0078 #include <stdarg.h> // vararg stuff
0079 #include <ctype.h>      // isprint
0080 #include <syslog.h>
0081 #include <errno.h>
0082 #include <string.h>
0083 #include <kconfig.h>
0084 
0085 #ifdef Q_OS_SOLARIS
0086 // For the purposes of KDebug Solaris has a GNU-libc-compatible
0087 // backtrace() function. This isn't detected by the CMake checks
0088 // normally (check_function_exists fails), but we know it's there.
0089 // For better results, we would use walk_context(), but that's
0090 // a little more code -- see also the crash handler in kcrash.cpp .
0091 #define HAVE_BACKTRACE (1)
0092 #endif
0093 
0094 #if HAVE_BACKTRACE
0095 #include <execinfo.h>
0096 #ifdef __GNUC__
0097 #define HAVE_BACKTRACE_DEMANGLE
0098 #include <cxxabi.h>
0099 #endif
0100 #endif
0101 
0102 #include "kdebugdbusiface_p.h"
0103 #include <QMutex>
0104 
0105 KDELIBS4SUPPORT_DEPRECATED_EXPORT bool kde_kdebug_enable_dbus_interface = false;
0106 
0107 class KNoDebugStream: public QIODevice
0108 {
0109     // Q_OBJECT
0110 public:
0111     KNoDebugStream()
0112     {
0113         open(WriteOnly);
0114     }
0115     bool isSequential() const override
0116     {
0117         return true;
0118     }
0119     qint64 readData(char *, qint64) override
0120     {
0121         return 0; /* eof */
0122     }
0123     qint64 readLineData(char *, qint64) override
0124     {
0125         return 0; /* eof */
0126     }
0127     qint64 writeData(const char *, qint64 len) override
0128     {
0129         return len;
0130     }
0131 
0132     void setContext(const char *debugFile, int line,
0133                     const char *funcinfo, const QByteArray &areaName)
0134     {
0135         context.file = debugFile;
0136         context.line = line;
0137         context.function = funcinfo;
0138         category = areaName; // for storage
0139         context.category = category.constData();
0140     }
0141 protected:
0142     QMessageLogContext context;
0143     QByteArray category;
0144 };
0145 
0146 class KSyslogDebugStream: public KNoDebugStream
0147 {
0148     // Q_OBJECT
0149 public:
0150     qint64 writeData(const char *data, qint64 len) override
0151     {
0152         if (len) {
0153             // not using fromRawData because we need a terminating NUL
0154             const QByteArray buf(data, len);
0155             syslog(m_priority, "%s", buf.constData());
0156         }
0157         return len;
0158     }
0159     void setPriority(int priority)
0160     {
0161         m_priority = priority;
0162     }
0163 private:
0164     int m_priority;
0165 };
0166 
0167 class KFileDebugStream: public KNoDebugStream
0168 {
0169     // Q_OBJECT
0170 public:
0171     qint64 writeData(const char *data, qint64 len) override
0172     {
0173         if (len) {
0174             QFile aOutputFile(m_fileName);
0175             if (aOutputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered)) {
0176                 QByteArray buf = QByteArray::fromRawData(data, len);
0177 
0178                 // Apply QT_MESSAGE_PATTERN
0179                 const QString formatted = qFormatLogMessage(QtDebugMsg /*hack*/, context, QString::fromUtf8(buf));
0180                 buf = formatted.toUtf8();
0181 
0182                 aOutputFile.write(buf.trimmed());
0183                 aOutputFile.putChar('\n');
0184             }
0185         }
0186         return len;
0187     }
0188     void setFileName(const QString &fn)
0189     {
0190         m_fileName = fn;
0191     }
0192 private:
0193     QString m_fileName;
0194 };
0195 
0196 class KMessageBoxDebugStream: public KNoDebugStream
0197 {
0198     // Q_OBJECT
0199 public:
0200     qint64 writeData(const char *data, qint64 len) override
0201     {
0202         if (len) {
0203             // Since we are in kdecore here, we cannot use KMsgBox
0204             QString msg = QString::fromLatin1(data, len);
0205             KMessage::message(KMessage::Information, msg, m_caption);
0206         }
0207         return len;
0208     }
0209     void setCaption(const QString &h)
0210     {
0211         m_caption = h;
0212     }
0213 private:
0214     QString m_caption;
0215 };
0216 
0217 class KLineEndStrippingDebugStream: public KNoDebugStream
0218 {
0219     // Q_OBJECT
0220 public:
0221     qint64 writeData(const char *data, qint64 len) override
0222     {
0223         QByteArray buf = QByteArray::fromRawData(data, len);
0224         qt_message_output(QtDebugMsg,
0225                           context,
0226                           QString::fromLocal8Bit(buf.trimmed()));
0227         return len;
0228     }
0229 };
0230 
0231 struct KDebugPrivate {
0232     enum OutputMode {
0233         FileOutput = 0,
0234         MessageBoxOutput = 1,
0235         QtOutput = 2,
0236         SyslogOutput = 3,
0237         NoOutput = 4,
0238         DefaultOutput = QtOutput, // if you change DefaultOutput, also change the defaults in kdebugdialog!
0239         Unknown = 5
0240     };
0241 
0242     struct Area {
0243         inline Area()
0244         {
0245             clear();
0246         }
0247         void clear(OutputMode set = Unknown)
0248         {
0249             for (int i = 0; i < 4; ++i) {
0250                 logFileName[i].clear();
0251                 mode[i] = set;
0252             }
0253         }
0254 
0255         QByteArray name;
0256         QString logFileName[4];
0257         OutputMode mode[4];
0258     };
0259     typedef QHash<unsigned int, Area> Cache;
0260 
0261     KDebugPrivate()
0262         : config(nullptr), kDebugDBusIface(nullptr), m_disableAll(false)
0263     {
0264         Q_ASSERT(int(QtDebugMsg) == 0);
0265         Q_ASSERT(int(QtFatalMsg) == 3);
0266 
0267         // Create the D-Bus interface if it has not been created yet
0268         // But only register to D-Bus if we are in a process with a D-Bus event loop,
0269         // otherwise introspection will just hang.
0270         // Examples of processes without a D-Bus event loop: kioslaves and the main kdeinit process.
0271         //
0272         // How to know that we have a real event loop? That's tricky.
0273         // We could delay registration in kDebugDBusIface with a QTimer, but
0274         // it would still get triggered by kioslaves that use enterLoop/exitLoop
0275         // to run kio jobs synchronously.
0276         //
0277         // Solution: we have a bool that is set by KApplication
0278         // (kioslaves should use QCoreApplication but not KApplication).
0279         if (kde_kdebug_enable_dbus_interface) {
0280             kDebugDBusIface = new KDebugDBusIface;
0281         }
0282 
0283         for (int i = 0; i < 8; i++) {
0284             m_nullOutputYesNoCache[i] = -1;
0285         }
0286 
0287     }
0288 
0289     ~KDebugPrivate()
0290     {
0291         delete config;
0292         delete kDebugDBusIface;
0293     }
0294 
0295     void loadAreaNames()
0296     {
0297         // Don't clear the cache here, that would lose previously registered dynamic areas
0298         //cache.clear();
0299 
0300         Area &areaData = cache[0];
0301         areaData.clear();
0302 
0303         areaData.name = qApp ? QCoreApplication::applicationName().toUtf8() : QByteArray("unnamed app");
0304         //qDebug() << "loadAreaNames: area 0 has name" << areaData.name;
0305 
0306         for (int i = 0; i < 8; i++) {
0307             m_nullOutputYesNoCache[i] = -1;
0308         }
0309 
0310         QString filename(QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QLatin1String("kdebug.areas")));
0311         if (!QFile::exists(filename)) {
0312             return;
0313         }
0314         QFile file(filename);
0315         if (!file.open(QIODevice::ReadOnly)) {
0316             qWarning("Couldn't open %s", filename.toLocal8Bit().constData());
0317             file.close();
0318             return;
0319         }
0320 
0321         uint lineNumber = 0;
0322 
0323         while (!file.atEnd()) {
0324             const QByteArray line = file.readLine().trimmed();
0325             ++lineNumber;
0326             if (line.isEmpty()) {
0327                 continue;
0328             }
0329 
0330             int i = 0;
0331             unsigned char ch = line[i];
0332 
0333             if (ch == '#') {
0334                 continue;    // We have an eof, a comment or an empty line
0335             }
0336 
0337             if (ch < '0' || ch > '9') {
0338                 qWarning("Syntax error parsing '%s': no number (line %u)", qPrintable(filename), lineNumber);
0339                 continue;
0340             }
0341 
0342             do {
0343                 ch = line[++i];
0344             } while (ch >= '0' && ch <= '9' && i < line.length());
0345 
0346             unsigned int number = line.left(i).toUInt();
0347 
0348             while (i < line.length() && line[i] <= ' ') {
0349                 i++;
0350             }
0351 
0352             Area areaData;
0353             areaData.name = line.mid(i);
0354             cache.insert(number, areaData);
0355         }
0356         file.close();
0357     }
0358 
0359     inline int level(QtMsgType type)
0360     {
0361         return int(type) - int(QtDebugMsg);
0362     }
0363 
0364     QString groupNameForArea(unsigned int area) const
0365     {
0366         QString groupName = QString::number(area);
0367         if (area == 0 || !config->hasGroup(groupName)) {
0368             groupName = QString::fromLocal8Bit(cache.value(area).name);
0369         }
0370         return groupName;
0371     }
0372 
0373     OutputMode areaOutputMode(QtMsgType type, unsigned int area, bool enableByDefault)
0374     {
0375         if (!configObject()) {
0376             return QtOutput;
0377         }
0378 
0379         QString key;
0380         switch (type) {
0381         case QtDebugMsg:
0382             key = QLatin1String("InfoOutput");
0383             if (m_disableAll) {
0384                 return NoOutput;
0385             }
0386             break;
0387         case QtWarningMsg:
0388             key = QLatin1String("WarnOutput");
0389             break;
0390         case QtFatalMsg:
0391             key = QLatin1String("FatalOutput");
0392             break;
0393         case QtCriticalMsg:
0394         default:
0395             /* Programmer error, use "Error" as default */
0396             key = QLatin1String("ErrorOutput");
0397             break;
0398         }
0399 
0400         const KConfigGroup cg(config, groupNameForArea(area));
0401         const int mode = cg.readEntry(key, int(enableByDefault ? DefaultOutput : NoOutput));
0402         return OutputMode(mode);
0403     }
0404 
0405     QString logFileName(QtMsgType type, unsigned int area)
0406     {
0407         if (!configObject()) {
0408             return QString();
0409         }
0410 
0411         const char *aKey;
0412         switch (type) {
0413         case QtDebugMsg:
0414             aKey = "InfoFilename";
0415             break;
0416         case QtWarningMsg:
0417             aKey = "WarnFilename";
0418             break;
0419         case QtFatalMsg:
0420             aKey = "FatalFilename";
0421             break;
0422         case QtCriticalMsg:
0423         default:
0424             aKey = "ErrorFilename";
0425             break;
0426         }
0427 
0428         KConfigGroup cg(config, groupNameForArea(area));
0429         return cg.readPathEntry(aKey, QLatin1String("kdebug.dbg"));
0430     }
0431 
0432     KConfig *configObject()
0433     {
0434         if (!config) {
0435             config = new KConfig(QLatin1String("kdebugrc"), KConfig::NoGlobals);
0436             m_disableAll = config->group(QString()).readEntry("DisableAll", false);
0437         }
0438         return config;
0439     }
0440 
0441     Cache::Iterator areaData(QtMsgType type, unsigned int num, bool enableByDefault = true)
0442     {
0443         if (!cache.contains(0)) {
0444             //qDebug() << "cache size=" << cache.count() << "loading area names";
0445             loadAreaNames(); // fills 'cache'
0446             Q_ASSERT(cache.contains(0));
0447         }
0448 
0449         Cache::Iterator it = cache.find(num);
0450         if (it == cache.end()) {
0451             // unknown area
0452             Q_ASSERT(cache.contains(0));
0453             it = cache.find(0);
0454             num = 0;
0455         }
0456 
0457         if (num == 0) { // area 0 is special, it becomes the named area "appname"
0458             static bool s_firstDebugFromApplication = true;
0459             if (s_firstDebugFromApplication && !m_disableAll) {
0460                 s_firstDebugFromApplication = false;
0461                 //qDebug() << "First debug output from" << it->name << "writing out with default" << enableByDefault;
0462                 writeGroupForNamedArea(it->name, enableByDefault);
0463             }
0464         }
0465 
0466         const int lev = level(type);
0467         //qDebug() << "in cache for" << num << ":" << it->mode[lev];
0468         if (it->mode[lev] == Unknown) {
0469             it->mode[lev] = areaOutputMode(type, num, enableByDefault);
0470         }
0471         if (it->mode[lev] == FileOutput && it->logFileName[lev].isEmpty()) {
0472             it->logFileName[lev] = logFileName(type, num);
0473         }
0474 
0475         Q_ASSERT(it->mode[lev] != Unknown);
0476 
0477         return it;
0478     }
0479 
0480     QDebug setupFileWriter(const QString &fileName)
0481     {
0482         if (!filewriter.hasLocalData()) {
0483             filewriter.setLocalData(new KFileDebugStream);
0484         }
0485         filewriter.localData()->setFileName(fileName);
0486         QDebug result(filewriter.localData());
0487         return result;
0488     }
0489 
0490     QDebug setupMessageBoxWriter(QtMsgType type, const QByteArray &areaName)
0491     {
0492         if (!messageboxwriter.hasLocalData()) {
0493             messageboxwriter.setLocalData(new KMessageBoxDebugStream);
0494         }
0495         QDebug result(messageboxwriter.localData());
0496         QByteArray header;
0497 
0498         switch (type) {
0499         case QtDebugMsg:
0500             header = "Info";
0501             break;
0502         case QtWarningMsg:
0503             header = "Warning";
0504             break;
0505         case QtFatalMsg:
0506             header = "Fatal Error";
0507             break;
0508         case QtCriticalMsg:
0509         default:
0510             header = "Error";
0511             break;
0512         }
0513 
0514         if (!areaName.isEmpty()) {
0515             header += " (";
0516             header += areaName;
0517             header += ')';
0518         }
0519         messageboxwriter.localData()->setCaption(QString::fromLatin1(header));
0520         return result;
0521     }
0522 
0523     QDebug setupSyslogWriter(QtMsgType type)
0524     {
0525         if (!syslogwriter.hasLocalData()) {
0526             syslogwriter.setLocalData(new KSyslogDebugStream);
0527         }
0528         QDebug result(syslogwriter.localData());
0529         int level = 0;
0530 
0531         switch (type) {
0532         case QtDebugMsg:
0533             level = LOG_INFO;
0534             break;
0535         case QtWarningMsg:
0536             level = LOG_WARNING;
0537             break;
0538         case QtFatalMsg:
0539             level = LOG_CRIT;
0540             break;
0541         case QtCriticalMsg:
0542         default:
0543             level = LOG_ERR;
0544             break;
0545         }
0546         syslogwriter.localData()->setPriority(level);
0547         return result;
0548     }
0549 
0550     QDebug setupQtWriter(QtMsgType type)
0551     {
0552         if (type == QtWarningMsg) {
0553             // KDE warnings are not the same thing as Qt warnings
0554             // in Qt, warnings indicate bad code, which must be corrected before the release
0555             // in KDE, it's just something that everyone sees (including users)
0556             type = QtDebugMsg;
0557         }
0558         if (type != QtDebugMsg) {
0559             return QDebug(type);
0560         }
0561         return QDebug(&lineendstrippingwriter);
0562     }
0563 
0564     QDebug printHeader(QDebug s)
0565     {
0566 #ifdef KDE_EXTENDED_DEBUG_OUTPUT
0567         static int printTimeStamp = qEnvironmentVariableIntValue("KDE_DEBUG_TIMESTAMP");
0568         //s = s.nospace();
0569         if (printTimeStamp > 0) {
0570             if (printTimeStamp >= 2) {
0571                 // the extended print: 17:03:24.123
0572                 const QString sformat = QString::fromLatin1("hh:mm:ss.zzz");
0573                 s << qPrintable(QDateTime::currentDateTime().time().toString(sformat));
0574             } else {
0575                 // the default print: 17:03:24
0576                 s << qPrintable(QDateTime::currentDateTime().time().toString());
0577             }
0578             //s << ' ';
0579         }
0580 
0581         if (m_indentString.hasLocalData()) {
0582             s.setAutoInsertSpaces(false);
0583             s << m_indentString.localData()->toLatin1().constData();
0584             s.setAutoInsertSpaces(true);
0585         }
0586 
0587 #if 0 // This is in Qt now, see %{function} in QT_MESSAGE_PATTERN (qlogging.cpp). Only the coloring is missing (TODO Qt-5.1)
0588         if (funcinfo && printMethodName) {
0589             if (colored) {
0590                 if (type <= QtDebugMsg) {
0591                     s << "\033[0;34m";    //blue
0592                 } else {
0593                     s << "\033[0;31m";    //red
0594                 }
0595             }
0596 # ifdef Q_CC_GNU
0597             // strip the function info down to the base function name
0598             // note that this throws away the template definitions,
0599             // the parameter types (overloads) and any const/volatile qualifiers
0600             QByteArray info = funcinfo;
0601             int pos = info.indexOf('(');
0602             Q_ASSERT_X(pos != -1, "kDebug",
0603                        "Bug in kDebug(): I don't know how to parse this function name");
0604             while (info.at(pos - 1) == ' ')
0605                 // that '(' we matched was actually the opening of a function-pointer
0606             {
0607                 pos = info.indexOf('(', pos + 1);
0608             }
0609 
0610             info.truncate(pos);
0611             // gcc 4.1.2 don't put a space between the return type and
0612             // the function name if the function is in an anonymous namespace
0613             int index = 1;
0614             forever {
0615             index = info.indexOf("<unnamed>::", index);
0616                 if (index == -1)
0617                 {
0618                     break;
0619                 }
0620 
0621                 if (info.at(index - 1) != ':')
0622                 {
0623                     info.insert(index, ' ');
0624                 }
0625 
0626                 index += strlen("<unnamed>::");
0627             }
0628             pos = info.lastIndexOf(' ');
0629             if (pos != -1) {
0630                 int startoftemplate = info.lastIndexOf('<');
0631                 if (startoftemplate != -1 && pos > startoftemplate &&
0632                         pos < info.lastIndexOf(">::"))
0633                     // we matched a space inside this function's template definition
0634                 {
0635                     pos = info.lastIndexOf(' ', startoftemplate);
0636                 }
0637             }
0638 
0639             if (pos + 1 == info.length())
0640                 // something went wrong, so gracefully bail out
0641             {
0642                 s << " " << funcinfo;
0643             } else {
0644                 s << " " << info.constData() + pos + 1;
0645             }
0646 # else
0647             s << " " << funcinfo;
0648 # endif
0649             if (colored) {
0650                 s  << "\033[0m";
0651             }
0652         }
0653 
0654         s << ":";
0655         s.space();
0656 #endif
0657 #else // KDE_EXTENDED_DEBUG_OUTPUT
0658         Q_UNUSED(funcinfo);
0659         if (!areaName.isEmpty()) {
0660             s.nospace();
0661             s << areaName.constData() << ':';
0662             s.space();
0663         }
0664 #endif
0665         return s;
0666     }
0667 
0668     QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line,
0669                   const char *funcinfo)
0670     {
0671         Cache::Iterator it = areaData(type, area);
0672         OutputMode mode = it->mode[level(type)];
0673         Q_ASSERT(mode != Unknown);
0674         QString file = it->logFileName[level(type)];
0675 
0676         QByteArray areaName = it->name;
0677         //if (areaName.isEmpty())
0678         //    areaName = cache.value(0).name;
0679 
0680         QDebug s(&devnull);
0681         switch (mode) {
0682         case FileOutput:
0683             s = setupFileWriter(file);
0684             filewriter.localData()->setContext(debugFile, line, funcinfo, areaName);
0685             break;
0686         case MessageBoxOutput:
0687             s = setupMessageBoxWriter(type, areaName);
0688             break;
0689         case SyslogOutput:
0690             s = setupSyslogWriter(type);
0691             break;
0692         case NoOutput:
0693             s = QDebug(&devnull);
0694             return s; //no need to take the time to "print header" if we don't want to output anyway
0695             break;
0696         case Unknown: // should not happen
0697         default:                // QtOutput
0698             lineendstrippingwriter.setContext(debugFile, line, funcinfo, areaName);
0699             s = setupQtWriter(type);
0700             break;
0701         }
0702 
0703         return printHeader(s);
0704     }
0705 
0706     void writeGroupForNamedArea(const QByteArray &areaName, bool enabled)
0707     {
0708         // Ensure that this area name appears in kdebugrc, so that users (via kdebugdialog)
0709         // can turn it off.
0710         KConfig *cfgObj = configObject();
0711         if (cfgObj) {
0712             KConfigGroup cg(cfgObj, QString::fromUtf8(areaName));
0713             const QString key = QString::fromLatin1("InfoOutput");
0714             if (!cg.hasKey(key)) {
0715                 cg.writeEntry(key, int(enabled ? KDebugPrivate::QtOutput : KDebugPrivate::NoOutput));
0716                 cg.sync();
0717             }
0718         }
0719     }
0720 
0721     QMutex mutex;
0722     KConfig *config;
0723     KDebugDBusIface *kDebugDBusIface;
0724     Cache cache;
0725     bool m_disableAll;
0726     int m_nullOutputYesNoCache[8];
0727 
0728     KNoDebugStream devnull;
0729     QThreadStorage<QString *> m_indentString;
0730     QThreadStorage<KSyslogDebugStream *> syslogwriter;
0731     QThreadStorage<KFileDebugStream *> filewriter;
0732     QThreadStorage<KMessageBoxDebugStream *> messageboxwriter;
0733     KLineEndStrippingDebugStream lineendstrippingwriter;
0734 };
0735 
0736 Q_GLOBAL_STATIC(KDebugPrivate, kDebug_data)
0737 
0738 #if HAVE_BACKTRACE
0739 static QString maybeDemangledName(char *name)
0740 {
0741 #ifdef HAVE_BACKTRACE_DEMANGLE
0742     const int len = strlen(name);
0743     QByteArray in = QByteArray::fromRawData(name, len);
0744     const int mangledNameStart = in.indexOf("(_");
0745     if (mangledNameStart >= 0) {
0746         const int mangledNameEnd = in.indexOf('+', mangledNameStart + 2);
0747         if (mangledNameEnd >= 0) {
0748             int status;
0749             // if we forget about this line and the one that undoes its effect we don't change the
0750             // internal data of the QByteArray::fromRawData() ;)
0751             name[mangledNameEnd] = 0;
0752             char *demangled = abi::__cxa_demangle(name + mangledNameStart + 1, nullptr, nullptr, &status);
0753             name[mangledNameEnd] = '+';
0754             if (demangled) {
0755                 QString ret = QString::fromLatin1(name, mangledNameStart + 1) +
0756                               QString::fromLatin1(demangled) +
0757                               QString::fromLatin1(name + mangledNameEnd, len - mangledNameEnd);
0758                 free(demangled);
0759                 return ret;
0760             }
0761         }
0762     }
0763 #endif
0764     return QString::fromLatin1(name);
0765 }
0766 #endif
0767 
0768 QString kRealBacktrace(int levels)
0769 {
0770     QString s;
0771 #if HAVE_BACKTRACE
0772     void *trace[256];
0773     int n = backtrace(trace, 256);
0774     if (!n) {
0775         return s;
0776     }
0777     char **strings = backtrace_symbols(trace, n);
0778 
0779     if (levels != -1) {
0780         n = qMin(n, levels);
0781     }
0782     s = QLatin1String("[\n");
0783 
0784     for (int i = 0; i < n; ++i)
0785         s += QString::number(i) + QLatin1String(": ") +
0786              maybeDemangledName(strings[i]) + QLatin1Char('\n');
0787     s += QLatin1String("]\n");
0788     if (strings) {
0789         free(strings);
0790     }
0791 #endif
0792     return s;
0793 }
0794 
0795 QDebug kDebugDevNull()
0796 {
0797     return QDebug(&kDebug_data()->devnull);
0798 }
0799 
0800 QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
0801 {
0802     if (kDebug_data.isDestroyed()) {
0803         // we don't know what to return now...
0804         qCritical().nospace() << "kDebugStream called after destruction (from "
0805                               << (funcinfo ? funcinfo : "")
0806                               << (file ? " file " : " unknown file")
0807                               << (file ? file : "")
0808                               << " line " << line << ")";
0809         return QDebug(level);
0810     }
0811 
0812     QMutexLocker locker(&kDebug_data()->mutex);
0813     return kDebug_data()->stream(level, area, file, line, funcinfo);
0814 }
0815 
0816 QDebug perror(QDebug s, KDebugTag)
0817 {
0818     return s << QString::fromLocal8Bit(strerror(errno));
0819 }
0820 
0821 QDebug operator<<(QDebug s, const KDateTime &time)
0822 {
0823     if (time.isDateOnly()) {
0824         s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")";
0825     } else {
0826         s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")";
0827     }
0828     return s.space();
0829 }
0830 
0831 void kClearDebugConfig()
0832 {
0833     if (!kDebug_data) {
0834         return;
0835     }
0836     KDebugPrivate *d = kDebug_data;
0837     QMutexLocker locker(&d->mutex);
0838     delete d->config;
0839     d->config = nullptr;
0840 
0841     KDebugPrivate::Cache::Iterator it = d->cache.begin(),
0842                                    end = d->cache.end();
0843     for (; it != end; ++it) {
0844         it->clear();
0845     }
0846 
0847     for (int i = 0; i < 8; i++) {
0848         d->m_nullOutputYesNoCache[i] = -1;
0849     }
0850 }
0851 
0852 // static
0853 bool KDebug::hasNullOutput(QtMsgType type,
0854                            bool condition,
0855                            int area,
0856                            bool enableByDefault)
0857 {
0858     if (!condition) {
0859         return true;
0860     }
0861     if (kDebug_data.isDestroyed()) {
0862         // kDebugStream() will generate a warning anyway, so we don't.
0863         return false;
0864     }
0865     KDebugPrivate *const d = kDebug_data;
0866     QMutexLocker locker(&d->mutex);
0867 
0868     if (type == QtDebugMsg) {
0869         int *entries = d->m_nullOutputYesNoCache;
0870         for (int i = 0; i < 8; i += 2) {
0871             if (entries[i] == area) {
0872                 return entries[i + 1];
0873             }
0874         }
0875     }
0876 
0877     KDebugPrivate::Cache::Iterator it = d->areaData(type, area, enableByDefault);
0878     const bool ret = it->mode[d->level(type)] == KDebugPrivate::NoOutput;
0879 
0880     // cache result for next time...
0881     if (type == QtDebugMsg) {
0882         int *entries = d->m_nullOutputYesNoCache;
0883         int idx = (qrand() % 4) * 2;
0884         entries[idx] = area;
0885         entries[idx + 1] = ret;
0886     }
0887 
0888     return ret;
0889 }
0890 
0891 int KDebug::registerArea(const QByteArray &areaName, bool enabled)
0892 {
0893     // TODO for optimization: static int s_lastAreaNumber = 1;
0894     KDebugPrivate *d = kDebug_data;
0895     QMutexLocker locker(&d->mutex);
0896     int areaNumber = 1;
0897     while (d->cache.contains(areaNumber)) {
0898         ++areaNumber;
0899     }
0900     KDebugPrivate::Area areaData;
0901     areaData.name = areaName;
0902     //qDebug() << "Assigning area number" << areaNumber << "for name" << areaName;
0903     d->cache.insert(areaNumber, areaData);
0904     d->writeGroupForNamedArea(areaName, enabled);
0905     return areaNumber;
0906 }
0907 
0908 #ifndef KDE_NO_DEBUG_OUTPUT
0909 
0910 class Q_DECL_HIDDEN KDebug::Block::Private
0911 {
0912 public:
0913     QByteArray m_label;
0914 };
0915 
0916 KDebug::Block::Block(const char *label, int area)
0917     : m_area(area), d(nullptr)
0918 {
0919     if (hasNullOutputQtDebugMsg(area)) {
0920         d = nullptr; // remember, for the dtor
0921     } else {
0922         d = new Private;
0923         d->m_label = label;
0924         m_startTime.start();
0925         kDebug(area) << "BEGIN:" << label;
0926 
0927         // The indent string is per thread
0928         QThreadStorage<QString *> &indentString = kDebug_data()->m_indentString;
0929         if (!indentString.hasLocalData()) {
0930             indentString.setLocalData(new QString);
0931         }
0932         *(indentString.localData()) += QLatin1String("  ");
0933     }
0934 }
0935 
0936 KDebug::Block::~Block()
0937 {
0938     if (d) {
0939         const double duration = m_startTime.elapsed() / 1000.0;
0940         QThreadStorage<QString *> &indentString = kDebug_data()->m_indentString;
0941         indentString.localData()->chop(2);
0942 
0943         // Print timing information, and a special message (DELAY) if the method took longer than 5s
0944         if (duration < 5.0) {
0945             kDebug(m_area)
0946                     << "END__:"
0947                     << d->m_label.constData()
0948                     << qPrintable(QString::fromLatin1("[Took: %3s]").arg(QString::number(duration, 'g', 2)));
0949         } else {
0950             kDebug(m_area)
0951                     << "END__:"
0952                     << d->m_label.constData()
0953                     << qPrintable(QString::fromLatin1("[DELAY Took (quite long) %3s]").arg(QString::number(duration, 'g', 2)));
0954         }
0955         delete d;
0956     }
0957 }
0958 
0959 #endif