mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-21 09:05:10 -07:00
* [Client] Detect main-thread event loop stalls LagMonitor ticks the GUI event loop every 500 ms and records gaps beyond 2 s as stalls, warning with their duration and keeping a bounded ring of recent records for diagnostics. Measurement uses a monotonic QElapsedTimer so wall-clock steps and suspend do not fabricate stalls. Recorded timestamps stay in wall time for correlating with user reports. Took 1 minute Took 13 minutes Took 2 minutes * [Client] Rename LagMonitor constants to SCREAMING_SNAKE_CASE Took 15 minutes * [Client] Discard suspend-spanning gaps in LagMonitor Windows counts sleep time in its monotonic clock, so a suspend would fabricate one bogus stall per resume. Reset the clock on application state changes and drop implausibly huge gaps; extract recordGap() for testability. Took 3 minutes * [Client] Unit test LagMonitor stall recording Drives recordGap() directly to cover the threshold, plausibility cap, trim, and clear behavior without timing-dependent waits. Took 36 seconds --------- Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
63 lines
1.6 KiB
C++
63 lines
1.6 KiB
C++
#include "lag_monitor.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QEvent>
|
|
#include <QTimer>
|
|
|
|
LagMonitor::LagMonitor(QObject *parent) : QObject(parent)
|
|
{
|
|
qApp->installEventFilter(this);
|
|
|
|
timer = new QTimer(this);
|
|
timer->setInterval(TICK_INTERVAL_MS);
|
|
connect(timer, &QTimer::timeout, this, &LagMonitor::checkTick);
|
|
tickClock.start();
|
|
timer->start();
|
|
}
|
|
|
|
QList<LagMonitor::StallRecord> LagMonitor::recentStalls() const
|
|
{
|
|
return stalls;
|
|
}
|
|
|
|
void LagMonitor::clearStalls()
|
|
{
|
|
stalls.clear();
|
|
}
|
|
|
|
bool LagMonitor::eventFilter(QObject *obj, QEvent *event)
|
|
{
|
|
if (event->type() == QEvent::ApplicationStateChange) {
|
|
// The transition may span a suspend or an arbitrary unfocused period;
|
|
// discard the gap so it cannot be mistaken for a stall.
|
|
tickClock.restart();
|
|
}
|
|
return QObject::eventFilter(obj, event);
|
|
}
|
|
|
|
void LagMonitor::checkTick()
|
|
{
|
|
recordGap(tickClock.restart());
|
|
}
|
|
|
|
void LagMonitor::recordGap(qint64 gapMs)
|
|
{
|
|
if (gapMs <= STALL_THRESHOLD_MS) {
|
|
return;
|
|
}
|
|
|
|
if (gapMs > MAX_PLAUSIBLE_STALL_MS) {
|
|
qCDebug(LagMonitorLog, "Ignoring implausible %lld ms gap (likely suspend)", static_cast<long long>(gapMs));
|
|
return;
|
|
}
|
|
|
|
const StallRecord record{.timestampMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(), .durationMs = gapMs};
|
|
|
|
stalls.append(record);
|
|
while (stalls.size() > MAX_RECORDED_STALLS) {
|
|
stalls.removeFirst();
|
|
}
|
|
|
|
qCWarning(LagMonitorLog, "Event loop stalled for %lld ms (threshold: %d ms)", static_cast<long long>(gapMs),
|
|
STALL_THRESHOLD_MS);
|
|
}
|