00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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>
00075 #include <unistd.h>
00076 #include <stdarg.h>
00077 #include <ctype.h>
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
00098 public:
00099 KNoDebugStream() { open(WriteOnly); }
00100 bool isSequential() const { return true; }
00101 qint64 readData(char *, qint64) { return 0; }
00102 qint64 readLineData(char *, qint64) { return 0; }
00103 qint64 writeData(const char *, qint64 len) { return len; }
00104 };
00105
00106 class KSyslogDebugStream: public KNoDebugStream
00107 {
00108
00109 public:
00110 qint64 writeData(const char *data, qint64 len)
00111 {
00112 if (len) {
00113 int nPriority = *data++;
00114
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
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
00147 public:
00148 qint64 writeData(const char *data, qint64 len)
00149 {
00150 if (len) {
00151
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
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
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
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
00245
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;
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
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
00357 Area &area = cache[0];
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
00368 return cache.find(0);
00369 }
00370
00371 int l = level(type);
00372 Cache::Iterator it = cache.find(num);
00373
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";
00475 else
00476 s << "\033[0;31m";
00477 }
00478 # ifdef Q_CC_GNU
00479
00480
00481
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
00488 pos = info.indexOf('(', pos + 1);
00489
00490 info.truncate(pos);
00491
00492
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
00510 pos = info.lastIndexOf(' ', startoftemplate);
00511 }
00512
00513 if (pos + 1 == info.length())
00514
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;
00562 break;
00563 default:
00564 s = setupQtWriter(type);
00565 #ifndef Q_OS_WIN
00566
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
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 }