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