2

Is there a way to print the signals and slots being called?

I'm experiencing a weird deadlock in Qt which happens only in particular conditions across multiple threads, and I'd like to know the order of signals/slots being called.

Of course, for slots, I write the method body, and in the worst scenario, I can manually add a print out of the method. But the body of signals is generated automatically, so it is not possible, unless I write a custom moc, which seems like an overkill for this task...

fferri
  • 18,285
  • 5
  • 46
  • 95

1 Answers1

2

If one leverages the built-in hooks, it's possible to automatically instrument all signals, and all slots connected using the Qt 4 connect syntax. Unfortunately, QtPrivate::QSlotObject doesn't implement these hooks: slots connected using the Qt 5 syntax need to be instrumented manually (e.g. by connecting a functor to them, or adding code to them).

Signal notifications can be relied on for connected signals. Objects with no signals, and some signals of objects with other connections, will not be reported. This is presumably what you want.

Thus:

// https://github.com/KubaO/stackoverflown/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"

Output:

SIGNAL Class(0x7fff51901af0, name = "obj") "objectNameChanged(QString)"
SIGNAL Class(0x7fff51901af0, name = "obj") "aSignal()"
SLOT Class(0x7fff51901af0, name = "obj") "aSlot()"
slot
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • This is fantastic! Would you mind suggesting this to the Qt official issue tracker? It would help all Qt programmers a lot. Nevermind that getting it to work with Qt 5 or 6 might be an issue - let's get the ball rolling and see where it lands. – mrts Apr 15 '21 at 16:35
  • @mrts But it's not any sort of an issue. It's just a question someone had. It's not a Qt bug or anything like that. As for helping all Qt programmers: they surely know about StackOverflow :) There's really no higher visibility imaginable than an answer that comes up in Google search results (thanks to SO's SEO). – Kuba hasn't forgotten Monica Apr 15 '21 at 18:52
  • Well, the main issue is that it does not work with modern Qt signal-slot connections, so a wider discussion is justified. I think that noone has really seriously thought about signal-slot tracing developer experience in Qt, there are many questions like this in SO, I stumbled upon this gem among other answers. Having said that, no pushing intended :)! – mrts Apr 16 '21 at 14:47