原文地址:http://blog.163.com/net_worm/blog/static/127702419201002004518944/
在我們前面的分析中,經常看到qWarning()和qDebug()之類的調用。今天深入的分析QT的調試跟蹤系統。
我們先看QDebug.h中的宏定義:
1 #if !defined(QT_NO_DEBUG_STREAM) 2 Q_CORE_EXPORT_INLINE QDebug qDebug() { return QDebug(QtDebugMsg); } 3 4 #else // QT_NO_DEBUG_STREAM 5 #undef qDebug 6 inline QNoDebug qDebug() { return QNoDebug(); } 7 #define qDebug QT_NO_QDEBUG_MACRO 8 9 #ifdef QT_NO_MEMBER_TEMPLATES 10 template<typename T> 11 inline QNoDebug operator<<(QNoDebug debug, const T &) { return debug; } 12 #endif 13 14 #endif 15 16 #if !defined(QT_NO_WARNING_OUTPUT) 17 Q_CORE_EXPORT_INLINE QDebug qWarning() { return QDebug(QtWarningMsg); } 18 #else 19 #undef qWarning 20 inline QNoDebug qWarning() { return QNoDebug(); } 21 #define qWarning QT_NO_QWARNING_MACRO 22 #endif
這里很明顯,QT的調試跟蹤系統就是兩條:DEBUG和WARNING。先看DEBUG,如果我們定義了宏QT_NO_DEBUG_STREAM,qDebug()被定義成QNoDebug(),而QT_NO_QDEBUG_MACRO的定義:
1 #define QT_NO_QDEBUG_MACRO if(1); else qDebug
注意if后面的分號,其等價於空語句。
我們再看QNoDebug類的定義:
1 class QNoDebug 2 { 3 public: 4 inline QNoDebug(){} 5 inline QNoDebug(const QDebug &){} 6 inline ~QNoDebug(){} 7 #if !defined( QT_NO_TEXTSTREAM ) 8 inline QNoDebug &operator<<(QTextStreamFunction) { return *this; } 9 inline QNoDebug &operator<<(QTextStreamManipulator) { return *this; } 10 #endif 11 inline QNoDebug &space() { return *this; } 12 inline QNoDebug &nospace() { return *this; } 13 inline QNoDebug &maybeSpace() { return *this; } 14 15 #ifndef QT_NO_MEMBER_TEMPLATES 16 template<typename T> 17 inline QNoDebug &operator<<(const T &) { return *this; } 18 #endif 19 };
重載的<<操作只是返回其自身。另外一種情況(就是有DEBUG_STREAM)的時候,也就是缺省情況下,qDebug被定為QDebug,QDebug的輸出設備是什么呢?看QDebug類中構造的定義:
1 inline QDebug(QIODevice *device) : stream(new Stream(device)) {} 2 inline QDebug(QString *string) : stream(new Stream(string)) {} 3 inline QDebug(QtMsgType t) : stream(new Stream(t)) {} 4 inline QDebug(const QDebug &o):stream(o.stream) { ++stream->ref; }
可以知道QDebug的輸出設備可以使QString,QIODevice或者QtMsgType指定的類型。
如果我們在main()函數里面寫這樣一行程序:
1 qDebug() << "Hello world!";
其執行的效果是向stderr設備輸出"Hello world!",根據
1 Q_CORE_EXPORT_INLINE QDebug qDebug() { return QDebug(QtDebugMsg); }
定義,實際調用的是:
1 inline QDebug(QtMsgType t) : stream(new Stream(t)) {}
語句,在新生成Stream對象的時候,調用的是:
1 Stream(QtMsgType t) : ts(&buffer, QIODevice::WriteOnly), ref(1), type(t), space(true), message_output(true) {}
執行完畢之后,會釋放QDebug對象,看看QDebug的釋放:
1 inline ~QDebug() { 2 if (!--stream->ref) { 3 if(stream->message_output) 4 qt_message_output(stream->type, stream->buffer.toLocal8Bit().data()); 5 delete stream; 6 } 7 }
我們再來看qt_message_output()的代碼:
1 void qt_message_output(QtMsgType msgType, const char *buf) 2 { 3 if (handler) { 4 (*handler)(msgType, buf); 5 } else { 6 #if defined(Q_CC_MWERKS) 7 mac_default_handler(buf); 8 #elif defined(Q_OS_WINCE) 9 QString fstr = QString::fromLatin1(buf); 10 fstr += QLatin1String("\n"); 11 OutputDebugString(reinterpret_cast<const wchar_t *> (fstr.utf16())); 12 #else 13 fprintf(stderr, "%s\n", buf); 14 fflush(stderr); 15 #endif 16 } 17 18 if (msgType == QtFatalMsg 19 || (msgType == QtWarningMsg 20 && (!qgetenv("QT_FATAL_WARNINGS").isNull())) ) { 21 22 #if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR) 23 // get the current report mode 24 int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); 25 _CrtSetReportMode(_CRT_ERROR, reportMode); 26 #if !defined(Q_OS_WINCE) 27 int ret = _CrtDbgReport(_CRT_ERROR, __FILE__, __LINE__, QT_VERSION_STR, buf); 28 #else 29 int ret = _CrtDbgReportW(_CRT_ERROR, _CRT_WIDE(__FILE__), 30 __LINE__, _CRT_WIDE(QT_VERSION_STR), reinterpret_cast<const wchar_t *> (QString::fromLatin1(buf).utf16())); 31 #endif 32 if (ret == 0 && reportMode & _CRTDBG_MODE_WNDW) 33 return; // ignore 34 else if (ret == 1) 35 _CrtDbgBreak(); 36 #endif 37 38 #if (defined(Q_OS_UNIX) || defined(Q_CC_MINGW)) 39 abort(); // trap; generates core dump 40 #else 41 exit(1); // goodbye cruel world 42 #endif 43 } 44 }
首先是判斷用戶有沒有handler,如果有這個處理能力就讓用戶自己處理:
1 static QtMsgHandler handler = 0; // pointer to debug handler
在Qglobal.cpp中定義。要是想自己處理,只要讓handler指向自己的處理函數就可以了,多少有點C程序的味道。
否則的話就會輸出到stderr設備上(Win系統中非WinCE的情況)。
其他,如果是FATAL(致命)錯誤或者警告,則會調用_CrtDbgReport(),其模式是_CRT_ERROR。也就是往調試器報告致命錯誤。QWarning的實現基本類似,不再深入一步一步分析。
