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 #include <QThreadStorage> 00035 00036 #ifdef Q_WS_WIN 00037 #include <fcntl.h> 00038 #include <windows.h> 00039 #ifndef _WIN32_WCE 00040 #include <wincon.h> 00041 #endif 00042 #else 00043 #include <unistd.h> 00044 #include <stdio.h> 00045 #endif 00046 00047 #ifdef NDEBUG 00048 #undef kDebug 00049 #undef kBacktrace 00050 #endif 00051 00052 #include <config.h> 00053 00054 #ifdef HAVE_SYS_TIME_H 00055 #include <sys/time.h> 00056 #endif 00057 #ifdef HAVE_TIME_H 00058 #include <time.h> 00059 #endif 00060 00061 #include "kglobal.h" 00062 #include "kstandarddirs.h" 00063 #include "kdatetime.h" 00064 #include "kcmdlineargs.h" 00065 00066 #include <kmessage.h> 00067 #include <klocale.h> 00068 #include <kconfiggroup.h> 00069 #include <kurl.h> 00070 00071 #include <QtCore/QFile> 00072 #include <QtCore/QHash> 00073 #include <QtCore/QObject> 00074 #include <QtCore/QChar> 00075 #include <QtCore/QCoreApplication> 00076 00077 #include <stdlib.h> // abort 00078 #include <unistd.h> // getpid 00079 #include <stdarg.h> // vararg stuff 00080 #include <ctype.h> // isprint 00081 #include <syslog.h> 00082 #include <errno.h> 00083 #include <string.h> 00084 #include <kconfig.h> 00085 #include "kcomponentdata.h" 00086 00087 #ifdef Q_OS_SOLARIS 00088 // For the purposes of KDebug Solaris has a GNU-libc-compatible 00089 // backtrace() function. This isn't detected by the CMake checks 00090 // normally (check_function_exists fails), but we know it's there. 00091 // For better results, we would use walk_context(), but that's 00092 // a little more code -- see also the crash handler in kcrash.cpp . 00093 #define HAVE_BACKTRACE (1) 00094 #endif 00095 00096 #ifdef HAVE_BACKTRACE 00097 #include <execinfo.h> 00098 #ifdef __GNUC__ 00099 #define HAVE_BACKTRACE_DEMANGLE 00100 #include <cxxabi.h> 00101 #endif 00102 #endif 00103 00104 #include "kdebugdbusiface_p.h" 00105 #include <QMutex> 00106 00107 00108 00109 KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface = false; 00110 00111 class KNoDebugStream: public QIODevice 00112 { 00113 // Q_OBJECT 00114 public: 00115 KNoDebugStream() { open(WriteOnly); } 00116 bool isSequential() const { return true; } 00117 qint64 readData(char *, qint64) { return 0; /* eof */ } 00118 qint64 readLineData(char *, qint64) { return 0; /* eof */ } 00119 qint64 writeData(const char *, qint64 len) { return len; } 00120 }; 00121 00122 class KSyslogDebugStream: public KNoDebugStream 00123 { 00124 // Q_OBJECT 00125 public: 00126 qint64 writeData(const char *data, qint64 len) 00127 { 00128 if (len) { 00129 // not using fromRawData because we need a terminating NUL 00130 const QByteArray buf(data, len); 00131 syslog(m_priority, "%s", buf.constData()); 00132 } 00133 return len; 00134 } 00135 void setPriority(int priority) { m_priority = priority; } 00136 private: 00137 int m_priority; 00138 }; 00139 00140 class KFileDebugStream: public KNoDebugStream 00141 { 00142 // Q_OBJECT 00143 public: 00144 qint64 writeData(const char *data, qint64 len) 00145 { 00146 if (len) { 00147 QFile aOutputFile(m_fileName); 00148 aOutputFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered); 00149 QByteArray buf = QByteArray::fromRawData(data, len); 00150 aOutputFile.write(buf.trimmed()); 00151 aOutputFile.putChar('\n'); 00152 aOutputFile.close(); 00153 } 00154 return len; 00155 } 00156 void setFileName(const QString& fn) { m_fileName = fn; } 00157 private: 00158 QString m_fileName; 00159 }; 00160 00161 class KMessageBoxDebugStream: public KNoDebugStream 00162 { 00163 // Q_OBJECT 00164 public: 00165 qint64 writeData(const char *data, qint64 len) 00166 { 00167 if (len) { 00168 // Since we are in kdecore here, we cannot use KMsgBox 00169 QString msg = QString::fromAscii(data, len); 00170 KMessage::message(KMessage::Information, msg, m_caption); 00171 } 00172 return len; 00173 } 00174 void setCaption(const QString& h) { m_caption = h; } 00175 private: 00176 QString m_caption; 00177 }; 00178 00179 class KLineEndStrippingDebugStream: public KNoDebugStream 00180 { 00181 // Q_OBJECT 00182 public: 00183 qint64 writeData(const char *data, qint64 len) 00184 { 00185 QByteArray buf = QByteArray::fromRawData(data, len); 00186 qt_message_output(QtDebugMsg, buf.trimmed().constData()); 00187 return len; 00188 } 00189 }; 00190 00191 struct KDebugPrivate 00192 { 00193 enum OutputMode { 00194 FileOutput = 0, 00195 MessageBoxOutput = 1, 00196 QtOutput = 2, 00197 SyslogOutput = 3, 00198 NoOutput = 4, 00199 DefaultOutput = QtOutput, // if you change DefaultOutput, also change the defaults in kdebugdialog! 00200 Unknown = 5 00201 }; 00202 00203 struct Area { 00204 inline Area() { clear(); } 00205 void clear(OutputMode set = Unknown) 00206 { 00207 for (int i = 0; i < 4; ++i) { 00208 logFileName[i].clear(); 00209 mode[i] = set; 00210 } 00211 } 00212 00213 QByteArray name; 00214 QString logFileName[4]; 00215 OutputMode mode[4]; 00216 }; 00217 typedef QHash<unsigned int, Area> Cache; 00218 00219 KDebugPrivate() 00220 : config(0), kDebugDBusIface(0), m_disableAll(false), m_seenMainComponent(false) 00221 { 00222 Q_ASSERT(int(QtDebugMsg) == 0); 00223 Q_ASSERT(int(QtFatalMsg) == 3); 00224 00225 // Create the D-Bus interface if it has not been created yet 00226 // But only register to D-Bus if we are in a process with a D-Bus event loop, 00227 // otherwise introspection will just hang. 00228 // Examples of processes without a D-Bus event loop: kioslaves and the main kdeinit process. 00229 // 00230 // How to know that we have a real event loop? That's tricky. 00231 // We could delay registration in kDebugDBusIface with a QTimer, but 00232 // it would still get triggered by kioslaves that use enterLoop/exitLoop 00233 // to run kio jobs synchronously. 00234 // 00235 // Solution: we have a bool that is set by KApplication 00236 // (kioslaves should use QCoreApplication but not KApplication). 00237 if (kde_kdebug_enable_dbus_interface) { 00238 kDebugDBusIface = new KDebugDBusIface; 00239 } 00240 00241 for (int i = 0; i < 8; i++) { 00242 m_nullOutputYesNoCache[i] = -1; 00243 } 00244 00245 } 00246 00247 ~KDebugPrivate() 00248 { 00249 delete config; 00250 delete kDebugDBusIface; 00251 } 00252 00253 void loadAreaNames() 00254 { 00255 // Don't clear the cache here, that would lose previously registered dynamic areas 00256 //cache.clear(); 00257 00258 Area &areaData = cache[0]; 00259 areaData.clear(); 00260 00261 if (KGlobal::hasMainComponent()) { 00262 areaData.name = KGlobal::mainComponent().componentName().toUtf8(); 00263 m_seenMainComponent = true; 00264 } else { 00265 areaData.name = qApp ? qAppName().toUtf8() : QByteArray("unnamed app"); 00266 m_seenMainComponent = false; 00267 } 00268 //qDebug() << "loadAreaNames: area 0 has name" << areaData.name; 00269 00270 for (int i = 0; i < 8; i++) { 00271 m_nullOutputYesNoCache[i] = -1; 00272 } 00273 00274 QString filename(KStandardDirs::locate("config", QLatin1String("kdebug.areas"))); 00275 if (filename.isEmpty()) { 00276 return; 00277 } 00278 QFile file(filename); 00279 if (!file.open(QIODevice::ReadOnly)) { 00280 qWarning("Couldn't open %s", filename.toLocal8Bit().constData()); 00281 file.close(); 00282 return; 00283 } 00284 00285 uint lineNumber=0; 00286 00287 while (!file.atEnd()) { 00288 const QByteArray line = file.readLine().trimmed(); 00289 ++lineNumber; 00290 if (line.isEmpty()) 00291 continue; 00292 00293 int i=0; 00294 unsigned char ch=line[i]; 00295 00296 if (ch =='#') 00297 continue; // We have an eof, a comment or an empty line 00298 00299 if (ch < '0' && ch > '9') { 00300 qWarning("Syntax error: no number (line %u)",lineNumber); 00301 continue; 00302 } 00303 00304 do { 00305 ch=line[++i]; 00306 } while (ch >= '0' && ch <= '9' && i < line.length()); 00307 00308 unsigned int number = line.left(i).toUInt(); 00309 00310 while (i < line.length() && line[i] <= ' ') 00311 i++; 00312 00313 Area areaData; 00314 areaData.name = line.mid(i); 00315 cache.insert(number, areaData); 00316 } 00317 file.close(); 00318 } 00319 00320 inline int level(QtMsgType type) 00321 { return int(type) - int(QtDebugMsg); } 00322 00323 QString groupNameForArea(unsigned int area) const 00324 { 00325 QString groupName = QString::number(area); 00326 if (area == 0 || !config->hasGroup(groupName)) { 00327 groupName = QString::fromLocal8Bit(cache.value(area).name); 00328 } 00329 return groupName; 00330 } 00331 00332 OutputMode areaOutputMode(QtMsgType type, unsigned int area, bool enableByDefault) 00333 { 00334 if (!configObject()) 00335 return QtOutput; 00336 00337 QString key; 00338 switch (type) { 00339 case QtDebugMsg: 00340 key = QLatin1String( "InfoOutput" ); 00341 if (m_disableAll) 00342 return NoOutput; 00343 break; 00344 case QtWarningMsg: 00345 key = QLatin1String( "WarnOutput" ); 00346 break; 00347 case QtFatalMsg: 00348 key = QLatin1String( "FatalOutput" ); 00349 break; 00350 case QtCriticalMsg: 00351 default: 00352 /* Programmer error, use "Error" as default */ 00353 key = QLatin1String( "ErrorOutput" ); 00354 break; 00355 } 00356 00357 const KConfigGroup cg(config, groupNameForArea(area)); 00358 const int mode = cg.readEntry(key, int(enableByDefault ? DefaultOutput : NoOutput)); 00359 return OutputMode(mode); 00360 } 00361 00362 QString logFileName(QtMsgType type, unsigned int area) 00363 { 00364 if (!configObject()) 00365 return QString(); 00366 00367 const char* aKey; 00368 switch (type) 00369 { 00370 case QtDebugMsg: 00371 aKey = "InfoFilename"; 00372 break; 00373 case QtWarningMsg: 00374 aKey = "WarnFilename"; 00375 break; 00376 case QtFatalMsg: 00377 aKey = "FatalFilename"; 00378 break; 00379 case QtCriticalMsg: 00380 default: 00381 aKey = "ErrorFilename"; 00382 break; 00383 } 00384 00385 KConfigGroup cg(config, groupNameForArea(area)); 00386 return cg.readPathEntry(aKey, QLatin1String("kdebug.dbg")); 00387 } 00388 00389 KConfig* configObject() 00390 { 00391 if (!config) { 00392 config = new KConfig(QLatin1String("kdebugrc"), KConfig::NoGlobals); 00393 m_disableAll = config->group(QString()).readEntry("DisableAll", false); 00394 } 00395 return config; 00396 } 00397 00398 Cache::Iterator areaData(QtMsgType type, unsigned int num, bool enableByDefault = true) 00399 { 00400 if (!cache.contains(0)) { 00401 //qDebug() << "cache size=" << cache.count() << "loading area names"; 00402 loadAreaNames(); // fills 'cache' 00403 Q_ASSERT(cache.contains(0)); 00404 } else if (!m_seenMainComponent && KGlobal::hasMainComponent()) { 00405 // Update the name for area 0 once a main component exists 00406 cache[0].name = KGlobal::mainComponent().componentName().toUtf8(); 00407 m_seenMainComponent = true; 00408 } 00409 00410 Cache::Iterator it = cache.find(num); 00411 if (it == cache.end()) { 00412 // unknown area 00413 Q_ASSERT(cache.contains(0)); 00414 it = cache.find(0); 00415 num = 0; 00416 } 00417 00418 if (num == 0 && type == QtDebugMsg) { // area 0 is special, it becomes the named area "appname" 00419 static bool s_firstDebugFromApplication = true; 00420 if (s_firstDebugFromApplication && !m_disableAll) { 00421 s_firstDebugFromApplication = false; 00422 //qDebug() << "First debug output from" << it->name << "writing out with default" << enableByDefault; 00423 writeGroupForNamedArea(it->name, enableByDefault); 00424 } 00425 } 00426 00427 const int lev = level(type); 00428 //qDebug() << "in cache for" << num << ":" << it->mode[lev]; 00429 if (it->mode[lev] == Unknown) 00430 it->mode[lev] = areaOutputMode(type, num, enableByDefault); 00431 if (it->mode[lev] == FileOutput && it->logFileName[lev].isEmpty()) 00432 it->logFileName[lev] = logFileName(type, num); 00433 00434 Q_ASSERT(it->mode[lev] != Unknown); 00435 00436 return it; 00437 } 00438 00439 QDebug setupFileWriter(const QString &fileName) 00440 { 00441 if (!filewriter.hasLocalData()) 00442 filewriter.setLocalData(new KFileDebugStream); 00443 filewriter.localData()->setFileName(fileName); 00444 QDebug result(filewriter.localData()); 00445 return result; 00446 } 00447 00448 QDebug setupMessageBoxWriter(QtMsgType type, const QByteArray &areaName) 00449 { 00450 if (!messageboxwriter.hasLocalData()) 00451 messageboxwriter.setLocalData(new KMessageBoxDebugStream); 00452 QDebug result(messageboxwriter.localData()); 00453 QByteArray header; 00454 00455 switch (type) { 00456 case QtDebugMsg: 00457 header = "Info ("; 00458 break; 00459 case QtWarningMsg: 00460 header = "Warning ("; 00461 break; 00462 case QtFatalMsg: 00463 header = "Fatal Error ("; 00464 break; 00465 case QtCriticalMsg: 00466 default: 00467 header = "Error ("; 00468 break; 00469 } 00470 00471 header += areaName; 00472 header += ')'; 00473 messageboxwriter.localData()->setCaption(QString::fromAscii(header)); 00474 return result; 00475 } 00476 00477 QDebug setupSyslogWriter(QtMsgType type) 00478 { 00479 if (!syslogwriter.hasLocalData()) 00480 syslogwriter.setLocalData(new KSyslogDebugStream); 00481 QDebug result(syslogwriter.localData()); 00482 int level = 0; 00483 00484 switch (type) { 00485 case QtDebugMsg: 00486 level = LOG_INFO; 00487 break; 00488 case QtWarningMsg: 00489 level = LOG_WARNING; 00490 break; 00491 case QtFatalMsg: 00492 level = LOG_CRIT; 00493 break; 00494 case QtCriticalMsg: 00495 default: 00496 level = LOG_ERR; 00497 break; 00498 } 00499 syslogwriter.localData()->setPriority(level); 00500 return result; 00501 } 00502 00503 QDebug setupQtWriter(QtMsgType type) 00504 { 00505 if (type != QtDebugMsg) { 00506 if (type == QtWarningMsg) { 00507 // KDE warnings are not the same thing as Qt warnings 00508 // in Qt, warnings indicate bad code, which must be corrected before the release 00509 // in KDE, it's just something that everyone sees (including users) 00510 type = QtDebugMsg; 00511 } 00512 return QDebug(type); 00513 } 00514 return QDebug(&lineendstrippingwriter); 00515 } 00516 00517 QDebug printHeader(QDebug s, const QByteArray &areaName, const char * file, int line, const char *funcinfo, QtMsgType type, bool colored) 00518 { 00519 #ifdef KDE_EXTENDED_DEBUG_OUTPUT 00520 static bool printProcessInfo = (qgetenv("KDE_DEBUG_NOPROCESSINFO").isEmpty()); 00521 static bool printAreaName = (qgetenv("KDE_DEBUG_NOAREANAME").isEmpty()); 00522 static bool printMethodName = (qgetenv("KDE_DEBUG_NOMETHODNAME").isEmpty()); 00523 static bool printFileLine = (!qgetenv("KDE_DEBUG_FILELINE").isEmpty()); 00524 00525 static int printTimeStamp = qgetenv("KDE_DEBUG_TIMESTAMP").toInt(); 00526 QByteArray programName; 00527 s = s.nospace(); 00528 if (printTimeStamp > 0) { 00529 if (printTimeStamp >= 2) { 00530 // the extended print: 17:03:24.123 00531 const QString sformat = QString::fromLatin1("hh:mm:ss.zzz"); 00532 s << qPrintable(QDateTime::currentDateTime().time().toString(sformat)); 00533 } else { 00534 // the default print: 17:03:24 00535 s << qPrintable(QDateTime::currentDateTime().time().toString()); 00536 } 00537 s << ' '; 00538 } 00539 00540 if (printProcessInfo) { 00541 programName = cache.value(0).name; 00542 if (programName.isEmpty()) { 00543 if (QCoreApplication::instance()) 00544 programName = QCoreApplication::instance()->applicationName().toLocal8Bit(); 00545 else 00546 programName = "<unknown program name>"; 00547 } 00548 s << programName.constData() << "(" << unsigned(getpid()) << ")"; 00549 } 00550 if (printAreaName && (!printProcessInfo || areaName != programName)) { 00551 if (printProcessInfo) 00552 s << "/"; 00553 s << areaName.constData(); 00554 } 00555 00556 if (m_indentString.hasLocalData()) { 00557 s << m_indentString.localData()->toLatin1().constData(); 00558 } 00559 00560 if (printFileLine) { 00561 s << ' ' << file << ':' << line << ' '; 00562 } 00563 00564 if (funcinfo && printMethodName) { 00565 if (colored) { 00566 if (type <= QtDebugMsg) 00567 s << "\033[0;34m"; //blue 00568 else 00569 s << "\033[0;31m"; //red 00570 } 00571 # ifdef Q_CC_GNU 00572 // strip the function info down to the base function name 00573 // note that this throws away the template definitions, 00574 // the parameter types (overloads) and any const/volatile qualifiers 00575 QByteArray info = funcinfo; 00576 int pos = info.indexOf('('); 00577 Q_ASSERT_X(pos != -1, "kDebug", 00578 "Bug in kDebug(): I don't know how to parse this function name"); 00579 while (info.at(pos - 1) == ' ') 00580 // that '(' we matched was actually the opening of a function-pointer 00581 pos = info.indexOf('(', pos + 1); 00582 00583 info.truncate(pos); 00584 // gcc 4.1.2 don't put a space between the return type and 00585 // the function name if the function is in an anonymous namespace 00586 int index = 1; 00587 forever { 00588 index = info.indexOf("<unnamed>::", index); 00589 if ( index == -1 ) 00590 break; 00591 00592 if ( info.at(index-1) != ':' ) 00593 info.insert(index, ' '); 00594 00595 index += strlen("<unnamed>::"); 00596 } 00597 pos = info.lastIndexOf(' '); 00598 if (pos != -1) { 00599 int startoftemplate = info.lastIndexOf('<'); 00600 if (startoftemplate != -1 && pos > startoftemplate && 00601 pos < info.lastIndexOf(">::")) 00602 // we matched a space inside this function's template definition 00603 pos = info.lastIndexOf(' ', startoftemplate); 00604 } 00605 00606 if (pos + 1 == info.length()) 00607 // something went wrong, so gracefully bail out 00608 s << " " << funcinfo; 00609 else 00610 s << " " << info.constData() + pos + 1; 00611 # else 00612 s << " " << funcinfo; 00613 # endif 00614 if(colored) 00615 s << "\033[0m"; 00616 } 00617 00618 s << ":"; 00619 #else 00620 Q_UNUSED(funcinfo); 00621 if (!areaName.isEmpty()) 00622 s << areaName.constData() << ':'; 00623 #endif 00624 return s.space(); 00625 } 00626 00627 QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line, 00628 const char *funcinfo) 00629 { 00630 static bool env_colored = (!qgetenv("KDE_COLOR_DEBUG").isEmpty()); 00631 Cache::Iterator it = areaData(type, area); 00632 OutputMode mode = it->mode[level(type)]; 00633 Q_ASSERT(mode != Unknown); 00634 QString file = it->logFileName[level(type)]; 00635 QByteArray areaName = it->name; 00636 00637 if (areaName.isEmpty()) 00638 areaName = cache.value(0).name; 00639 00640 bool colored=false; 00641 00642 QDebug s(&devnull); 00643 switch (mode) { 00644 case FileOutput: 00645 s = setupFileWriter(file); 00646 break; 00647 case MessageBoxOutput: 00648 s = setupMessageBoxWriter(type, areaName); 00649 break; 00650 case SyslogOutput: 00651 s = setupSyslogWriter(type); 00652 break; 00653 case NoOutput: 00654 s = QDebug(&devnull); 00655 return s; //no need to take the time to "print header" if we don't want to output anyway 00656 break; 00657 case Unknown: // should not happen 00658 default: // QtOutput 00659 s = setupQtWriter(type); 00660 #ifndef Q_OS_WIN 00661 //only color if the debug goes to a tty. 00662 colored = env_colored && isatty(fileno(stderr)); 00663 #endif 00664 break; 00665 } 00666 00667 return printHeader(s, areaName, debugFile, line, funcinfo, type, colored); 00668 } 00669 00670 void writeGroupForNamedArea(const QByteArray& areaName, bool enabled) 00671 { 00672 // Ensure that this area name appears in kdebugrc, so that users (via kdebugdialog) 00673 // can turn it off. 00674 KConfig* cfgObj = configObject(); 00675 if (cfgObj) { 00676 KConfigGroup cg(cfgObj, QString::fromUtf8(areaName)); 00677 const QString key = QString::fromLatin1("InfoOutput"); 00678 if (!cg.hasKey(key)) { 00679 cg.writeEntry(key, int(enabled ? KDebugPrivate::QtOutput : KDebugPrivate::NoOutput)); 00680 cg.sync(); 00681 } 00682 } 00683 } 00684 00685 QMutex mutex; 00686 KConfig *config; 00687 KDebugDBusIface *kDebugDBusIface; 00688 Cache cache; 00689 bool m_disableAll; 00690 bool m_seenMainComponent; // false: area zero still contains qAppName 00691 int m_nullOutputYesNoCache[8]; 00692 00693 KNoDebugStream devnull; 00694 QThreadStorage<QString*> m_indentString; 00695 QThreadStorage<KSyslogDebugStream*> syslogwriter; 00696 QThreadStorage<KFileDebugStream*> filewriter; 00697 QThreadStorage<KMessageBoxDebugStream*> messageboxwriter; 00698 KLineEndStrippingDebugStream lineendstrippingwriter; 00699 }; 00700 00701 K_GLOBAL_STATIC(KDebugPrivate, kDebug_data) 00702 00703 #ifdef HAVE_BACKTRACE 00704 static QString maybeDemangledName(char *name) 00705 { 00706 #ifdef HAVE_BACKTRACE_DEMANGLE 00707 const int len = strlen(name); 00708 QByteArray in = QByteArray::fromRawData(name, len); 00709 const int mangledNameStart = in.indexOf("(_"); 00710 if (mangledNameStart >= 0) { 00711 const int mangledNameEnd = in.indexOf('+', mangledNameStart + 2); 00712 if (mangledNameEnd >= 0) { 00713 int status; 00714 // if we forget about this line and the one that undoes its effect we don't change the 00715 // internal data of the QByteArray::fromRawData() ;) 00716 name[mangledNameEnd] = 0; 00717 char *demangled = abi::__cxa_demangle(name + mangledNameStart + 1, 0, 0, &status); 00718 name[mangledNameEnd] = '+'; 00719 if (demangled) { 00720 QString ret = QString::fromLatin1(name, mangledNameStart + 1) + 00721 QString::fromLatin1(demangled) + 00722 QString::fromLatin1(name + mangledNameEnd, len - mangledNameEnd); 00723 free(demangled); 00724 return ret; 00725 } 00726 } 00727 } 00728 #endif 00729 return QString::fromLatin1(name); 00730 } 00731 #endif 00732 00733 QString kRealBacktrace(int levels) 00734 { 00735 QString s; 00736 #ifdef HAVE_BACKTRACE 00737 void* trace[256]; 00738 int n = backtrace(trace, 256); 00739 if (!n) 00740 return s; 00741 char** strings = backtrace_symbols (trace, n); 00742 00743 if ( levels != -1 ) 00744 n = qMin( n, levels ); 00745 s = QLatin1String("[\n"); 00746 00747 for (int i = 0; i < n; ++i) 00748 s += QString::number(i) + QLatin1String(": ") + 00749 maybeDemangledName(strings[i]) + QLatin1Char('\n'); 00750 s += QLatin1String("]\n"); 00751 if (strings) 00752 free (strings); 00753 #endif 00754 return s; 00755 } 00756 00757 QDebug kDebugDevNull() 00758 { 00759 return QDebug(&kDebug_data->devnull); 00760 } 00761 00762 QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo) 00763 { 00764 if (kDebug_data.isDestroyed()) { 00765 // we don't know what to return now... 00766 qCritical().nospace() << "kDebugStream called after destruction (from " 00767 << (funcinfo ? funcinfo : "") 00768 << (file ? " file " : " unknown file") 00769 << (file ? file :"") 00770 << " line " << line << ")"; 00771 return QDebug(level); 00772 } 00773 00774 QMutexLocker locker(&kDebug_data->mutex); 00775 return kDebug_data->stream(level, area, file, line, funcinfo); 00776 } 00777 00778 QDebug perror(QDebug s, KDebugTag) 00779 { 00780 return s << QString::fromLocal8Bit(strerror(errno)); 00781 } 00782 00783 QDebug operator<<(QDebug s, const KDateTime &time) 00784 { 00785 if ( time.isDateOnly() ) 00786 s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")"; 00787 else 00788 s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")"; 00789 return s.space(); 00790 } 00791 00792 QDebug operator<<(QDebug s, const KUrl &url) 00793 { 00794 s.nospace() << "KUrl(" << url.prettyUrl() << ")"; 00795 return s.space(); 00796 } 00797 00798 void kClearDebugConfig() 00799 { 00800 if (!kDebug_data) return; 00801 KDebugPrivate* d = kDebug_data; 00802 QMutexLocker locker(&d->mutex); 00803 delete d->config; 00804 d->config = 0; 00805 00806 KDebugPrivate::Cache::Iterator it = d->cache.begin(), 00807 end = d->cache.end(); 00808 for ( ; it != end; ++it) 00809 it->clear(); 00810 00811 for (int i = 0; i < 8; i++) { 00812 d->m_nullOutputYesNoCache[i] = -1; 00813 } 00814 } 00815 00816 // static 00817 bool KDebug::hasNullOutput(QtMsgType type, 00818 bool condition, 00819 int area, 00820 bool enableByDefault) 00821 { 00822 if (!condition) { 00823 return true; 00824 } 00825 if (kDebug_data.isDestroyed()) { 00826 // kDebugStream() will generate a warning anyway, so we don't. 00827 return false; 00828 } 00829 KDebugPrivate *const d = kDebug_data; 00830 QMutexLocker locker(&d->mutex); 00831 00832 if (type == QtDebugMsg) { 00833 int *entries = d->m_nullOutputYesNoCache; 00834 for (int i = 0; i < 8; i += 2) { 00835 if (entries[i] == area) { 00836 return entries[i + 1]; 00837 } 00838 } 00839 } 00840 00841 KDebugPrivate::Cache::Iterator it = d->areaData(type, area, enableByDefault); 00842 const bool ret = it->mode[d->level(type)] == KDebugPrivate::NoOutput; 00843 00844 // cache result for next time... 00845 if (type == QtDebugMsg) { 00846 int *entries = d->m_nullOutputYesNoCache; 00847 int idx = (qrand() % 4) * 2; 00848 entries[idx] = area; 00849 entries[idx + 1] = ret; 00850 } 00851 00852 return ret; 00853 } 00854 00855 int KDebug::registerArea(const QByteArray& areaName, bool enabled) 00856 { 00857 // TODO for optimization: static int s_lastAreaNumber = 1; 00858 KDebugPrivate* d = kDebug_data; 00859 QMutexLocker locker(&d->mutex); 00860 int areaNumber = 1; 00861 while (d->cache.contains(areaNumber)) { 00862 ++areaNumber; 00863 } 00864 KDebugPrivate::Area areaData; 00865 areaData.name = areaName; 00866 //qDebug() << "Assigning area number" << areaNumber << "for name" << areaName; 00867 d->cache.insert(areaNumber, areaData); 00868 d->writeGroupForNamedArea(areaName, enabled); 00869 return areaNumber; 00870 } 00871 00872 #ifndef KDE_NO_DEBUG_OUTPUT 00873 00874 KDebug::Block::Block(const char* label, int area) 00875 : m_label(label), m_area(area), d(0) 00876 { 00877 if (hasNullOutputQtDebugMsg(area)) { 00878 m_label = 0; // remember, for the dtor 00879 } else { 00880 m_startTime.start(); 00881 kDebug(area) << "BEGIN:" << label; 00882 00883 // The indent string is per thread 00884 QThreadStorage<QString*> & indentString = kDebug_data->m_indentString; 00885 if (!indentString.hasLocalData()) { 00886 indentString.setLocalData(new QString); 00887 } 00888 *(indentString.localData()) += QLatin1String(" "); 00889 } 00890 } 00891 00892 KDebug::Block::~Block() 00893 { 00894 if (m_label) { 00895 const double duration = m_startTime.elapsed() / 1000.0; 00896 QThreadStorage<QString*> & indentString = kDebug_data->m_indentString; 00897 indentString.localData()->chop(2); 00898 00899 // Print timing information, and a special message (DELAY) if the method took longer than 5s 00900 if (duration < 5.0) { 00901 kDebug(m_area) 00902 << "END__:" 00903 << m_label 00904 << qPrintable(QString::fromLatin1("[Took: %3s]").arg(QString::number(duration, 'g', 2))); 00905 } else { 00906 kDebug(m_area) 00907 << "END__:" 00908 << m_label 00909 << qPrintable(QString::fromLatin1("[DELAY Took (quite long) %3s]").arg(QString::number(duration, 'g', 2))); 00910 } 00911 } 00912 } 00913 00914 #endif
KDE 4.6 API Reference