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