kdecore Library API Documentation

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