QtDebugger/src/signaldumper.cpp

182 lines
4.4 KiB
C++

#include "signaldumper.h"
#include <QList>
#include <QMetaObject>
#include <QMetaType>
#include <QMetaMethod>
#include <QObject>
#include <QVariant>
#include <QCoreApplication>
#include <QProcess>
#include <QDebug>
#include <sys/types.h>
#include <sys/stat.h>
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<QByteArray> 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<void **>(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();
}