1
0
Fork 0

Delay at least 100ms between individual UI updates

Thanks to @JustasMasiulis for the help!
This commit is contained in:
Duncan Ogilvie 2023-11-27 23:09:19 +01:00
parent 570aaea06d
commit f56fa5ce23
6 changed files with 254 additions and 124 deletions

View File

@ -28,20 +28,188 @@ class BridgeArchitecture : public Architecture
/************************************************************************************
Class Members
************************************************************************************/
static const char* msg2str(GUIMSG msg)
{
switch(msg)
{
case GUI_UPDATE_REGISTER_VIEW:
return "GUI_UPDATE_REGISTER_VIEW";
case GUI_UPDATE_DISASSEMBLY_VIEW:
return "GUI_UPDATE_DISASSEMBLY_VIEW";
case GUI_UPDATE_BREAKPOINTS_VIEW:
return "GUI_UPDATE_BREAKPOINTS_VIEW";
case GUI_UPDATE_DUMP_VIEW:
return "GUI_UPDATE_DUMP_VIEW";
case GUI_UPDATE_THREAD_VIEW:
return "GUI_UPDATE_THREAD_VIEW";
case GUI_UPDATE_MEMORY_VIEW:
return "GUI_UPDATE_MEMORY_VIEW";
case GUI_UPDATE_SIDEBAR:
return "GUI_UPDATE_SIDEBAR";
case GUI_REPAINT_TABLE_VIEW:
return "GUI_REPAINT_TABLE_VIEW";
case GUI_UPDATE_PATCHES:
return "GUI_UPDATE_PATCHES";
case GUI_UPDATE_CALLSTACK:
return "GUI_UPDATE_CALLSTACK";
case GUI_UPDATE_SEHCHAIN:
return "GUI_UPDATE_SEHCHAIN";
case GUI_UPDATE_TIME_WASTED_COUNTER:
return "GUI_UPDATE_TIME_WASTED_COUNTER";
case GUI_UPDATE_ARGUMENT_VIEW:
return "GUI_UPDATE_ARGUMENT_VIEW";
case GUI_UPDATE_WATCH_VIEW:
return "GUI_UPDATE_WATCH_VIEW";
case GUI_UPDATE_GRAPH_VIEW:
return "GUI_UPDATE_GRAPH_VIEW";
case GUI_UPDATE_TYPE_WIDGET:
return "GUI_UPDATE_TYPE_WIDGET";
case GUI_UPDATE_TRACE_BROWSER:
return "GUI_UPDATE_TRACE_BROWSER";
default:
return "<unknown message>";
}
}
void Bridge::throttleUpdateSlot(GUIMSG msg)
{
// NOTE: This is running synchronously on the UI thread
auto lastUpdate = mLastUpdates[msg];
auto now = GetTickCount();
auto elapsed = now - lastUpdate;
const auto interval = 100;
if(lastUpdate > 0 && elapsed < interval)
{
//qDebug() << "Delay update:" << msg2str(msg);
QTimer* timer = mUpdateTimers[msg];
if(timer == nullptr)
{
timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [this, msg]
{
doUpdate(msg);
});
mUpdateTimers[msg] = timer;
}
if(!timer->isActive())
{
timer->setInterval(interval - elapsed);
timer->start();
}
}
else
{
//qDebug() << "No delay: " << msg2str(msg);
doUpdate(msg);
}
}
void Bridge::doUpdate(GUIMSG msg)
{
auto start = GetTickCount();
switch(msg)
{
case GUI_UPDATE_REGISTER_VIEW:
updateRegisters();
break;
case GUI_UPDATE_DISASSEMBLY_VIEW:
updateDisassembly();
break;
case GUI_UPDATE_BREAKPOINTS_VIEW:
updateBreakpoints();
break;
case GUI_UPDATE_DUMP_VIEW:
updateDump();
break;
case GUI_UPDATE_THREAD_VIEW:
updateThreads();
break;
case GUI_UPDATE_MEMORY_VIEW:
updateMemory();
break;
case GUI_UPDATE_SIDEBAR:
updateSideBar();
break;
case GUI_REPAINT_TABLE_VIEW:
repaintTableView();
break;
case GUI_UPDATE_PATCHES:
updatePatches();
break;
case GUI_UPDATE_CALLSTACK:
updateCallStack();
break;
case GUI_UPDATE_SEHCHAIN:
updateSEHChain();
break;
case GUI_UPDATE_TIME_WASTED_COUNTER:
updateTimeWastedCounter();
break;
case GUI_UPDATE_ARGUMENT_VIEW:
updateArgumentView();
break;
case GUI_UPDATE_WATCH_VIEW:
updateWatch();
break;
case GUI_UPDATE_GRAPH_VIEW:
updateGraph();
break;
case GUI_UPDATE_TYPE_WIDGET:
typeUpdateWidget();
break;
case GUI_UPDATE_TRACE_BROWSER:
updateTraceBrowser();
break;
default:
__debugbreak();
}
// Log potentially bottlenecked updates
auto now = GetTickCount();
auto elapsed = now - start;
if(elapsed > 5)
qDebug() << msg2str(msg) << elapsed << "ms";
mLastUpdates[msg] = now;
}
Bridge::Bridge(QObject* parent) : QObject(parent)
{
InitializeCriticalSection(&csBridge);
InitializeCriticalSection(&mCsBridge);
for(size_t i = 0; i < BridgeResult::Last; i++)
resultEvents[i] = CreateEventW(nullptr, true, true, nullptr);
dwMainThreadId = GetCurrentThreadId();
mResultEvents[i] = CreateEventW(nullptr, true, true, nullptr);
mMainThreadId = GetCurrentThreadId();
connect(this, &Bridge::throttleUpdate, this, &Bridge::throttleUpdateSlot);
}
Bridge::~Bridge()
{
EnterCriticalSection(&csBridge);
EnterCriticalSection(&mCsBridge);
for(size_t i = 0; i < BridgeResult::Last; i++)
CloseHandle(resultEvents[i]);
DeleteCriticalSection(&csBridge);
CloseHandle(mResultEvents[i]);
DeleteCriticalSection(&mCsBridge);
}
void Bridge::CopyToClipboard(const QString & text)
@ -68,8 +236,8 @@ void Bridge::setResult(BridgeResult::Type type, dsint result)
#ifdef DEBUG
OutputDebugStringA(QString().sprintf("[x64dbg] [%u] Bridge::setResult(%d, %p)\n", GetCurrentThreadId(), type, result).toUtf8().constData());
#endif //DEBUG
bridgeResults[type] = result;
SetEvent(resultEvents[type]);
mBridgeResults[type] = result;
SetEvent(mResultEvents[type]);
}
/************************************************************************************
@ -103,7 +271,7 @@ void Bridge::emitMenuAddToList(QWidget* parent, QMenu* menu, GUIMENUTYPE hMenu,
void Bridge::setDbgStopped()
{
dbgStopped = true;
mDbgStopped = true;
}
/************************************************************************************
@ -112,7 +280,7 @@ void Bridge::setDbgStopped()
void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
{
if(dbgStopped) //there can be no more messages if the debugger stopped = IGNORE
if(mDbgStopped) //there can be no more messages if the debugger stopped = IGNORE
return nullptr;
switch(type)
{
@ -160,24 +328,12 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
emit redirectLogStop();
break;
case GUI_UPDATE_REGISTER_VIEW:
emit updateRegisters();
break;
case GUI_UPDATE_DISASSEMBLY_VIEW:
emit updateDisassembly();
break;
case GUI_UPDATE_BREAKPOINTS_VIEW:
emit updateBreakpoints();
break;
case GUI_UPDATE_WINDOW_TITLE:
emit updateWindowTitle(QString((const char*)param1));
break;
case GUI_GET_WINDOW_HANDLE:
return winId;
return mWinId;
case GUI_DUMP_AT:
emit dumpAt((dsint)param1);
@ -252,25 +408,25 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
break;
case GUI_REF_ADDCOLUMN:
if(referenceManager->currentReferenceView())
referenceManager->currentReferenceView()->addColumnAtRef((int)param1, QString((const char*)param2));
if(mReferenceManager->currentReferenceView())
mReferenceManager->currentReferenceView()->addColumnAtRef((int)param1, QString((const char*)param2));
break;
case GUI_REF_SETROWCOUNT:
{
if(referenceManager->currentReferenceView())
referenceManager->currentReferenceView()->setRowCount((dsint)param1);
if(mReferenceManager->currentReferenceView())
mReferenceManager->currentReferenceView()->setRowCount((dsint)param1);
}
break;
case GUI_REF_GETROWCOUNT:
if(referenceManager->currentReferenceView())
return (void*)referenceManager->currentReferenceView()->stdList()->getRowCount();
if(mReferenceManager->currentReferenceView())
return (void*)mReferenceManager->currentReferenceView()->stdList()->getRowCount();
return 0;
case GUI_REF_SEARCH_GETROWCOUNT:
if(referenceManager->currentReferenceView())
return (void*)referenceManager->currentReferenceView()->mCurList->getRowCount();
if(mReferenceManager->currentReferenceView())
return (void*)mReferenceManager->currentReferenceView()->mCurList->getRowCount();
return 0;
case GUI_REF_DELETEALLCOLUMNS:
@ -280,16 +436,16 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
case GUI_REF_SETCELLCONTENT:
{
CELLINFO* info = (CELLINFO*)param1;
if(referenceManager->currentReferenceView())
referenceManager->currentReferenceView()->setCellContent(info->row, info->col, QString(info->str));
if(mReferenceManager->currentReferenceView())
mReferenceManager->currentReferenceView()->setCellContent(info->row, info->col, QString(info->str));
}
break;
case GUI_REF_GETCELLCONTENT:
{
QString content;
if(referenceManager->currentReferenceView())
content = referenceManager->currentReferenceView()->stdList()->getCellContent((int)param1, (int)param2);
if(mReferenceManager->currentReferenceView())
content = mReferenceManager->currentReferenceView()->stdList()->getCellContent((int)param1, (int)param2);
auto bytes = content.toUtf8();
auto data = BridgeAlloc(bytes.size() + 1);
memcpy(data, bytes.constData(), bytes.size());
@ -299,8 +455,8 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
case GUI_REF_SEARCH_GETCELLCONTENT:
{
QString content;
if(referenceManager->currentReferenceView())
content = referenceManager->currentReferenceView()->mCurList->getCellContent((int)param1, (int)param2);
if(mReferenceManager->currentReferenceView())
content = mReferenceManager->currentReferenceView()->mCurList->getCellContent((int)param1, (int)param2);
auto bytes = content.toUtf8();
auto data = BridgeAlloc(bytes.size() + 1);
memcpy(data, bytes.constData(), bytes.size());
@ -316,26 +472,26 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
break;
case GUI_REF_SETPROGRESS:
if(referenceManager->currentReferenceView())
if(mReferenceManager->currentReferenceView())
{
auto newProgress = (int)param1;
if(referenceManager->currentReferenceView()->progress() != newProgress)
if(mReferenceManager->currentReferenceView()->progress() != newProgress)
emit referenceSetProgress(newProgress);
}
break;
case GUI_REF_SETCURRENTTASKPROGRESS:
if(referenceManager->currentReferenceView())
if(mReferenceManager->currentReferenceView())
{
auto newProgress = (int)param1;
if(referenceManager->currentReferenceView()->currentTaskProgress() != newProgress)
if(mReferenceManager->currentReferenceView()->currentTaskProgress() != newProgress)
emit referenceSetCurrentTaskProgress((int)param1, QString((const char*)param2));
}
break;
case GUI_REF_SETSEARCHSTARTCOL:
if(referenceManager->currentReferenceView())
referenceManager->currentReferenceView()->setSearchStartCol((duint)param1);
if(mReferenceManager->currentReferenceView())
mReferenceManager->currentReferenceView()->setSearchStartCol((duint)param1);
break;
case GUI_REF_INITIALIZE:
@ -350,18 +506,6 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
emit stackDumpAt((duint)param1, (duint)param2);
break;
case GUI_UPDATE_DUMP_VIEW:
emit updateDump();
break;
case GUI_UPDATE_THREAD_VIEW:
emit updateThreads();
break;
case GUI_UPDATE_MEMORY_VIEW:
emit updateMemory();
break;
case GUI_ADD_RECENT_FILE:
emit addRecentFile(QString((const char*)param1));
break;
@ -619,26 +763,6 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
emit addMsgToStatusBar(QString((const char*)param1));
break;
case GUI_UPDATE_SIDEBAR:
emit updateSideBar();
break;
case GUI_REPAINT_TABLE_VIEW:
emit repaintTableView();
break;
case GUI_UPDATE_PATCHES:
emit updatePatches();
break;
case GUI_UPDATE_CALLSTACK:
emit updateCallStack();
break;
case GUI_UPDATE_SEHCHAIN:
emit updateSEHChain();
break;
case GUI_SYMBOL_REFRESH_CURRENT:
emit symbolRefreshCurrent();
break;
@ -669,17 +793,13 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
case GUI_EXECUTE_ON_GUI_THREAD:
{
if(GetCurrentThreadId() == dwMainThreadId)
if(GetCurrentThreadId() == mMainThreadId)
((GUICALLBACKEX)param1)(param2);
else
emit executeOnGuiThread(param1, param2);
}
break;
case GUI_UPDATE_TIME_WASTED_COUNTER:
emit updateTimeWastedCounter();
break;
case GUI_SET_GLOBAL_NOTES:
{
QString text = QString((const char*)param1);
@ -734,10 +854,6 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
emit unregisterScriptLang((int)param1);
break;
case GUI_UPDATE_ARGUMENT_VIEW:
emit updateArgumentView();
break;
case GUI_FOCUS_VIEW:
{
int hWindow = int(param1);
@ -764,10 +880,6 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
}
break;
case GUI_UPDATE_WATCH_VIEW:
emit updateWatch();
break;
case GUI_LOAD_GRAPH:
{
BridgeResult result(BridgeResult::LoadGraph);
@ -784,17 +896,13 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
}
break;
case GUI_UPDATE_GRAPH_VIEW:
emit updateGraph();
break;
case GUI_SET_LOG_ENABLED:
loggingEnabled = param1 != 0;
emit setLogEnabled(loggingEnabled);
mLoggingEnabled = param1 != 0;
emit setLogEnabled(mLoggingEnabled);
break;
case GUI_IS_LOG_ENABLED:
return (void*)loggingEnabled;
return (void*)mLoggingEnabled;
case GUI_ADD_FAVOURITE_TOOL:
{
@ -883,10 +991,6 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
}
break;
case GUI_UPDATE_TYPE_WIDGET:
emit typeUpdateWidget();
break;
case GUI_CLOSE_APPLICATION:
emit closeApplication();
break;
@ -914,12 +1018,8 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
}
break;
case GUI_UPDATE_TRACE_BROWSER:
emit updateTraceBrowser();
break;
case GUI_INVALIDATE_SYMBOL_SOURCE:
symbolView->invalidateSymbolSource(duint(param1));
mSymbolView->invalidateSymbolSource(duint(param1));
break;
case GUI_GET_CURRENT_GRAPH:
@ -947,7 +1047,28 @@ void* Bridge::processMessage(GUIMSG type, void* param1, void* param2)
break;
case GUI_GET_MAIN_THREAD_ID:
return (void*)dwMainThreadId;
return (void*)mMainThreadId;
case GUI_UPDATE_REGISTER_VIEW:
case GUI_UPDATE_DISASSEMBLY_VIEW:
case GUI_UPDATE_BREAKPOINTS_VIEW:
case GUI_UPDATE_DUMP_VIEW:
case GUI_UPDATE_THREAD_VIEW:
case GUI_UPDATE_MEMORY_VIEW:
case GUI_UPDATE_SIDEBAR:
case GUI_REPAINT_TABLE_VIEW:
case GUI_UPDATE_PATCHES:
case GUI_UPDATE_CALLSTACK:
case GUI_UPDATE_SEHCHAIN:
case GUI_UPDATE_TIME_WASTED_COUNTER:
case GUI_UPDATE_ARGUMENT_VIEW:
case GUI_UPDATE_WATCH_VIEW:
case GUI_UPDATE_GRAPH_VIEW:
case GUI_UPDATE_TYPE_WIDGET:
case GUI_UPDATE_TRACE_BROWSER:
// NOTE: this can run on any thread.
emit throttleUpdate(type);
break;
}
return nullptr;

View File

@ -26,6 +26,11 @@ class Bridge : public QObject
friend class BridgeResult;
void doUpdate(GUIMSG msg);
private slots:
void throttleUpdateSlot(GUIMSG msg);
public:
explicit Bridge(QObject* parent = nullptr);
~Bridge();
@ -49,12 +54,12 @@ public:
void setDbgStopped();
//Public variables
void* winId = nullptr;
ReferenceManager* referenceManager = nullptr;
void* mWinId = nullptr;
ReferenceManager* mReferenceManager = nullptr;
bool mIsRunning = false;
duint mLastCip = 0;
SymbolView* symbolView = nullptr;
bool loggingEnabled = true;
SymbolView* mSymbolView = nullptr;
bool mLoggingEnabled = true;
signals:
void disassembleAt(duint va, duint eip);
@ -183,11 +188,14 @@ signals:
void showReferences();
void gotoTraceIndex(duint index);
void showTraceBrowser();
void throttleUpdate(GUIMSG msg);
private:
CRITICAL_SECTION csBridge;
HANDLE resultEvents[BridgeResult::Last];
duint bridgeResults[BridgeResult::Last];
DWORD dwMainThreadId = 0;
volatile bool dbgStopped = false;
CRITICAL_SECTION mCsBridge;
HANDLE mResultEvents[BridgeResult::Last];
duint mBridgeResults[BridgeResult::Last];
DWORD mMainThreadId = 0;
volatile bool mDbgStopped = false;
QMap<GUIMSG, DWORD> mLastUpdates;
QMap<GUIMSG, QTimer*> mUpdateTimers;
};

View File

@ -6,11 +6,11 @@ BridgeResult::BridgeResult(Type type)
: mType(type)
{
Bridge* bridge = Bridge::getBridge();
EnterCriticalSection(&bridge->csBridge);
EnterCriticalSection(&bridge->mCsBridge);
#ifdef DEBUG
OutputDebugStringA(QString().sprintf("[x64dbg] [%u] BridgeResult(%d)\n", GetCurrentThreadId(), type).toUtf8().constData());
#endif //DEBUG
ResetEvent(bridge->resultEvents[type]);
ResetEvent(bridge->mResultEvents[type]);
}
BridgeResult::~BridgeResult()
@ -18,7 +18,7 @@ BridgeResult::~BridgeResult()
#ifdef DEBUG
OutputDebugStringA(QString().sprintf("[x64dbg] [%u] ~BridgeResult(%d)\n", GetCurrentThreadId(), mType).toUtf8().constData());
#endif //DEBUG
LeaveCriticalSection(&Bridge::getBridge()->csBridge);
LeaveCriticalSection(&Bridge::getBridge()->mCsBridge);
}
dsint BridgeResult::Wait()
@ -27,9 +27,9 @@ dsint BridgeResult::Wait()
OutputDebugStringA(QString().sprintf("[x64dbg] [%u] BridgeResult::Wait(%d)\n", GetCurrentThreadId(), mType).toUtf8().constData());
#endif //DEBUG
Bridge* bridge = Bridge::getBridge();
HANDLE hResultEvent = bridge->resultEvents[mType];
HANDLE hResultEvent = bridge->mResultEvents[mType];
//Don't freeze when waiting on the main thread (https://github.com/x64dbg/x64dbg/issues/1716)
if(GetCurrentThreadId() == bridge->dwMainThreadId)
if(GetCurrentThreadId() == bridge->mMainThreadId)
while(WaitForSingleObject(hResultEvent, 10) == WAIT_TIMEOUT)
QCoreApplication::processEvents();
else
@ -37,5 +37,5 @@ dsint BridgeResult::Wait()
#ifdef DEBUG
OutputDebugStringA(QString().sprintf("[x64dbg] [%u] BridgeResult::~Wait(%d)\n", GetCurrentThreadId(), mType).toUtf8().constData());
#endif //DEBUG
return bridge->bridgeResults[mType];
return bridge->mBridgeResults[mType];
}

View File

@ -154,7 +154,7 @@ MainWindow::MainWindow(QWidget* parent)
// Symbol view
mSymbolView = new SymbolView();
Bridge::getBridge()->symbolView = mSymbolView;
Bridge::getBridge()->mSymbolView = mSymbolView;
mSymbolView->setWindowTitle(tr("Symbols"));
mSymbolView->setWindowIcon(DIcon("pdb"));
mSymbolView->hide();
@ -207,7 +207,7 @@ MainWindow::MainWindow(QWidget* parent)
// Reference manager
mReferenceManager = new ReferenceManager(this);
Bridge::getBridge()->referenceManager = mReferenceManager;
Bridge::getBridge()->mReferenceManager = mReferenceManager;
mReferenceManager->setWindowTitle(tr("References"));
mReferenceManager->setWindowIcon(DIcon("search"));

View File

@ -123,7 +123,7 @@ QAction* ThreadView::makeCommandAction(QAction* action, const QString & command)
}
/**
* @brief ThreadView::ExecCommand execute command slot for menus. Only used by command that reference thread id.
* @brief ThreadView::execCommandSlot execute command slot for menus. Only used by command that reference thread id.
*/
void ThreadView::execCommandSlot()
{

View File

@ -190,6 +190,7 @@ int main(int argc, char* argv[])
qRegisterMetaType<duint>("duint");
qRegisterMetaType<byte_t>("byte_t");
qRegisterMetaType<DBGSTATE>("DBGSTATE");
qRegisterMetaType<GUIMSG>("GUIMSG");
// Set QString codec to UTF-8
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
@ -207,7 +208,7 @@ int main(int argc, char* argv[])
mainWindow->show();
// Set some data
Bridge::getBridge()->winId = (void*)mainWindow->winId();
Bridge::getBridge()->mWinId = (void*)mainWindow->winId();
// Init debugger
const char* errormsg = DbgInit();