diff options
Diffstat (limited to 'desktop/modules/perf/PerfPanel.qml')
| -rw-r--r-- | desktop/modules/perf/PerfPanel.qml | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/desktop/modules/perf/PerfPanel.qml b/desktop/modules/perf/PerfPanel.qml index aa2e49f..ff9f74b 100644 --- a/desktop/modules/perf/PerfPanel.qml +++ b/desktop/modules/perf/PerfPanel.qml @@ -131,6 +131,39 @@ Column { // --- CPU ---------------------------------------------------------------- + // The five heaviest processes, from one `top -b` streaming a frame every + // 2s for as long as the page is open; it dies with the panel. top marks + // no frame's end but writes each in one burst, so a frame is published + // once its output goes quiet. The first is skipped, since top computes + // it over each process's whole life rather than the last interval, so + // rows load about 2s after the page opens. + property var procs: [] + property string procBy: "cpu" + property string frame: "" + property int frames: 0 + + Process { + running: true + command: ["env", "LC_ALL=C", "top", "-b", "-d2", "-w512"] + stdout: SplitParser { + onRead: line => { + // Every header after the first reaches here with the blank + // line before it still attached, as "\ntop - ...". + if (/^\s*top - /.test(line)) { + panel.frames++; + panel.frame = ""; + } + panel.frame += line + "\n"; + frameEnd.restart(); + } + } + } + Timer { + id: frameEnd + interval: 150 + onTriggered: if (panel.frames >= 2) panel.procs = P.procs(panel.frame) + } + Card { title: "CPU" headline: Perf.load === null ? "--" : Math.round(Perf.load) + "%" @@ -174,6 +207,66 @@ Column { frac: Perf.mem ? Perf.mem.used / Perf.mem.total : 0 fill: panel.tone(panel.pct(Perf.mem ? Perf.mem.used : null, Perf.mem ? Perf.mem.total : 0), 25, 75) } + + // Sorted by CPU or RAM, the active one lit; switching re-sorts the + // frame already held rather than waiting for the next. + Item { + width: parent.width + height: byRow.implicitHeight + Label { text: "Processes"; color: Theme.subtext } + Row { + id: byRow + anchors.right: parent.right + spacing: 10 + Repeater { + model: [{ key: "cpu", text: "CPU" }, { key: "mem", text: "RAM" }] + delegate: Label { + required property var modelData + text: modelData.text + font.bold: panel.procBy === modelData.key + color: panel.procBy === modelData.key ? Theme.text : Theme.overlay + MouseArea { + anchors.fill: parent + anchors.margins: -4 + cursorShape: Qt.PointingHandCursor + onClicked: panel.procBy = modelData.key + } + } + } + } + } + Repeater { + model: panel.procs.slice().sort((a, b) => b[panel.procBy] - a[panel.procBy]).slice(0, 5) + // Two right-aligned columns of fixed width, so the numbers line + // up down the table whatever their length. + delegate: Item { + required property var modelData + width: parent.width + implicitHeight: nameText.implicitHeight + Label { + id: nameText + anchors { left: parent.left; right: cpuText.left; rightMargin: 8 } + text: modelData.name + elide: Text.ElideRight + } + Label { + id: cpuText + anchors.right: memText.left + width: panel.fs * 4 + horizontalAlignment: Text.AlignRight + text: modelData.cpu.toFixed(1) + "%" + font.features: { "tnum": 1 } + } + Label { + id: memText + anchors.right: parent.right + width: panel.fs * 4.5 + horizontalAlignment: Text.AlignRight + text: P.bytes(modelData.mem) + font.features: { "tnum": 1 } + } + } + } } // --- temperatures ------------------------------------------------------- |
