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 static bool printProcessInfo = (qgetenv("KDE_DEBUG_NOPROCESSINFO").isEmpty());
00444 static bool printAreaName = (qgetenv("KDE_DEBUG_NOAREANAME").isEmpty());
00445 static bool printMethodName = (qgetenv("KDE_DEBUG_NOMETHODNAME").isEmpty());
00446 QByteArray programName;
00447 s = s.nospace();
00448 if (printProcessInfo) {
00449 programName = cache.value(0).name;
00450 if (programName.isEmpty())
00451 programName = "<unknown program name>";
00452 s << programName.constData() << "(" << unsigned(getpid()) << ")";
00453 }
00454 if (printAreaName && (!printProcessInfo || areaName != programName)) {
00455 if (printProcessInfo)
00456 s << "/";
00457 s << areaName.constData();
00458 }
00459
00460 if (funcinfo && printMethodName) {
00461 if(colored)
00462 s << "\033[0;34m";
00463 # ifdef Q_CC_GNU
00464
00465
00466
00467 QByteArray info = funcinfo;
00468 int pos = info.indexOf('(');
00469 Q_ASSERT_X(pos != -1, "kDebug",
00470 "Bug in kDebug(): I don't know how to parse this function name");
00471 while (info.at(pos - 1) == ' ')
00472
00473 pos = info.indexOf('(', pos + 1);
00474
00475 info.truncate(pos);
00476
00477
00478 int index = 1;
00479 forever {
00480 index = info.indexOf("<unnamed>::", index);
00481 if ( index == -1 )
00482 break;
00483
00484 if ( info.at(index-1) != ':' )
00485 info.insert(index, ' ');
00486
00487 index += strlen("<unnamed>::");
00488 }
00489 pos = info.lastIndexOf(' ');
00490 if (pos != -1) {
00491 int startoftemplate = info.lastIndexOf('<');
00492 if (startoftemplate != -1 && pos > startoftemplate &&
00493 pos < info.lastIndexOf(">::"))
00494
00495 pos = info.lastIndexOf(' ', startoftemplate);
00496 }
00497
00498 if (pos + 1 == info.length())
00499
00500 s << " " << funcinfo;
00501 else
00502 s << " " << info.constData() + pos + 1;
00503 # else
00504 s << " " << funcinfo;
00505 # endif
00506 if(colored)
00507 s << "\033[0m";
00508 }
00509
00510 s << ":";
00511 #else
00512 Q_UNUSED(funcinfo);
00513 if (!areaName.isEmpty())
00514 s << areaName.constData() << ':';
00515 #endif
00516 return s.space();
00517 }
00518
00519 QDebug stream(QtMsgType type, unsigned int area, const char *debugFile, int line,
00520 const char *funcinfo)
00521 {
00522 static bool env_colored = (!qgetenv("KDE_COLOR_DEBUG").isEmpty());
00523 Cache::Iterator it = areaData(type, area);
00524 OutputMode mode = it->mode[level(type)];
00525 QString file = it->logFileName[level(type)];
00526 QByteArray areaName = it->name;
00527
00528 if (areaName.isEmpty())
00529 areaName = cache.value(0).name;
00530
00531 bool colored=false;
00532
00533 QDebug s(&devnull);
00534 switch (mode) {
00535 case FileOutput:
00536 s = setupFileWriter(file);
00537 break;
00538 case MessageBoxOutput:
00539 s = setupMessageBoxWriter(type, areaName);
00540 break;
00541 case SyslogOutput:
00542 s = setupSyslogWriter(type);
00543 break;
00544 case NoOutput:
00545 s = QDebug(&devnull);
00546 return s;
00547 break;
00548 default:
00549 s = setupQtWriter(type);
00550 #ifndef Q_OS_WIN
00551
00552 colored = env_colored && isatty(fileno(stderr));
00553 #endif
00554 break;
00555 }
00556
00557 return printHeader(s, areaName, debugFile, line, funcinfo, colored);
00558 }
00559
00560 QMutex mutex;
00561 KConfig *config;
00562 KDebugDBusIface *kDebugDBusIface;
00563 Cache cache;
00564
00565 KNoDebugStream devnull;
00566 KSyslogDebugStream syslogwriter;
00567 KFileDebugStream filewriter;
00568 KMessageBoxDebugStream messageboxwriter;
00569 KLineEndStrippingDebugStream lineendstrippingwriter;
00570 };
00571
00572 K_GLOBAL_STATIC(KDebugPrivate, kDebug_data)
00573
00574 QString kRealBacktrace(int levels)
00575 {
00576 QString s;
00577 #ifdef HAVE_BACKTRACE
00578 void* trace[256];
00579 int n = backtrace(trace, 256);
00580 if (!n)
00581 return s;
00582 char** strings = backtrace_symbols (trace, n);
00583
00584 if ( levels != -1 )
00585 n = qMin( n, levels );
00586 s = QLatin1String("[\n");
00587
00588 for (int i = 0; i < n; ++i)
00589 s += QString::number(i) +
00590 QLatin1String(": ") +
00591 QLatin1String(strings[i]) + QLatin1String("\n");
00592 s += QLatin1String("]\n");
00593 if (strings)
00594 free (strings);
00595 #endif
00596 return s;
00597 }
00598
00599 QDebug kDebugDevNull()
00600 {
00601 return QDebug(&kDebug_data->devnull);
00602 }
00603
00604 QDebug kDebugStream(QtMsgType level, int area, const char *file, int line, const char *funcinfo)
00605 {
00606 if (kDebug_data.isDestroyed()) {
00607
00608 qCritical().nospace() << "kDebugStream called after destruction (from "
00609 << (funcinfo ? funcinfo : "")
00610 << (file ? " file " : " unknown file")
00611 << (file ? file :"")
00612 << " line " << line << ")";
00613 return QDebug(level);
00614 }
00615
00616 QMutexLocker locker(&kDebug_data->mutex);
00617 return kDebug_data->stream(level, area, file, line, funcinfo);
00618 }
00619
00620 QDebug perror(QDebug s, KDebugTag)
00621 {
00622 return s << QString::fromLocal8Bit(strerror(errno));
00623 }
00624
00625 QDebug operator<<(QDebug s, const KDateTime &time)
00626 {
00627 if ( time.isDateOnly() )
00628 s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::QtTextDate)) << ")";
00629 else
00630 s.nospace() << "KDateTime(" << qPrintable(time.toString(KDateTime::ISODate)) << ")";
00631 return s.space();
00632 }
00633
00634 QDebug operator<<(QDebug s, const KUrl &url)
00635 {
00636 s.nospace() << "KUrl(" << url.prettyUrl() << ")";
00637 return s.space();
00638 }
00639
00640 void kClearDebugConfig()
00641 {
00642 if (!kDebug_data) return;
00643 QMutexLocker locker(&kDebug_data->mutex);
00644 delete kDebug_data->config;
00645 kDebug_data->config = 0;
00646
00647 KDebugPrivate::Cache::Iterator it = kDebug_data->cache.begin(),
00648 end = kDebug_data->cache.end();
00649 for ( ; it != end; ++it)
00650 it->clear();
00651 }