kdebug.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) 00003 2002 Holger Freyther (freyther@kde.org) 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 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> // abort 00052 #include <unistd.h> // getpid 00053 #include <stdarg.h> // vararg stuff 00054 #include <ctype.h> // isprint 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 // Do not call this deleter from ~KApplication 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() ) // areas already loaded 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; // We have an eof, a comment or an empty line 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 // Do not call this deleter from ~KApplication 00179 KGlobal::unregisterStaticDeleter(&pcd); 00180 00181 // create the dcop interface if it has not been created yet 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 //AB: this is necessary here, otherwise all output with area 0 won't be 00194 //prefixed with anything, unless something with area != 0 is called before 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 /* Determine output */ 00213 00214 QString key; 00215 switch( nLevel ) 00216 { 00217 case KDEBUG_INFO: 00218 key = "InfoOutput"; 00219 aCaption = "Info"; 00220 nPriority = LOG_INFO; 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 /* Programmer error, use "Error" as default */ 00235 key = "ErrorOutput"; 00236 aCaption = "Error"; 00237 nPriority = LOG_ERR; 00238 break; 00239 } 00240 00241 short nOutput = kDebug_data->config ? kDebug_data->config->readNumEntry(key, 4) : 4; 00242 00243 // If the application doesn't have a QApplication object it can't use 00244 // a messagebox. 00245 if (!kapp && (nOutput == 1)) 00246 nOutput = 2; 00247 else if ( nOutput == 4 && nLevel != KDEBUG_FATAL ) 00248 return; 00249 00250 const int BUFSIZE = 4096; 00251 char buf[BUFSIZE]; 00252 if ( !kDebug_data->aAreaName.isEmpty() ) { 00253 strlcpy( buf, kDebug_data->aAreaName.data(), BUFSIZE ); 00254 strlcat( buf, ": ", BUFSIZE ); 00255 strlcat( buf, data, BUFSIZE ); 00256 } 00257 else 00258 strlcpy( buf, data, BUFSIZE ); 00259 00260 00261 // Output 00262 switch( nOutput ) 00263 { 00264 case 0: // File 00265 { 00266 const char* aKey; 00267 switch( nLevel ) 00268 { 00269 case KDEBUG_INFO: 00270 aKey = "InfoFilename"; 00271 break; 00272 case KDEBUG_WARN: 00273 aKey = "WarnFilename"; 00274 break; 00275 case KDEBUG_FATAL: 00276 aKey = "FatalFilename"; 00277 break; 00278 case KDEBUG_ERROR: 00279 default: 00280 aKey = "ErrorFilename"; 00281 break; 00282 } 00283 QFile aOutputFile( kDebug_data->config->readPathEntry(aKey, "kdebug.dbg") ); 00284 aOutputFile.open( IO_WriteOnly | IO_Append | IO_Raw ); 00285 aOutputFile.writeBlock( buf, strlen( buf ) ); 00286 aOutputFile.close(); 00287 break; 00288 } 00289 case 1: // Message Box 00290 { 00291 // Since we are in kdecore here, we cannot use KMsgBox and use 00292 // QMessageBox instead 00293 if ( !kDebug_data->aAreaName.isEmpty() ) 00294 aCaption += QString("(%1)").arg( kDebug_data->aAreaName ); 00295 QMessageBox::warning( 0L, aCaption, data, i18n("&OK") ); 00296 break; 00297 } 00298 case 2: // Shell 00299 { 00300 write( 2, buf, strlen( buf ) ); //fputs( buf, stderr ); 00301 break; 00302 } 00303 case 3: // syslog 00304 { 00305 syslog( nPriority, "%s", buf); 00306 break; 00307 } 00308 } 00309 00310 // check if we should abort 00311 if( ( nLevel == KDEBUG_FATAL ) 00312 && ( !kDebug_data->config || kDebug_data->config->readNumEntry( "AbortFatal", 1 ) ) ) 00313 abort(); 00314 } 00315 00316 kdbgstream &perror( kdbgstream &s) { return s << QString::fromLocal8Bit(strerror(errno)); } 00317 kdbgstream kdDebug(int area) { return kdbgstream(area, KDEBUG_INFO); } 00318 kdbgstream kdDebug(bool cond, int area) { if (cond) return kdbgstream(area, KDEBUG_INFO); else return kdbgstream(0, 0, false); } 00319 00320 kdbgstream kdError(int area) { return kdbgstream("ERROR: ", area, KDEBUG_ERROR); } 00321 kdbgstream kdError(bool cond, int area) { if (cond) return kdbgstream("ERROR: ", area, KDEBUG_ERROR); else return kdbgstream(0,0,false); } 00322 kdbgstream kdWarning(int area) { return kdbgstream("WARNING: ", area, KDEBUG_WARN); } 00323 kdbgstream kdWarning(bool cond, int area) { if (cond) return kdbgstream("WARNING: ", area, KDEBUG_WARN); else return kdbgstream(0,0,false); } 00324 kdbgstream kdFatal(int area) { return kdbgstream("FATAL: ", area, KDEBUG_FATAL); } 00325 kdbgstream kdFatal(bool cond, int area) { if (cond) return kdbgstream("FATAL: ", area, KDEBUG_FATAL); else return kdbgstream(0,0,false); } 00326 00327 kdbgstream::kdbgstream(kdbgstream &str) 00328 : output(str.output), area(str.area), level(str.level), print(str.print) 00329 { 00330 str.output.truncate(0); 00331 } 00332 00333 void kdbgstream::flush() { 00334 if (output.isEmpty() || !print) 00335 return; 00336 kDebugBackend( level, area, output.local8Bit().data() ); 00337 output = QString::null; 00338 } 00339 00340 kdbgstream &kdbgstream::form(const char *format, ...) 00341 { 00342 char buf[4096]; 00343 va_list arguments; 00344 va_start( arguments, format ); 00345 vsnprintf( buf, sizeof(buf), format, arguments ); 00346 va_end(arguments); 00347 *this << buf; 00348 return *this; 00349 } 00350 00351 kdbgstream::~kdbgstream() { 00352 if (!output.isEmpty()) { 00353 fprintf(stderr, "ASSERT: debug output not ended with \\n\n"); 00354 fprintf(stderr, "%s", kdBacktrace().latin1()); 00355 *this << "\n"; 00356 } 00357 } 00358 00359 kdbgstream& kdbgstream::operator << (char ch) 00360 { 00361 if (!print) return *this; 00362 if (!isprint(ch)) 00363 output += "\\x" + QString::number( static_cast<uint>( ch ), 16 ).rightJustify(2, '0'); 00364 else { 00365 output += ch; 00366 if (ch == '\n') flush(); 00367 } 00368 return *this; 00369 } 00370 00371 kdbgstream& kdbgstream::operator << (QChar ch) 00372 { 00373 if (!print) return *this; 00374 if (!ch.isPrint()) 00375 output += "\\x" + QString::number( ch.unicode(), 16 ).rightJustify(2, '0'); 00376 else { 00377 output += ch; 00378 if (ch == '\n') flush(); 00379 } 00380 return *this; 00381 } 00382 00383 kdbgstream& kdbgstream::operator << (QWidget* widget) 00384 { 00385 return *this << const_cast< const QWidget* >( widget ); 00386 } 00387 00388 kdbgstream& kdbgstream::operator << (const QWidget* widget) 00389 { 00390 QString string, temp; 00391 // ----- 00392 if(widget==0) 00393 { 00394 string=(QString)"[Null pointer]"; 00395 } else { 00396 temp.setNum((ulong)widget, 16); 00397 string=(QString)"["+widget->className()+" pointer " 00398 + "(0x" + temp + ")"; 00399 if(widget->name(0)==0) 00400 { 00401 string += " to unnamed widget, "; 00402 } else { 00403 string += (QString)" to widget " + widget->name() + ", "; 00404 } 00405 string += "geometry=" 00406 + QString().setNum(widget->width()) 00407 + "x"+QString().setNum(widget->height()) 00408 + "+"+QString().setNum(widget->x()) 00409 + "+"+QString().setNum(widget->y()) 00410 + "]"; 00411 } 00412 if (!print) 00413 { 00414 return *this; 00415 } 00416 output += string; 00417 if (output.at(output.length() -1 ) == '\n') 00418 { 00419 flush(); 00420 } 00421 return *this; 00422 } 00423 /* 00424 * either use 'output' directly and do the flush if needed 00425 * or use the QString operator which calls the char* operator 00426 * 00427 */ 00428 kdbgstream& kdbgstream::operator<<( const QDateTime& time) { 00429 *this << time.toString(); 00430 return *this; 00431 } 00432 kdbgstream& kdbgstream::operator<<( const QDate& date) { 00433 *this << date.toString(); 00434 00435 return *this; 00436 } 00437 kdbgstream& kdbgstream::operator<<( const QTime& time ) { 00438 *this << time.toString(); 00439 return *this; 00440 } 00441 kdbgstream& kdbgstream::operator<<( const QPoint& p ) { 00442 *this << "(" << p.x() << ", " << p.y() << ")"; 00443 return *this; 00444 } 00445 kdbgstream& kdbgstream::operator<<( const QSize& s ) { 00446 *this << "[" << s.width() << "x" << s.height() << "]"; 00447 return *this; 00448 } 00449 kdbgstream& kdbgstream::operator<<( const QRect& r ) { 00450 *this << "[" << r.x() << "," << r.y() << " - " << r.width() << "x" << r.height() << "]"; 00451 return *this; 00452 } 00453 kdbgstream& kdbgstream::operator<<( const QRegion& reg ) { 00454 *this<< "[ "; 00455 00456 QMemArray<QRect>rs=reg.rects(); 00457 for (uint i=0;i<rs.size();++i) 00458 *this << QString("[%1,%2 - %3x%4] ").arg(rs[i].x()).arg(rs[i].y()).arg(rs[i].width()).arg(rs[i].height() ) ; 00459 00460 *this <<"]"; 00461 return *this; 00462 } 00463 kdbgstream& kdbgstream::operator<<( const KURL& u ) { 00464 *this << u.prettyURL(); 00465 return *this; 00466 } 00467 kdbgstream& kdbgstream::operator<<( const QStringList& l ) { 00468 *this << "("; 00469 *this << l.join(","); 00470 *this << ")"; 00471 00472 return *this; 00473 } 00474 kdbgstream& kdbgstream::operator<<( const QColor& c ) { 00475 if ( c.isValid() ) 00476 *this <<c.name(); 00477 else 00478 *this << "(invalid/default)"; 00479 return *this; 00480 } 00481 kdbgstream& kdbgstream::operator<<( const QPen& p ) { 00482 static const char* const s_penStyles[] = { 00483 "NoPen", "SolidLine", "DashLine", "DotLine", "DashDotLine", 00484 "DashDotDotLine" }; 00485 static const char* const s_capStyles[] = { 00486 "FlatCap", "SquareCap", "RoundCap" }; 00487 *this << "[ style:"; 00488 *this << s_penStyles[ p.style() ]; 00489 *this << " width:"; 00490 *this << p.width(); 00491 *this << " color:"; 00492 if ( p.color().isValid() ) 00493 *this << p.color().name(); 00494 else 00495 *this <<"(invalid/default)"; 00496 if ( p.width() > 0 ) // cap style doesn't matter, otherwise 00497 { 00498 *this << " capstyle:"; 00499 *this << s_capStyles[ p.capStyle() >> 4 ]; 00500 // join style omitted 00501 } 00502 *this <<" ]"; 00503 return *this; 00504 } 00505 kdbgstream& kdbgstream::operator<<( const QBrush& b) { 00506 static const char* const s_brushStyles[] = { 00507 "NoBrush", "SolidPattern", "Dense1Pattern", "Dense2Pattern", "Dense3Pattern", 00508 "Dense4Pattern", "Dense5Pattern", "Dense6Pattern", "Dense7Pattern", 00509 "HorPattern", "VerPattern", "CrossPattern", "BDiagPattern", "FDiagPattern", 00510 "DiagCrossPattern" }; 00511 00512 *this <<"[ style: "; 00513 *this <<s_brushStyles[ b.style() ]; 00514 *this <<" color: "; 00515 // can't use operator<<(str, b.color()) because that terminates a kdbgstream (flushes) 00516 if ( b.color().isValid() ) 00517 *this <<b.color().name() ; 00518 else 00519 *this <<"(invalid/default)"; 00520 if ( b.pixmap() ) 00521 *this <<" has a pixmap"; 00522 *this <<" ]"; 00523 return *this; 00524 } 00525 00526 kdbgstream& kdbgstream::operator<<( const QVariant& v) { 00527 *this << "[variant: "; 00528 *this << v.typeName(); 00529 // For now we just attempt a conversion to string. 00530 // Feel free to switch(v.type()) and improve the output. 00531 *this << " toString="; 00532 *this << v.toString(); 00533 *this << "]"; 00534 return *this; 00535 } 00536 00537 kdbgstream& kdbgstream::operator<<( const QByteArray& data) { 00538 if (!print) return *this; 00539 output += '['; 00540 unsigned int i = 0; 00541 unsigned int sz = QMIN( data.size(), 64 ); 00542 for ( ; i < sz ; ++i ) { 00543 output += QString::number( (unsigned char) data[i], 16 ).rightJustify(2, '0'); 00544 if ( i < sz ) 00545 output += ' '; 00546 } 00547 if ( sz < data.size() ) 00548 output += "..."; 00549 output += ']'; 00550 return *this; 00551 } 00552 00553 QString kdBacktrace(int levels) 00554 { 00555 QString s; 00556 #ifdef HAVE_BACKTRACE 00557 void* trace[256]; 00558 int n = backtrace(trace, 256); 00559 if (!n) 00560 return s; 00561 char** strings = backtrace_symbols (trace, n); 00562 00563 if ( levels != -1 ) 00564 n = QMIN( n, levels ); 00565 s = "[\n"; 00566 00567 for (int i = 0; i < n; ++i) 00568 s += QString::number(i) + 00569 QString::fromLatin1(": ") + 00570 QString::fromLatin1(strings[i]) + QString::fromLatin1("\n"); 00571 s += "]\n"; 00572 if (strings) 00573 free (strings); 00574 #endif 00575 return s; 00576 } 00577 00578 QString kdBacktrace() 00579 { 00580 return kdBacktrace(-1 /*all*/); 00581 } 00582 00583 void kdClearDebugConfig() 00584 { 00585 if (kDebug_data) { 00586 delete kDebug_data->config; 00587 kDebug_data->config = 0; 00588 } 00589 } 00590 00591 00592 // Needed for --enable-final 00593 #ifdef NDEBUG 00594 #define kdDebug kndDebug 00595 #endif