initial
This commit is contained in:
commit
c792e02da8
|
@ -0,0 +1,6 @@
|
|||
*.so*
|
||||
ui/*.h
|
||||
build/
|
||||
Makefile
|
||||
.qmake.stash
|
||||
.vscode/settings.json
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QMainWindow;
|
||||
class QTreeWidgetItem;
|
||||
|
||||
namespace Ui {
|
||||
class DebugWindow;
|
||||
}
|
||||
|
||||
|
||||
class QtDebugger : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QtDebugger(QObject* parent = nullptr);
|
||||
~QtDebugger();
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
void currentWindowChanged( int row );
|
||||
void currentObjectChanged( QTreeWidgetItem* new_ );
|
||||
|
||||
protected:
|
||||
QMainWindow* debugWindow = nullptr;
|
||||
|
||||
private:
|
||||
Ui::DebugWindow* ui;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
TEMPLATE = lib
|
||||
|
||||
QT += core gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
CONFIG += c++17
|
||||
|
||||
# debugging
|
||||
|
||||
LIBS += -ldl
|
||||
CONFIG += debug
|
||||
|
||||
# The following define makes your compiler emit warnings if you use
|
||||
# any Qt feature that has been marked deprecated (the exact warnings
|
||||
# depend on your compiler). Please consult the documentation of the
|
||||
# deprecated API in order to know how to port your code away from it.
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
# You can also make your code fail to compile if it uses deprecated APIs.
|
||||
# In order to do so, uncomment the following line.
|
||||
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||
|
||||
MOC_DIR = build/generated/
|
||||
UI_DIR = ui/
|
||||
OBJECTS_DIR = build/
|
||||
INCLUDEPATH += $$PWD/include/
|
||||
|
||||
SOURCES += \
|
||||
src/injector.cpp \
|
||||
src/qtdebugger.cpp
|
||||
|
||||
HEADERS += \
|
||||
include/qtdebugger.h
|
||||
|
||||
FORMS += \
|
||||
ui/debugwindow.ui
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
// this file injects the debugger into the QApplication
|
||||
|
||||
#include "qtdebugger.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
class QApplication;
|
||||
|
||||
int QApplication::exec() {
|
||||
// init debugger
|
||||
QtDebugger debugger;
|
||||
|
||||
// get real exec call
|
||||
static int (*real_exec)() = (int (*)())dlsym(RTLD_NEXT, "_ZN12QApplication4execEv");
|
||||
if ( !real_exec ) {
|
||||
// failed to init real exec
|
||||
return -1;
|
||||
}
|
||||
|
||||
// call real exec call
|
||||
int returnVal = real_exec();
|
||||
|
||||
// end debugger
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#include "qtdebugger.h"
|
||||
#include "ui_debugwindow.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QMetaProperty>
|
||||
#include <QMainWindow>
|
||||
#include <QSplitter>
|
||||
#include <QWindow>
|
||||
|
||||
#include <functional>
|
||||
#include <set>
|
||||
|
||||
QtDebugger::QtDebugger(QObject* parent) : QObject(parent), debugWindow( new QMainWindow() ), ui( new Ui::DebugWindow() ) {
|
||||
this->ui->setupUi( debugWindow );
|
||||
debugWindow->show();
|
||||
|
||||
QList<int> sizes;
|
||||
sizes << 40 << 70 << 150 << 490;
|
||||
this->ui->splitter->setSizes( sizes );
|
||||
|
||||
QObject::connect( this->ui->refreshButton, &QPushButton::clicked, this, &QtDebugger::refresh );
|
||||
QObject::connect( this->ui->windowList, &QListWidget::currentRowChanged, this, &QtDebugger::currentWindowChanged );
|
||||
QObject::connect( this->ui->objectTree, &QTreeWidget::currentItemChanged, this, &QtDebugger::currentObjectChanged );
|
||||
|
||||
this->refresh();
|
||||
}
|
||||
|
||||
QtDebugger::~QtDebugger() {
|
||||
delete this->debugWindow;
|
||||
delete this->ui;
|
||||
}
|
||||
|
||||
void QtDebugger::refresh() {
|
||||
this->ui->windowList->clear();
|
||||
this->ui->objectTree->clear();
|
||||
this->ui->propertiesTree->clear();
|
||||
|
||||
// get windows
|
||||
QWindowList windows = qApp->allWindows();
|
||||
for( const QWindow* win : windows ) {
|
||||
QListWidgetItem* item = new QListWidgetItem( win->title() );
|
||||
if ( win->winId() == debugWindow->winId() ) {
|
||||
// not selectable or anything
|
||||
item->setFlags( item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled) );
|
||||
}
|
||||
item->setData(Qt::UserRole, win->winId());
|
||||
this->ui->windowList->addItem( item );
|
||||
}
|
||||
|
||||
this->currentWindowChanged( 0 );
|
||||
}
|
||||
|
||||
void QtDebugger::currentWindowChanged( int row ) {
|
||||
this->ui->objectTree->clear();
|
||||
this->ui->propertiesTree->clear();
|
||||
|
||||
if ( row == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// find the window id
|
||||
QListWidgetItem* item = this->ui->windowList->item( row );
|
||||
if( !item ) {
|
||||
return;
|
||||
}
|
||||
QVariant data = item->data(Qt::UserRole);
|
||||
bool ok = true;
|
||||
WId wid = data.toULongLong( &ok );
|
||||
|
||||
// find the window
|
||||
QWindowList windows = qApp->allWindows();
|
||||
auto it = std::find_if( windows.constBegin(), windows.constEnd(), [wid](const QWindow* w){ return w->winId() == wid; } );
|
||||
if( !ok || it == windows.constEnd() ) {
|
||||
qWarning() << "invalid window";
|
||||
return;
|
||||
}
|
||||
const QWindow* win = *it;
|
||||
|
||||
// update the tree
|
||||
QTreeWidgetItem* root = new QTreeWidgetItem( );
|
||||
root->setText( 0, "root" );
|
||||
this->ui->objectTree->insertTopLevelItem( 0, root );
|
||||
|
||||
std::function<void(const QObject*, uint32_t, uint32_t, QTreeWidgetItem*)> walkTree = [&](const QObject* obj, uint32_t maxdepth, uint32_t currentDepth, QTreeWidgetItem* root) {
|
||||
if ( !obj ) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem( );
|
||||
item->setText(0, obj->metaObject()->className() );
|
||||
item->setText(1, obj->objectName() );
|
||||
item->setData(0, Qt::UserRole, QVariant((unsigned long long) obj));
|
||||
|
||||
root->addChild( item );
|
||||
if( currentDepth < maxdepth ) {
|
||||
const QObjectList& childs = obj->children();
|
||||
for( const QObject* child : childs ) {
|
||||
walkTree( child, maxdepth, currentDepth+1, item );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
walkTree( qApp, 128, 0, root);
|
||||
walkTree( win, 128, 0, root);
|
||||
}
|
||||
|
||||
void QtDebugger::currentObjectChanged( QTreeWidgetItem* new_ ) {
|
||||
this->ui->propertiesTree->clear();
|
||||
|
||||
if ( !new_ ) {
|
||||
qWarning() << "invalid row";
|
||||
return;
|
||||
}
|
||||
|
||||
// find the object
|
||||
QVariant variant = new_->data(0, Qt::UserRole);
|
||||
QObject* obj = (QObject*) variant.toULongLong();
|
||||
if( !obj ) {
|
||||
qWarning() << "invalid Object";
|
||||
return;
|
||||
}
|
||||
|
||||
// update the tree
|
||||
std::function<void(const QMetaObject*)> readProperties
|
||||
= [&] (const QMetaObject* meta) {
|
||||
if (!meta) {
|
||||
return;
|
||||
}
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
item->setText(0, meta->className());
|
||||
this->ui->propertiesTree->insertTopLevelItem( 0, item );
|
||||
item->setExpanded( true );
|
||||
|
||||
// read superclass properties
|
||||
readProperties(meta->superClass());
|
||||
|
||||
for( int32_t i = meta->propertyOffset(); i < meta->propertyCount(); ++i ) {
|
||||
QMetaProperty prop = meta->property(i);
|
||||
QString propName = prop.name();
|
||||
QVariant propVal = prop.read(obj);
|
||||
// QString value = propVal.canConvert<QString>() ? propVal.value<QString>() : "<unconvertable>";
|
||||
|
||||
QTreeWidgetItem* propItem = new QTreeWidgetItem();
|
||||
propItem->setText(0, prop.typeName());
|
||||
propItem->setText(1, propName);
|
||||
propItem->setData(2, Qt::DisplayRole, propVal);
|
||||
item->addChild( propItem );
|
||||
}
|
||||
};
|
||||
readProperties( obj->metaObject() );
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DebugWindow</class>
|
||||
<widget class="QMainWindow" name="DebugWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>511</width>
|
||||
<height>812</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>DebugWindow</string>
|
||||
</property>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QPushButton" name="refreshButton">
|
||||
<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>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="windowList"/>
|
||||
<widget class="QTreeWidget" name="objectTree">
|
||||
<property name="indentation">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>200</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">Name</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QTreeWidget" name="propertiesTree">
|
||||
<property name="columnCount">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>200</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">Type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>511</width>
|
||||
<height>24</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Reference in New Issue