4 This file is part of GammaRay, the Qt application inspection and
7 Copyright (C) 2010-2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8 Author: Milian Wolff <milian.wolff@kdab.com>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "messagehandler.h"
26 #include "ui_messagehandler.h"
28 #include "messagemodel.h"
30 #include <QSortFilterProxyModel>
31 #include <QtCore/QMutex>
33 #include <QMessageBox>
34 #include <QListWidget>
36 #include <QDialogButtonBox>
39 static QTextStream
cerr(stdout
);
41 using namespace GammaRay
;
43 static MessageModel
*s_model
= 0;
44 static QtMsgHandler s_handler
= 0;
45 static bool s_handlerDisabled
= false;
46 static QMutex
s_mutex(QMutex::Recursive
);
48 void handleMessage(QtMsgType type
, const char *msg
)
50 ///WARNING: do not trigger *any* kind of debug output here
51 /// this would trigger an infinite loop and hence crash!
53 MessageModel::Message message
;
55 message
.message
= QString::fromLocal8Bit(msg
);
56 message
.time
= QTime::currentTime();
58 if (type
== QtCriticalMsg
|| type
== QtFatalMsg
|| type
== QtWarningMsg
) {
59 message
.backtrace
= getBacktrace(50);
60 // remove trailing internal functions
61 // be a bit careful and first make sure that we find this function...
62 // TODO: go even higher until qWarning/qFatal/qDebug/... ?
64 for (int i
= 0; i
< message
.backtrace
.size(); ++i
) {
65 if (message
.backtrace
.at(i
).contains(QLatin1String("handleMessage"))) {
70 if (removeUntil
!= -1) {
71 message
.backtrace
= message
.backtrace
.mid(removeUntil
+ 1);
75 if (type
== QtFatalMsg
&& qgetenv("GAMMARAY_GDB") != "1" && qgetenv("GAMMARAY_UNITTEST") != "1" &&
76 QThread::currentThread() == QApplication::instance()->thread()) {
77 foreach (QWidget
*w
, qApp
->topLevelWidgets()) {
81 dlg
.setWindowTitle(QObject::tr("QFatal in %1").
82 arg(qApp
->applicationName().isEmpty() ?
83 qApp
->applicationFilePath() :
84 qApp
->applicationName()));
85 QGridLayout
*layout
= new QGridLayout
;
86 QLabel
*iconLabel
= new QLabel
;
87 QIcon icon
= dlg
.style()->standardIcon(QStyle::SP_MessageBoxCritical
, 0, &dlg
);
88 int iconSize
= dlg
.style()->pixelMetric(QStyle::PM_MessageBoxIconSize
, 0, &dlg
);
89 iconLabel
->setPixmap(icon
.pixmap(iconSize
, iconSize
));
90 iconLabel
->setSizePolicy(QSizePolicy::Fixed
, QSizePolicy::Fixed
);
91 layout
->addWidget(iconLabel
, 0, 0);
92 QLabel
*errorLabel
= new QLabel
;
93 errorLabel
->setTextFormat(Qt::PlainText
);
94 errorLabel
->setText(message
.message
);
95 layout
->addWidget(errorLabel
, 0, 1);
96 if (!message
.backtrace
.isEmpty()) {
97 QListWidget
*backtrace
= new QListWidget
;
98 foreach (const QString
&frame
, message
.backtrace
) {
99 backtrace
->addItem(frame
);
101 layout
->addWidget(backtrace
, 1, 0, 1, 2);
103 QDialogButtonBox
*buttons
= new QDialogButtonBox
;
104 buttons
->addButton(QDialogButtonBox::Close
);
105 QObject::connect(buttons
, SIGNAL(accepted()),
106 &dlg
, SLOT(accept()));
107 QObject::connect(buttons
, SIGNAL(rejected()),
108 &dlg
, SLOT(reject()));
109 layout
->addWidget(buttons
, 2, 0, 1, 2);
110 dlg
.setLayout(layout
);
113 } else if (!message
.backtrace
.isEmpty() &&
114 (qgetenv("GAMMARAY_UNITTEST") == "1" || type
== QtFatalMsg
)) {
115 cerr
<< "START BACKTRACE:" << endl
;
117 foreach (const QString
&frame
, message
.backtrace
) {
118 cerr
<< (++i
) << "\t" << frame
<< endl
;
120 cerr
<< "END BACKTRACE" << endl
;
123 // reset msg handler so the app still works as usual
124 // but make sure we don't let other threads bypass our
125 // handler during that time
126 QMutexLocker
lock(&s_mutex
);
127 s_handlerDisabled
= true;
128 qInstallMsgHandler(s_handler
);
129 qt_message_output(type
, msg
);
130 qInstallMsgHandler(handleMessage
);
131 s_handlerDisabled
= false;
135 // add directly from foreground thread, delay from background thread
136 QMetaObject::invokeMethod(s_model
, "addMessage", Qt::AutoConnection
,
137 Q_ARG(MessageModel::Message
, message
));
141 MessageHandler::MessageHandler(ProbeInterface
* /*probe*/, QWidget
*parent
)
143 ui(new Ui::MessageHandler
),
145 m_messageProxy(new QSortFilterProxyModel(this))
149 ui
->messageSearchLine
->setProxy(m_messageProxy
);
150 ui
->messageView
->setModel(m_messageProxy
);
151 ui
->messageView
->setIndentation(0);
152 ui
->messageView
->setSortingEnabled(true);
154 ///FIXME: implement this
155 ui
->backtraceView
->hide();
158 void MessageHandler::setModel(MessageModel
*model
)
160 m_messageModel
= model
;
161 m_messageProxy
->setSourceModel(m_messageModel
);
164 MessageHandlerFactory::MessageHandlerFactory(QObject
*parent
)
166 m_messageModel(new MessageModel(this))
168 Q_ASSERT(s_model
== 0);
169 s_model
= m_messageModel
;
171 // install handler directly, catches most cases,
172 // i.e. user has no special handler or the handler
173 // is created before the QApplication
174 ensureHandlerInstalled();
175 // recheck when eventloop is entered, if the user
176 // installs a handler after QApp but before .exec()
177 QMetaObject::invokeMethod(this, "ensureHandlerInstalled", Qt::QueuedConnection
);
180 void MessageHandlerFactory::ensureHandlerInstalled()
182 QMutexLocker
lock(&s_mutex
);
184 if (s_handlerDisabled
) {
188 QtMsgHandler prevHandler
= qInstallMsgHandler(handleMessage
);
190 if (prevHandler
!= handleMessage
) {
191 s_handler
= prevHandler
;
195 MessageHandlerFactory::~MessageHandlerFactory()
197 QMutexLocker
lock(&s_mutex
);
200 QtMsgHandler oldHandler
= qInstallMsgHandler(s_handler
);
201 if (oldHandler
!= handleMessage
) {
202 // ups, the app installed it's own handler after ours...
203 qInstallMsgHandler(oldHandler
);
208 QWidget
*MessageHandlerFactory::createWidget(ProbeInterface
*probe
, QWidget
*parentWidget
)
211 StandardToolFactory
<QObject
, MessageHandler
>::createWidget(probe
, parentWidget
);
213 MessageHandler
*handler
= qobject_cast
<MessageHandler
*>(widget
);
215 handler
->setModel(m_messageModel
);
219 #include "messagehandler.moc"