00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "kdebug.h"
00025
00026 #ifdef NDEBUG
00027 #undef kdDebug
00028 #undef kdBacktrace
00029 #endif
00030
00031 #include "kapplication.h"
00032 #include "kglobal.h"
00033 #include "kinstance.h"
00034 #include "kstandarddirs.h"
00035 #include <qmessagebox.h>
00036 #include <klocale.h>
00037 #include <qfile.h>
00038 #include <qintdict.h>
00039 #include <qstring.h>
00040 #include <qtextstream.h>
00041
00042 #include <stdlib.h>
00043 #include <unistd.h>
00044 #include <stdarg.h>
00045 #include <ctype.h>
00046 #include <syslog.h>
00047 #include <errno.h>
00048 #include <string.h>
00049 #include <kconfig.h>
00050 #include "kstaticdeleter.h"
00051 #include <config.h>
00052
00053 #ifdef HAVE_BACKTRACE
00054 #include <execinfo.h>
00055 #endif
00056
00057 class KDebugEntry;
00058
00059 class KDebugEntry
00060 {
00061 public:
00062 KDebugEntry (int n, QString d) {number=n; descr=d;}
00063 unsigned int number;
00064 QString descr;
00065 };
00066
00067 static QIntDict<KDebugEntry> *KDebugCache;
00068
00069 static KStaticDeleter< QIntDict<KDebugEntry> > kdd;
00070
00071 static QString getDescrFromNum(unsigned int _num)
00072 {
00073 if (!KDebugCache) {
00074 kdd.setObject(KDebugCache, new QIntDict<KDebugEntry>);
00075
00076 KGlobal::unregisterStaticDeleter(&kdd);
00077 KDebugCache->setAutoDelete(true);
00078 }
00079
00080 KDebugEntry *ent = KDebugCache->find( _num );
00081 if ( ent )
00082 return ent->descr;
00083
00084 if ( !KDebugCache->isEmpty() )
00085 return QString::null;
00086
00087 QString filename(locate("config","kdebug.areas"));
00088 QFile file(filename);
00089 if (!file.open(IO_ReadOnly)) {
00090 qWarning("Couldn't open %s", filename.local8Bit().data());
00091 file.close();
00092 return "";
00093 }
00094
00095 unsigned long number = 0;
00096 bool longOK;
00097
00098 QTextStream *ts = new QTextStream(&file);
00099 ts->setEncoding( QTextStream::Latin1 );
00100 while (!ts->eof()) {
00101 const QString data(ts->readLine());
00102 int i = 0;
00103 int len = data.length();
00104
00105 QChar ch = data[0];
00106 if (ch == '#' || ch.isNull()) {
00107 continue;
00108 }
00109 while (ch.isSpace()) {
00110 if (!(i < len))
00111 continue;
00112 ++i;
00113 ch = data[i];
00114 }
00115 if (ch.isNumber()) {
00116 int numStart = i ;
00117 while (ch.isNumber()) {
00118 if (!(i < len))
00119 continue;
00120 ++i;
00121 ch = data[i];
00122 }
00123 number = data.mid(numStart,i).toULong(&longOK);
00124 }
00125 while (ch.isSpace()) {
00126 if (!(i < len))
00127 continue;
00128 ++i;
00129 ch = data[i];
00130 }
00131 const QString description(data.mid(i, len));
00132
00133
00134 KDebugCache->insert(number, new KDebugEntry(number,description));
00135 }
00136
00137 delete ts;
00138 file.close();
00139
00140 ent = KDebugCache->find( _num );
00141 if ( ent )
00142 return ent->descr;
00143
00144 return QString::null;
00145 }
00146
00147 enum DebugLevels {
00148 KDEBUG_INFO= 0,
00149 KDEBUG_WARN= 1,
00150 KDEBUG_ERROR= 2,
00151 KDEBUG_FATAL= 3
00152 };
00153
00154
00155 struct kDebugPrivate {
00156 kDebugPrivate() :
00157 oldarea(0), config(0) { }
00158
00159 ~kDebugPrivate() { delete config; }
00160
00161 QString aAreaName;
00162 unsigned int oldarea;
00163 KConfig *config;
00164 };
00165
00166 static kDebugPrivate *kDebug_data = 0;
00167 static KStaticDeleter<kDebugPrivate> pcd;
00168
00169 static void kDebugBackend( unsigned short nLevel, unsigned int nArea, const char *data)
00170 {
00171 if ( !kDebug_data )
00172 {
00173 pcd.setObject(kDebug_data, new kDebugPrivate());
00174
00175 KGlobal::unregisterStaticDeleter(&pcd);
00176 }
00177
00178 if (!kDebug_data->config && KGlobal::_instance )
00179 {
00180 kDebug_data->config = new KConfig("kdebugrc", false, false);
00181 kDebug_data->config->setGroup("0");
00182 }
00183
00184 if (kDebug_data->config && kDebug_data->oldarea != nArea) {
00185 kDebug_data->config->setGroup( QString::number(static_cast<int>(nArea)) );
00186 kDebug_data->oldarea = nArea;
00187 if ( nArea > 0 && KGlobal::_instance )
00188 kDebug_data->aAreaName = getDescrFromNum(nArea);
00189 if ((nArea == 0) || kDebug_data->aAreaName.isEmpty())
00190 if ( KGlobal::_instance )
00191 kDebug_data->aAreaName = KGlobal::instance()->instanceName();
00192 }
00193
00194 int nPriority = 0;
00195 QString aCaption;
00196
00197
00198
00199 QString key;
00200 switch( nLevel )
00201 {
00202 case KDEBUG_INFO:
00203 key = "InfoOutput";
00204 aCaption = "Info";
00205 nPriority = LOG_INFO;
00206 break;
00207 case KDEBUG_WARN:
00208 key = "WarnOutput";
00209 aCaption = "Warning";
00210 nPriority = LOG_WARNING;
00211 break;
00212 case KDEBUG_FATAL:
00213 key = "FatalOutput";
00214 aCaption = "Fatal Error";
00215 nPriority = LOG_CRIT;
00216 break;
00217 case KDEBUG_ERROR:
00218 default:
00219
00220 key = "ErrorOutput";
00221 aCaption = "Error";
00222 nPriority = LOG_ERR;
00223 break;
00224 }
00225
00226 short nOutput = kDebug_data->config ? kDebug_data->config->readNumEntry(key, 4) : 4;
00227
00228
00229
00230 if (!kapp && (nOutput == 1))
00231 nOutput = 2;
00232
00233
00234 switch( nOutput )
00235 {
00236 case 0:
00237 {
00238 QString aKey;
00239 switch( nLevel )
00240 {
00241 case KDEBUG_INFO:
00242 aKey = "InfoFilename";
00243 break;
00244 case KDEBUG_WARN:
00245 aKey = "WarnFilename";
00246 break;
00247 case KDEBUG_FATAL:
00248 aKey = "FatalFilename";
00249 break;
00250 case KDEBUG_ERROR:
00251 default:
00252 aKey = "ErrorFilename";
00253 break;
00254 }
00255 QString aOutputFileName = kDebug_data->config->readEntry(aKey, "kdebug.dbg");
00256
00257 const int BUFSIZE = 4096;
00258 char buf[BUFSIZE] = "";
00259 buf[BUFSIZE-1] = '\0';
00260 int nSize;
00261 if ( !kDebug_data->aAreaName.isEmpty() )
00262 nSize = snprintf( buf, BUFSIZE-1, "%s: %s", kDebug_data->aAreaName.ascii(), data);
00263 else
00264 nSize = snprintf( buf, BUFSIZE-1, "%s", data);
00265
00266 QFile aOutputFile( aOutputFileName );
00267 aOutputFile.open( IO_WriteOnly | IO_Append );
00268 if ( ( nSize == -1 ) || ( nSize >= BUFSIZE ) )
00269 aOutputFile.writeBlock( buf, BUFSIZE-1 );
00270 else
00271 aOutputFile.writeBlock( buf, nSize );
00272 aOutputFile.close();
00273 break;
00274 }
00275 case 1:
00276 {
00277
00278
00279 if ( !kDebug_data->aAreaName.isEmpty() ) aCaption += QString("(")+kDebug_data->aAreaName+")";
00280 QMessageBox::warning( 0L, aCaption, data, i18n("&OK") );
00281 break;
00282 }
00283 case 2:
00284 {
00285 FILE *output;
00286
00287
00288
00289
00290 output = stderr;
00291
00292
00293 if ( !kDebug_data->aAreaName.isEmpty() ) fprintf( output, "%s: ", kDebug_data->aAreaName.ascii() );
00294 fputs( data, output);
00295 break;
00296 }
00297 case 3:
00298 {
00299 syslog( nPriority, "%s", data);
00300 }
00301 case 4:
00302 {
00303 }
00304 }
00305
00306
00307 if( ( nLevel == KDEBUG_FATAL )
00308 && ( !kDebug_data->config || kDebug_data->config->readNumEntry( "AbortFatal", 1 ) ) )
00309 abort();
00310 }
00311
00312 kdbgstream &perror( kdbgstream &s) { return s << QString::fromLocal8Bit(strerror(errno)); }
00313 kdbgstream kdDebug(int area) { return kdbgstream(area, KDEBUG_INFO); }
00314 kdbgstream kdDebug(bool cond, int area) { if (cond) return kdbgstream(area, KDEBUG_INFO); else return kdbgstream(0, 0, false); }
00315
00316 kdbgstream kdError(int area) { return kdbgstream("ERROR: ", area, KDEBUG_ERROR); }
00317 kdbgstream kdError(bool cond, int area) { if (cond) return kdbgstream("ERROR: ", area, KDEBUG_ERROR); else return kdbgstream(0,0,false); }
00318 kdbgstream kdWarning(int area) { return kdbgstream("WARNING: ", area, KDEBUG_WARN); }
00319 kdbgstream kdWarning(bool cond, int area) { if (cond) return kdbgstream("WARNING: ", area, KDEBUG_WARN); else return kdbgstream(0,0,false); }
00320 kdbgstream kdFatal(int area) { return kdbgstream("FATAL: ", area, KDEBUG_FATAL); }
00321 kdbgstream kdFatal(bool cond, int area) { if (cond) return kdbgstream("FATAL: ", area, KDEBUG_FATAL); else return kdbgstream(0,0,false); }
00322
00323 void kdbgstream::flush() {
00324 if (output.isEmpty() || !print)
00325 return;
00326 kDebugBackend( level, area, output.local8Bit().data() );
00327 output = QString::null;
00328 }
00329
00330 kdbgstream &kdbgstream::form(const char *format, ...)
00331 {
00332 char buf[4096];
00333 va_list arguments;
00334 va_start( arguments, format );
00335 buf[sizeof(buf)-1] = '\0';
00336 vsnprintf( buf, sizeof(buf)-1, format, arguments );
00337 va_end(arguments);
00338 *this << buf;
00339 return *this;
00340 }
00341
00342 kdbgstream::~kdbgstream() {
00343 if (!output.isEmpty()) {
00344 fprintf(stderr, "ASSERT: debug output not ended with \\n\n");
00345 *this << "\n";
00346 }
00347 }
00348
00349 kdbgstream& kdbgstream::operator << (char ch)
00350 {
00351 if (!print) return *this;
00352 if (!isprint(ch))
00353 output += "\\x" + QString::number( static_cast<uint>( ch ) + 0x100, 16 ).right(2);
00354 else {
00355 output += ch;
00356 if (ch == '\n') flush();
00357 }
00358 return *this;
00359 }
00360
00361 kdbgstream& kdbgstream::operator << (QWidget* widget)
00362 {
00363 QString string, temp;
00364
00365 if(widget==0)
00366 {
00367 string=(QString)"[Null pointer]";
00368 } else {
00369 temp.setNum((ulong)widget, 16);
00370 string=(QString)"["+widget->className()+" pointer "
00371 + "(0x" + temp + ")";
00372 if(widget->name(0)==0)
00373 {
00374 string += " to unnamed widget, ";
00375 } else {
00376 string += (QString)" to widget " + widget->name() + ", ";
00377 }
00378 string += "geometry="
00379 + QString().setNum(widget->width())
00380 + "x"+QString().setNum(widget->height())
00381 + "+"+QString().setNum(widget->x())
00382 + "+"+QString().setNum(widget->y())
00383 + "]";
00384 }
00385 if (!print)
00386 {
00387 return *this;
00388 }
00389 output += string;
00390 if (output.at(output.length() -1 ) == '\n')
00391 {
00392 flush();
00393 }
00394 return *this;
00395 }
00396
00397 QString kdBacktrace(int levels)
00398 {
00399 QString s;
00400 #ifdef HAVE_BACKTRACE
00401 void* trace[256];
00402 int n = backtrace(trace, 256);
00403 char** strings = backtrace_symbols (trace, n);
00404
00405 if ( levels != -1 )
00406 n = QMIN( n, levels );
00407 s = "[\n";
00408
00409 for (int i = 0; i < n; ++i)
00410 s += QString::number(i) +
00411 QString::fromLatin1(": ") +
00412 QString::fromLatin1(strings[i]) + QString::fromLatin1("\n");
00413 s += "]\n";
00414 free (strings);
00415 #endif
00416 return s;
00417 }
00418
00419 QString kdBacktrace()
00420 {
00421 return kdBacktrace(-1 );
00422 }
00423
00424
00425 #ifdef NDEBUG
00426 #define kdDebug kndDebug
00427 #endif