打印 signals/slots 调用的跟踪
print trace of signals/slots called
有没有办法打印被调用的信号和槽?
我在 Qt 中遇到了一个奇怪的死锁,它只在跨多个线程的特定条件下发生,我想知道 signals/slots 被调用的顺序。
当然,对于槽,我写了方法体,最坏的情况下,我可以手动添加一个打印出来的方法。但是信号体是自动生成的,所以这是不可能的,除非我写一个自定义的 moc,这对于这个任务来说似乎有点过分了...
如果利用内置挂钩,则可以自动检测所有信号,以及使用 Qt 4 connect
语法连接的所有槽。不幸的是,QtPrivate::QSlotObject
没有实现这些钩子:需要手动检测使用 Qt 5 语法连接的插槽(例如,通过将仿函数连接到它们,或向它们添加代码)。
连接信号可以依赖信号通知。没有信号的对象,以及有其他连接的对象的一些信号,将不会被报告。这大概就是你想要的。
因此:
// https://github.com/KubaO/Whosebugn/tree/master/questions/signal-spy-39597233
#include <QtCore>
#include <private/qobject_p.h>
int signalToMethodIndex(const QMetaObject * mo, int signal)
{
Q_ASSERT(signal >= 0);
for (int i = 0; i < mo->methodCount(); ++i) {
if (mo->method(i).methodType() == QMetaMethod::Signal) {
if (signal == 0) return i;
-- signal;
}
}
return -1;
}
class Spy {
static QThreadStorage<bool> entered;
static void signalBegin(QObject *caller, int signalIndex, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
auto index = signalToMethodIndex(caller->metaObject(), signalIndex);
if (index >= 0)
qDebug() << "SIGNAL" << caller << caller->metaObject()->method(index).methodSignature();
}
static void slotBegin(QObject *caller, int index, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
qDebug() << "SLOT" << caller << caller->metaObject()->method(index).methodSignature();
}
public:
static void start() {
QSignalSpyCallbackSet set{&signalBegin, &slotBegin, nullptr, nullptr};
qt_signal_spy_callback_set = set;
}
};
QThreadStorage<bool> Spy::entered;
struct Class : QObject {
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "slot"; }
Q_OBJECT
};
int main(int argc, char ** argv) {
Spy::start();
QCoreApplication app{argc, argv};
Class obj;
QObject::connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()));
obj.setObjectName("obj");
emit obj.aSignal();
}
#include "main.moc"
输出:
SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot
有没有办法打印被调用的信号和槽?
我在 Qt 中遇到了一个奇怪的死锁,它只在跨多个线程的特定条件下发生,我想知道 signals/slots 被调用的顺序。
当然,对于槽,我写了方法体,最坏的情况下,我可以手动添加一个打印出来的方法。但是信号体是自动生成的,所以这是不可能的,除非我写一个自定义的 moc,这对于这个任务来说似乎有点过分了...
如果利用内置挂钩,则可以自动检测所有信号,以及使用 Qt 4 connect
语法连接的所有槽。不幸的是,QtPrivate::QSlotObject
没有实现这些钩子:需要手动检测使用 Qt 5 语法连接的插槽(例如,通过将仿函数连接到它们,或向它们添加代码)。
连接信号可以依赖信号通知。没有信号的对象,以及有其他连接的对象的一些信号,将不会被报告。这大概就是你想要的。
因此:
// https://github.com/KubaO/Whosebugn/tree/master/questions/signal-spy-39597233
#include <QtCore>
#include <private/qobject_p.h>
int signalToMethodIndex(const QMetaObject * mo, int signal)
{
Q_ASSERT(signal >= 0);
for (int i = 0; i < mo->methodCount(); ++i) {
if (mo->method(i).methodType() == QMetaMethod::Signal) {
if (signal == 0) return i;
-- signal;
}
}
return -1;
}
class Spy {
static QThreadStorage<bool> entered;
static void signalBegin(QObject *caller, int signalIndex, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
auto index = signalToMethodIndex(caller->metaObject(), signalIndex);
if (index >= 0)
qDebug() << "SIGNAL" << caller << caller->metaObject()->method(index).methodSignature();
}
static void slotBegin(QObject *caller, int index, void **) {
if (entered.localData()) return;
QScopedValueRollback<bool> roll{entered.localData(), true};
qDebug() << "SLOT" << caller << caller->metaObject()->method(index).methodSignature();
}
public:
static void start() {
QSignalSpyCallbackSet set{&signalBegin, &slotBegin, nullptr, nullptr};
qt_signal_spy_callback_set = set;
}
};
QThreadStorage<bool> Spy::entered;
struct Class : QObject {
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "slot"; }
Q_OBJECT
};
int main(int argc, char ** argv) {
Spy::start();
QCoreApplication app{argc, argv};
Class obj;
QObject::connect(&obj, SIGNAL(aSignal()), &obj, SLOT(aSlot()));
obj.setObjectName("obj");
emit obj.aSignal();
}
#include "main.moc"
输出:
SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot