dump signals
This commit is contained in:
parent
329f6561f2
commit
2922508ca5
|
@ -9,6 +9,8 @@
|
|||
class QMainWindow;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
class SignalDumper;
|
||||
|
||||
namespace Ui {
|
||||
class DebugWindow;
|
||||
}
|
||||
|
@ -25,11 +27,14 @@ private slots:
|
|||
void refresh();
|
||||
void currentObjectChanged( QTreeWidgetItem* new_, QTreeWidgetItem* old );
|
||||
void openResourceExplorer();
|
||||
void toggleDumper();
|
||||
void toggleStyle();
|
||||
|
||||
protected:
|
||||
void resetStyleSheet();
|
||||
|
||||
QMainWindow* debugWindow = nullptr;
|
||||
SignalDumper* dumper = nullptr;
|
||||
|
||||
QString oldStyleSheet;
|
||||
bool oldStyleSheetValid = false;
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
|
||||
|
||||
class QProcess;
|
||||
|
||||
class SignalDumper : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static void dumpStr(QString str);
|
||||
|
||||
explicit SignalDumper(QObject* parent);
|
||||
virtual ~SignalDumper();
|
||||
|
||||
void startDump();
|
||||
void endDump();
|
||||
|
||||
private:
|
||||
static SignalDumper* Dumper;
|
||||
|
||||
void dump(QString str);
|
||||
|
||||
QProcess* displayProc = nullptr;
|
||||
QFile* fifo = nullptr;
|
||||
};
|
||||
|
||||
// declaration of "hidden" qt features
|
||||
|
||||
#define registerCall _Z32qt_register_signal_spy_callbacksP21QSignalSpyCallbackSet
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct QSignalSpyCallbackSet
|
||||
{
|
||||
typedef void (*BeginCallback)(QObject* caller, int signal_or_method_index, void** argv);
|
||||
typedef void (*EndCallback)(QObject* caller, int signal_or_method_index);
|
||||
BeginCallback signal_begin_callback, slot_begin_callback;
|
||||
EndCallback signal_end_callback, slot_end_callback;
|
||||
};
|
||||
|
||||
// from qcoreapplication.cpp:263 : qt_register_signal_spy_callbacks
|
||||
// or qobject_p.h:82
|
||||
extern "C" void Q_CORE_EXPORT _Z32qt_register_signal_spy_callbacksP21QSignalSpyCallbackSet(const QSignalSpyCallbackSet &callback_set);
|
||||
|
||||
struct QMetaObjectPrivate
|
||||
{
|
||||
Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject* m, int signal_index);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -30,12 +30,14 @@ INCLUDEPATH += $$PWD/include/
|
|||
SOURCES += \
|
||||
src/injector.cpp \
|
||||
src/qtdebugger.cpp \
|
||||
src/resourceexplorer.cpp
|
||||
src/resourceexplorer.cpp \
|
||||
src/signaldumper.cpp
|
||||
|
||||
HEADERS += \
|
||||
include/injector.h \
|
||||
include/qtdebugger.h \
|
||||
include/resourceexplorer.h
|
||||
include/resourceexplorer.h \
|
||||
include/signaldumper.h
|
||||
|
||||
FORMS += \
|
||||
ui/debugwindow.ui \
|
||||
|
|
|
@ -12,8 +12,9 @@
|
|||
#include <set>
|
||||
|
||||
#include "resourceexplorer.h"
|
||||
#include "signaldumper.h"
|
||||
|
||||
QtDebugger::QtDebugger(QObject* parent) : QObject(parent), debugWindow( new QMainWindow() ), ui( new Ui::DebugWindow() ) {
|
||||
QtDebugger::QtDebugger(QObject* parent) : QObject(parent), debugWindow( new QMainWindow() ), dumper( new SignalDumper(this) ), ui( new Ui::DebugWindow() ) {
|
||||
this->ui->setupUi( debugWindow );
|
||||
debugWindow->show();
|
||||
|
||||
|
@ -24,6 +25,8 @@ QtDebugger::QtDebugger(QObject* parent) : QObject(parent), debugWindow( new QMai
|
|||
QObject::connect( this->ui->refreshButton, &QPushButton::clicked, this, &QtDebugger::refresh );
|
||||
QObject::connect( this->ui->objectTree, &QTreeWidget::currentItemChanged, this, &QtDebugger::currentObjectChanged );
|
||||
QObject::connect( this->ui->resourceExplorerButton, &QPushButton::clicked, this, &QtDebugger::openResourceExplorer );
|
||||
QObject::connect( this->ui->snifferBox, &QCheckBox::toggled, this, &QtDebugger::toggleDumper);
|
||||
QObject::connect( this->ui->updateStyleBox, &QCheckBox::toggled, this, &QtDebugger::toggleStyle);
|
||||
|
||||
this->refresh();
|
||||
}
|
||||
|
@ -93,7 +96,7 @@ void QtDebugger::currentObjectChanged( QTreeWidgetItem* new_, QTreeWidgetItem* o
|
|||
}
|
||||
|
||||
QWidget* widget = qobject_cast<QWidget*>( obj );
|
||||
if ( widget ) {
|
||||
if ( widget && ui->updateStyleBox->isChecked() ) {
|
||||
this->oldStyleSheet = widget->styleSheet();
|
||||
this->oldStyleSheetValid = true;
|
||||
widget->setStyleSheet( "background-color: black;" );
|
||||
|
@ -135,6 +138,24 @@ void QtDebugger::openResourceExplorer() {
|
|||
re->show();
|
||||
}
|
||||
|
||||
void QtDebugger::toggleDumper() {
|
||||
const bool newState = ui->snifferBox->isChecked();
|
||||
if(newState) {
|
||||
dumper->startDump();
|
||||
} else {
|
||||
dumper->endDump();
|
||||
}
|
||||
}
|
||||
|
||||
void QtDebugger::toggleStyle() {
|
||||
const bool newState = ui->updateStyleBox->isChecked();
|
||||
if(newState) {
|
||||
|
||||
} else {
|
||||
resetStyleSheet();
|
||||
}
|
||||
}
|
||||
|
||||
void QtDebugger::resetStyleSheet() {
|
||||
// get currently selected item
|
||||
QList<QTreeWidgetItem*> items = this->ui->objectTree->selectedItems();
|
||||
|
|
|
@ -173,7 +173,7 @@ std::vector<ResourceExplorer::MemoryMap> ResourceExplorer::readMemoryMaps() {
|
|||
QString line = QString::fromUtf8( maps.readLine() );
|
||||
if( line.isEmpty() ) continue;
|
||||
|
||||
QVector<QStringRef> parts = line.splitRef(' ', QString::SplitBehavior::SkipEmptyParts );
|
||||
QVector<QStringRef> parts = line.splitRef(' ', Qt::SkipEmptyParts );
|
||||
if ( parts.size() != 6) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
#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();
|
||||
}
|
|
@ -21,8 +21,34 @@
|
|||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="updateStyleBox">
|
||||
<property name="text">
|
||||
<string>Change Style of Selected Widget</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="resourceExplorerButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Resource Explorer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="refreshButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
|
@ -41,22 +67,10 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resourceExplorerButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="snifferBox">
|
||||
<property name="text">
|
||||
<string>Resource Explorer</string>
|
||||
<string>Enable Signal/Slot sniffer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in New Issue