00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "kdebug.h"
00022
00023 #ifdef NDEBUG
00024 #undef kdDebug
00025 #undef kdBacktrace
00026 #endif
00027
00028 #include "kdebugdcopiface.h"
00029
00030 #include "kapplication.h"
00031 #include "kglobal.h"
00032 #include "kinstance.h"
00033 #include "kstandarddirs.h"
00034
00035 #include <qmessagebox.h>
00036 #include <klocale.h>
00037 #include <qfile.h>
00038 #include <qintdict.h>
00039 #include <qstring.h>
00040 #include <qdatetime.h>
00041 #include <qpoint.h>
00042 #include <qrect.h>
00043 #include <qregion.h>
00044 #include <qstringlist.h>
00045 #include <qpen.h>
00046 #include <qbrush.h>
00047 #include <qsize.h>
00048
00049 #include <kurl.h>
00050
00051 #include <stdlib.h>
00052 #include <unistd.h>
00053 #include <stdarg.h>
00054 #include <ctype.h>
00055 #include <syslog.h>
00056 #include <errno.h>
00057 #include <string.h>
00058 #include <kconfig.h>
00059 #include "kstaticdeleter.h"
00060 #include <config.h>
00061
00062 #ifdef HAVE_BACKTRACE
00063 #include <execinfo.h>
00064 #endif
00065
00066 class KDebugEntry;
00067
00068 class KDebugEntry
00069 {
00070 public:
00071 KDebugEntry (int n, const QCString& d) {number=n; descr=d;}
00072 unsigned int number;
00073 QCString descr;
00074 };
00075
00076 static QIntDict<KDebugEntry> *KDebugCache;
00077
00078 static KStaticDeleter< QIntDict<KDebugEntry> > kdd;
00079
00080 static QCString getDescrFromNum(unsigned int _num)
00081 {
00082 if (!KDebugCache) {
00083 kdd.setObject(KDebugCache, new QIntDict<KDebugEntry>( 601 ));
00084
00085 KGlobal::unregisterStaticDeleter(&kdd);
00086 KDebugCache->setAutoDelete(true);
00087 }
00088
00089 KDebugEntry *ent = KDebugCache->find( _num );
00090 if ( ent )
00091 return ent->descr;
00092
00093 if ( !KDebugCache->isEmpty() )
00094 return QCString();
00095
00096 QString filename(locate("config","kdebug.areas"));
00097 if (filename.isEmpty())
00098 return QCString();
00099
00100 QFile file(filename);
00101 if (!file.open(IO_ReadOnly)) {
00102 qWarning("Couldn't open %s", filename.local8Bit().data());
00103 file.close();
00104 return QCString();
00105 }
00106
00107 uint lineNumber=0;
00108 QCString line(1024);
00109 int len;
00110
00111 while (( len = file.readLine(line.data(),line.size()-1) ) > 0) {
00112 int i=0;
00113 ++lineNumber;
00114
00115 while (line[i] && line[i] <= ' ')
00116 i++;
00117
00118 unsigned char ch=line[i];
00119
00120 if ( !ch || ch =='#' || ch =='\n')
00121 continue;
00122
00123 if (ch < '0' && ch > '9') {
00124 qWarning("Syntax error: no number (line %u)",lineNumber);
00125 continue;
00126 }
00127
00128 const int numStart=i;
00129 do {
00130 ch=line[++i];
00131 } while ( ch >= '0' && ch <= '9');
00132
00133 const Q_ULONG number =line.mid(numStart,i).toULong();
00134
00135 while (line[i] && line[i] <= ' ')
00136 i++;
00137
00138 KDebugCache->insert(number, new KDebugEntry(number, line.mid(i, len-i-1)));
00139 }
00140 file.close();
00141
00142 ent = KDebugCache->find( _num );
00143 if ( ent )
00144 return ent->descr;
00145
00146 return QCString();
00147 }
00148
00149 enum DebugLevels {
00150 KDEBUG_INFO= 0,
00151 KDEBUG_WARN= 1,
00152 KDEBUG_ERROR= 2,
00153 KDEBUG_FATAL= 3
00154 };
00155
00156
00157 struct kDebugPrivate {
00158 kDebugPrivate() :
00159 oldarea(0), config(0) { }
00160
00161 ~kDebugPrivate() { delete config; }
00162
00163 QCString aAreaName;
00164 unsigned int oldarea;
00165 KConfig *config;
00166 };
00167
00168 static kDebugPrivate *kDebug_data = 0;
00169 static KStaticDeleter<kDebugPrivate> pcd;
00170 static KStaticDeleter<KDebugDCOPIface> dcopsd;
00171 static KDebugDCOPIface* kDebugDCOPIface = 0;
00172
00173 static void kDebugBackend( unsigned short nLevel, unsigned int nArea, const char *data)
00174 {
00175 if ( !kDebug_data )
00176 {
00177 pcd.setObject(kDebug_data, new kDebugPrivate());
00178
00179 KGlobal::unregisterStaticDeleter(&pcd);
00180
00181
00182 if (!kDebugDCOPIface)
00183 {
00184 kDebugDCOPIface = dcopsd.setObject(kDebugDCOPIface, new KDebugDCOPIface);
00185 }
00186 }
00187
00188 if (!kDebug_data->config && KGlobal::_instance )
00189 {
00190 kDebug_data->config = new KConfig("kdebugrc", false, false);
00191 kDebug_data->config->setGroup("0");
00192
00193
00194
00195 if ( KGlobal::_instance )
00196 kDebug_data->aAreaName = KGlobal::instance()->instanceName();
00197 }
00198
00199 if (kDebug_data->config && kDebug_data->oldarea != nArea) {
00200 kDebug_data->config->setGroup( QString::number(static_cast<int>(nArea)) );
00201 kDebug_data->oldarea = nArea;
00202 if ( nArea > 0 && KGlobal::_instance )
00203 kDebug_data->aAreaName = getDescrFromNum(nArea);
00204 if ((nArea == 0) || kDebug_data->aAreaName.isEmpty())
00205 if ( KGlobal::_instance )
00206 kDebug_data->aAreaName = KGlobal::instance()->instanceName();
00207 }
00208
00209 int nPriority = 0;
00210 QString aCaption;
00211
00212
00213
00214 QString key;
00215 switch( nLevel )
00216 {
00217 case KDEBUG_INFO:
00218 key = "InfoOutput";
00219 aCaption = "Info";
00220 nPriority = LOG_DEBUG;
00221 break;
00222 case KDEBUG_WARN:
00223 key = "WarnOutput";
00224 aCaption = "Warning";
00225 nPriority = LOG_WARNING;
00226 break;
00227 case KDEBUG_FATAL:
00228 key = "FatalOutput";
00229 aCaption = "Fatal Error";
00230 nPriority = LOG_CRIT;
00231 break;
00232 case KDEBUG_ERROR:
00233 default:
00234
00235 key = "ErrorOutput";
00236 aCaption = "Error";
00237 nPriority = LOG_ERR;
00238 break;
00239 }
00240
00241
00242 short nOutput = kDebug_data->config ? kDebug_data->config->readNumEntry(key, 4) : 4;
00243
00244
00245
00246 if (!kapp && (nOutput == 1))
00247 nOutput = 2;
00248 else if ( nOutput == 4 && nLevel != KDEBUG_FATAL )
00249 return;
00250
00251 const int BUFSIZE = 4096;
00252 char buf[BUFSIZE];
00253 buf[0] = '\0';
00254
00255 static bool printTimeStamp = !(QCString(getenv("KDE_DEBUG_TIMESTAMP")).isEmpty());
00256 if ( printTimeStamp ) {
00257 const QString ts = QDateTime::currentDateTime().time().toString() + ' ';
00258 strlcat( buf, ts.latin1(), BUFSIZE );
00259 }
00260
00261 if ( !kDebug_data->aAreaName.isEmpty() ) {
00262 strlcat( buf, kDebug_data->aAreaName.data(), BUFSIZE );
00263 strlcat( buf, ": ", BUFSIZE );
00264 strlcat( buf, data, BUFSIZE );
00265 }
00266 else
00267 strlcat( buf, data, BUFSIZE );
00268
00269
00270
00271 switch( nOutput )
00272 {
00273 case 0:
00274 {
00275 const char* aKey;
00276 switch( nLevel )
00277 {
00278 case KDEBUG_INFO:
00279 aKey = "InfoFilename";
00280 break;
00281 case KDEBUG_WARN:
00282 aKey = "WarnFilename";
00283 break;
00284 case KDEBUG_FATAL:
00285 aKey = "FatalFilename";
00286 break;
00287 case KDEBUG_ERROR:
00288 default:
00289 aKey = "ErrorFilename";
00290 break;
00291 }
00292 QFile aOutputFile( kDebug_data->config->readPathEntry(aKey, "kdebug.dbg") );
00293 aOutputFile.open( IO_WriteOnly | IO_Append | IO_Raw );
00294 aOutputFile.writeBlock( buf, strlen( buf ) );
00295 aOutputFile.close();
00296 break;
00297 }
00298 case 1:
00299 {
00300
00301
00302 if ( !kDebug_data->aAreaName.isEmpty() )
00303 aCaption += QString("(%1)").arg( kDebug_data->aAreaName );
00304 QMessageBox::warning( 0L, aCaption, data, i18n("&OK") );
00305 break;
00306 }
00307 case 2:
00308 {
00309 write( 2, buf, strlen( buf ) );
00310 break;
00311 }
00312 case 3:
00313 {
00314 syslog( nPriority, "%s:%s", getenv("USER"), buf);
00315 break;
00316 }
00317 }
00318
00319
00320 if( ( nLevel == KDEBUG_FATAL )
00321 && ( !kDebug_data->config || kDebug_data->config->readNumEntry( "AbortFatal", 1 ) ) )
00322 abort();
00323 }
00324
00325 kdbgstream &perror( kdbgstream &s) { return s << QString::fromLocal8Bit(strerror(errno)); }
00326 kdbgstream kdDebug(int area) { return kdbgstream(area, KDEBUG_INFO); }
00327 kdbgstream kdDebug(bool cond, int area) { if (cond) return kdbgstream(area, KDEBUG_INFO); else return kdbgstream(0, 0, false); }
00328
00329 kdbgstream kdError(int area) { return kdbgstream("ERROR: ", area, KDEBUG_ERROR); }
00330 kdbgstream kdError(bool cond, int area) { if (cond) return kdbgstream("ERROR: ", area, KDEBUG_ERROR); else return kdbgstream(0,0,false); }
00331 kdbgstream kdWarning(int area) { return kdbgstream("WARNING: ", area, KDEBUG_WARN); }
00332 kdbgstream kdWarning(bool cond, int area) { if (cond) return kdbgstream("WARNING: ", area, KDEBUG_WARN); else return kdbgstream(0,0,false); }
00333 kdbgstream kdFatal(int area) { return kdbgstream("FATAL: ", area, KDEBUG_FATAL); }
00334 kdbgstream kdFatal(bool cond, int area) { if (cond) return kdbgstream("FATAL: ", area, KDEBUG_FATAL); else return kdbgstream(0,0,false); }
00335
00336 kdbgstream::kdbgstream(kdbgstream &str)
00337 : output(str.output), area(str.area), level(str.level), print(str.print)
00338 {
00339 str.output.truncate(0);
00340 }
00341
00342 void kdbgstream::flush() {
00343 if (output.isEmpty() || !print)
00344 return;
00345 kDebugBackend( level, area, output.local8Bit().data() );
00346 output = QString::null;
00347 }
00348
00349 kdbgstream &kdbgstream::form(const char *format, ...)
00350 {
00351 char buf[4096];
00352 va_list arguments;
00353 va_start( arguments, format );
00354 vsnprintf( buf, sizeof(buf), format, arguments );
00355 va_end(arguments);
00356 *this << buf;
00357 return *this;
00358 }
00359
00360 kdbgstream::~kdbgstream() {
00361 if (!output.isEmpty()) {
00362 fprintf(stderr, "ASSERT: debug output not ended with \\n\n");
00363 fprintf(stderr, "%s", kdBacktrace().latin1());
00364 *this << "\n";
00365 }
00366 }
00367
00368 kdbgstream& kdbgstream::operator << (char ch)
00369 {
00370 if (!print) return *this;
00371 if (!isprint(ch))
00372 output += "\\x" + QString::number( static_cast<uint>( ch ), 16 ).rightJustify(2, '0');
00373 else {
00374 output += ch;
00375 if (ch == '\n') flush();
00376 }
00377 return *this;
00378 }
00379
00380 kdbgstream& kdbgstream::operator << (QChar ch)
00381 {
00382 if (!print) return *this;
00383 if (!ch.isPrint())
00384 output += "\\x" + QString::number( ch.unicode(), 16 ).rightJustify(2, '0');
00385 else {
00386 output += ch;
00387 if (ch == '\n') flush();
00388 }
00389 return *this;
00390 }
00391
00392 kdbgstream& kdbgstream::operator << (QWidget* widget)
00393 {
00394 return *this << const_cast< const QWidget* >( widget );
00395 }
00396
00397 kdbgstream& kdbgstream::operator << (const QWidget* widget)
00398 {
00399 QString string, temp;
00400
00401 if(widget==0)
00402 {
00403 string=(QString)"[Null pointer]";
00404 } else {
00405 temp.setNum((ulong)widget, 16);
00406 string=(QString)"["+widget->className()+" pointer "
00407 + "(0x" + temp + ")";
00408 if(widget->name(0)==0)
00409 {
00410 string += " to unnamed widget, ";
00411 } else {
00412 string += (QString)" to widget " + widget->name() + ", ";
00413 }
00414 string += "geometry="
00415 + QString().setNum(widget->width())
00416 + "x"+QString().setNum(widget->height())
00417 + "+"+QString().setNum(widget->x())
00418 + "+"+QString().setNum(widget->y())
00419 + "]";
00420 }
00421 if (!print)
00422 {
00423 return *this;
00424 }
00425 output += string;
00426 if (output.at(output.length() -1 ) == '\n')
00427 {
00428 flush();
00429 }
00430 return *this;
00431 }
00432
00433
00434
00435
00436
00437 kdbgstream& kdbgstream::operator<<( const QDateTime& time) {
00438 *this << time.toString();
00439 return *this;
00440 }
00441 kdbgstream& kdbgstream::operator<<( const QDate& date) {
00442 *this << date.toString();
00443
00444 return *this;
00445 }
00446 kdbgstream& kdbgstream::operator<<( const QTime& time ) {
00447 *this << time.toString();
00448 return *this;
00449 }
00450 kdbgstream& kdbgstream::operator<<( const QPoint& p ) {
00451 *this << "(" << p.x() << ", " << p.y() << ")";
00452 return *this;
00453 }
00454 kdbgstream& kdbgstream::operator<<( const QSize& s ) {
00455 *this << "[" << s.width() << "x" << s.height() << "]";
00456 return *this;
00457 }
00458 kdbgstream& kdbgstream::operator<<( const QRect& r ) {
00459 *this << "[" << r.x() << "," << r.y() << " - " << r.width() << "x" << r.height() << "]";
00460 return *this;
00461 }
00462 kdbgstream& kdbgstream::operator<<( const QRegion& reg ) {
00463 *this<< "[ ";
00464
00465 QMemArray<QRect>rs=reg.rects();
00466 for (uint i=0;i<rs.size();++i)
00467 *this << QString("[%1,%2 - %3x%4] ").arg(rs[i].x()).arg(rs[i].y()).arg(rs[i].width()).arg(rs[i].height() ) ;
00468
00469 *this <<"]";
00470 return *this;
00471 }
00472 kdbgstream& kdbgstream::operator<<( const KURL& u ) {
00473 *this << u.prettyURL();
00474 return *this;
00475 }
00476 kdbgstream& kdbgstream::operator<<( const QStringList& l ) {
00477 *this << "(";
00478 *this << l.join(",");
00479 *this << ")";
00480
00481 return *this;
00482 }
00483 kdbgstream& kdbgstream::operator<<( const QColor& c ) {
00484 if ( c.isValid() )
00485 *this <<c.name();
00486 else
00487 *this << "(invalid/default)";
00488 return *this;
00489 }
00490 kdbgstream& kdbgstream::operator<<( const QPen& p ) {
00491 static const char* const s_penStyles[] = {
00492 "NoPen", "SolidLine", "DashLine", "DotLine", "DashDotLine",
00493 "DashDotDotLine" };
00494 static const char* const s_capStyles[] = {
00495 "FlatCap", "SquareCap", "RoundCap" };
00496 *this << "[ style:";
00497 *this << s_penStyles[ p.style() ];
00498 *this << " width:";
00499 *this << p.width();
00500 *this << " color:";
00501 if ( p.color().isValid() )
00502 *this << p.color().name();
00503 else
00504 *this <<"(invalid/default)";
00505 if ( p.width() > 0 )
00506 {
00507 *this << " capstyle:";
00508 *this << s_capStyles[ p.capStyle() >> 4 ];
00509
00510 }
00511 *this <<" ]";
00512 return *this;
00513 }
00514 kdbgstream& kdbgstream::operator<<( const QBrush& b) {
00515 static const char* const s_brushStyles[] = {
00516 "NoBrush", "SolidPattern", "Dense1Pattern", "Dense2Pattern", "Dense3Pattern",
00517 "Dense4Pattern", "Dense5Pattern", "Dense6Pattern", "Dense7Pattern",
00518 "HorPattern", "VerPattern", "CrossPattern", "BDiagPattern", "FDiagPattern",
00519 "DiagCrossPattern" };
00520
00521 *this <<"[ style: ";
00522 *this <<s_brushStyles[ b.style() ];
00523 *this <<" color: ";
00524
00525 if ( b.color().isValid() )
00526 *this <<b.color().name() ;
00527 else
00528 *this <<"(invalid/default)";
00529 if ( b.pixmap() )
00530 *this <<" has a pixmap";
00531 *this <<" ]";
00532 return *this;
00533 }
00534
00535 kdbgstream& kdbgstream::operator<<( const QVariant& v) {
00536 *this << "[variant: ";
00537 *this << v.typeName();
00538
00539
00540 *this << " toString=";
00541 *this << v.toString();
00542 *this << "]";
00543 return *this;
00544 }
00545
00546 kdbgstream& kdbgstream::operator<<( const QByteArray& data) {
00547 if (!print) return *this;
00548 output += '[';
00549 unsigned int i = 0;
00550 unsigned int sz = QMIN( data.size(), 64 );
00551 for ( ; i < sz ; ++i ) {
00552 output += QString::number( (unsigned char) data[i], 16 ).rightJustify(2, '0');
00553 if ( i < sz )
00554 output += ' ';
00555 }
00556 if ( sz < data.size() )
00557 output += "...";
00558 output += ']';
00559 return *this;
00560 }
00561
00562 QString kdBacktrace(int levels)
00563 {
00564 QString s;
00565 #ifdef HAVE_BACKTRACE
00566 void* trace[256];
00567 int n = backtrace(trace, 256);
00568 if (!n)
00569 return s;
00570 char** strings = backtrace_symbols (trace, n);
00571
00572 if ( levels != -1 )
00573 n = QMIN( n, levels );
00574 s = "[\n";
00575
00576 for (int i = 0; i < n; ++i)
00577 s += QString::number(i) +
00578 QString::fromLatin1(": ") +
00579 QString::fromLatin1(strings[i]) + QString::fromLatin1("\n");
00580 s += "]\n";
00581 if (strings)
00582 free (strings);
00583 #endif
00584 return s;
00585 }
00586
00587 QString kdBacktrace()
00588 {
00589 return kdBacktrace(-1 );
00590 }
00591
00592 void kdClearDebugConfig()
00593 {
00594 if (kDebug_data) {
00595 delete kDebug_data->config;
00596 kDebug_data->config = 0;
00597 }
00598 }
00599
00600
00601
00602 #ifdef NDEBUG
00603 #define kdDebug kndDebug
00604 #endif