#include "signaldumper.h" #include #include #include #include #include #include #include #include #include #include #include static const uint32_t IndentSpacesCount = 4; static int iLevel = 0; SignalDumper* SignalDumper::Dumper = nullptr; static void signalDumperCallback(QObject* caller, int signal_index, void** argv) { Q_ASSERT(caller); Q_ASSERT(argv); Q_UNUSED(argv); const QMetaObject *mo = caller->metaObject(); Q_ASSERT(mo); QMetaMethod member = QMetaObjectPrivate::signal(mo, signal_index); Q_ASSERT(member.isValid()); QByteArray str; str.fill(' ', iLevel++ * IndentSpacesCount); str += "Signal: "; str += mo->className(); str += '('; QString objname = caller->objectName(); str += objname.toLocal8Bit(); if (!objname.isEmpty()) str += ' '; str += QByteArray::number(quintptr(caller), 16); str += ") "; str += member.name(); str += " ("; QList args = member.parameterTypes(); for (int i = 0; i < args.count(); ++i) { const QByteArray &arg = args.at(i); int typeId = QMetaType::type(args.at(i).constData()); if (arg.endsWith('*') || arg.endsWith('&')) { str += '('; str += arg; str += ')'; if (arg.endsWith('&')) str += '@'; quintptr addr = quintptr(*reinterpret_cast(argv[i + 1])); str.append(QByteArray::number(addr, 16)); } else if (typeId != QMetaType::UnknownType) { Q_ASSERT(typeId != QMetaType::Void); // void parameter => metaobject is corrupt str.append(arg) .append('(') .append(QVariant(typeId, argv[i + 1]).toString().toLocal8Bit()) .append(')'); } str.append(", "); } if (str.endsWith(", ")) str.chop(2); str.append(')'); SignalDumper::dumpStr(str); } static void signalDumperCallbackSlot(QObject* caller, int method_index, void** argv) { Q_ASSERT(caller); Q_ASSERT(argv); Q_UNUSED(argv); const QMetaObject *mo = caller->metaObject(); Q_ASSERT(mo); QMetaMethod member = mo->method(method_index); if (!member.isValid()) return; QByteArray str; str.fill(' ', iLevel * IndentSpacesCount); str += "Slot: "; str += mo->className(); str += '('; QString objname = caller->objectName(); str += objname.toLocal8Bit(); if (!objname.isEmpty()) str += ' '; str += QByteArray::number(quintptr(caller), 16); str += ") "; str += member.methodSignature(); SignalDumper::dumpStr(str); } static void signalDumperCallbackEndSignal(QObject* caller, int /*signal_index*/) { Q_ASSERT(caller); Q_ASSERT(caller->metaObject()); --iLevel; Q_ASSERT(iLevel >= 0); } void SignalDumper::dumpStr(QString str) { if(Dumper) { Dumper->dump(str); } } SignalDumper::SignalDumper(QObject* parent) : QObject(parent) {} SignalDumper::~SignalDumper() { if(Dumper == this) { endDump(); } } void SignalDumper::startDump() { if(Dumper) { return; } Dumper = this; // mkfifo QString fifoName = "/tmp/signalLogger." + QString::number(QCoreApplication::applicationPid()); QByteArray fifoNameArr = fifoName.toLocal8Bit(); if(::mkfifo(fifoNameArr.data(), 0700)) { qCritical() << "could not create fifo" << errno; } // make process displayProc = new QProcess(this); displayProc->start("gnome-terminal", { "--", "cat", fifoName }); if(fifo) { fifo->deleteLater(); } fifo = new QFile(fifoName); fifo->open(QIODevice::WriteOnly); iLevel = 0; // start listening static QSignalSpyCallbackSet set = { signalDumperCallback, signalDumperCallbackSlot, signalDumperCallbackEndSignal, 0 }; registerCall(set); } void SignalDumper::endDump() { if(Dumper != this) { return; } // stop listening static QSignalSpyCallbackSet nset = { 0, 0, 0 ,0 }; registerCall(nset); // kill process displayProc->terminate(); displayProc->deleteLater(); // del fifo fifo->remove(); Dumper = nullptr; } void SignalDumper::dump(QString str) { str += '\n'; QByteArray arr = str.toLocal8Bit(); fifo->write(arr); fifo->flush(); }