[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
This commit is contained in:
Lukas Brübach 2026-08-23 00:19:18 +02:00
parent 45fe8b57a5
commit fa823e4b1c
2 changed files with 48 additions and 8 deletions

View file

@ -1,9 +1,13 @@
#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);
@ -21,23 +25,39 @@ 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()
{
const qint64 gap = tickClock.restart();
recordGap(tickClock.restart());
}
if (gap <= STALL_THRESHOLD_MS) {
void LagMonitor::recordGap(qint64 gapMs)
{
if (gapMs <= STALL_THRESHOLD_MS) {
return;
}
StallRecord record;
record.timestampMsSinceEpoch = QDateTime::currentMSecsSinceEpoch(); // wall time, for log correlation
record.durationMs = gap;
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>(gap),
qCWarning(LagMonitorLog, "Event loop stalled for %lld ms (threshold: %d ms)", static_cast<long long>(gapMs),
STALL_THRESHOLD_MS);
}