File indexing completed on 2023-09-24 04:04:49
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