LoopGapMeter

Measures the largest gap between iterations of a thread's event loop, with and without the time the thread waited for a CPU.

In the namespace test. Declared in src/libs/libtest/test/LoopGapMeter.hpp.

Detailed description

A GUI freeze test pumps the event loop and asserts that no iteration took long (§GSI-T7). The wall time of an iteration also holds the time the thread was ready to run while the machine's other work had every CPU. On y8 at a load of 70, testPaintWorkerCanvas and testSceneWorkerWidget saw gaps of 355 and 423 ms that the same build did not show alone, so a busy machine read as a freeze of the code.

The meter subtracts that wait where the system reports it (platform::system::threadRunQueueWaitNs(), Linux). What is left is the stall's own part: the work the thread did, and every lock, pipe and driver it waited on. So a freeze of the code still counts in full, and the load of the machine does not. Where the system does not report the wait, the own gap equals the wall gap.

A thread that the program's own workers keep from a CPU is not a freeze this meter sees. Hold that with a bound on the workers, not on the gap.

Make the meter on the thread whose loop it measures, and call tick() on that thread after each iteration: the wait it reads is the calling thread's.

test::LoopGapMeter gaps;
while (gaps.elapsedMs() < 1500) {
        QCoreApplication::processEvents(QEventLoop::AllEvents, 20);
        gaps.tick();
}
QVERIFY2(gaps.maxOwnGapMs() < 250, qPrintable(gaps.describe()));

sampleStacksAfter() adds a watchdog thread that captures the measured thread's stack while a gap runs long, so a failure names what the thread did, not only how long it took.

In this section