Performance Monitoring Example

A resource and performance monitor.

Screenshot

Introduction

This example shows the basic usage of system and process monitoring APIs offered by the application-manager:

The example monitors performance (frame rate), as well as rescource usage (memory and CPU).

Invocation

The example can be started from within the "monitor" folder with:

path/to/bin/appman -c am-config.yaml -r

Note: The application-manager attempts to register a freedesktop.org compliant notification server. DBus errors might occur if it conflicts with the server running on the host desktop environment. In this case, a private session bus needs to be started by adding the --start-session-dbus option.

Walkthrough

We will start from the bottom, because this is the essential part.

SystemMonitor and ProcessMonitor Usage

    ProcessMonitor {
        id: processMon
        applicationId: ""
        reportingInterval: 1000
        count: 12

        memoryReportingEnabled: true
        frameRateReportingEnabled: true
        monitoredWindows: [root]

        onMemoryReportingChanged: processPss.reading = (memoryPss.total / 1e6).toFixed(0) + " MB";
        onFrameRateReportingChanged: {
            if (frameRate[0])
                frameChart.reading = frameRate[0].average.toFixed(0) + " fps";
        }
    }

    Connections {
        target: SystemMonitor
        onCpuLoadReportingChanged: systemLoad.reading = (load * 100).toFixed(1) + " %";
        onMemoryReportingChanged: systemMem.text = "Used Memory: " + (used / 1e9).toFixed(1) + " GB"
    }

    Component.onCompleted: {
        SystemMonitor.reportingInterval = 1000;
        SystemMonitor.count = 12;

        SystemMonitor.idleLoadThreshold = 0.05;
        SystemMonitor.cpuLoadReportingEnabled = true;
        SystemMonitor.gpuLoadReportingEnabled = true;
        SystemMonitor.memoryReportingEnabled = true;

        ApplicationManager.startApplication(ApplicationManager.application(0).id);
    }

The ProcessMonitor is an instantiatable type that will monitor the process that is associated with its applicationId property. An empty applicationId string means that the System-UI process is monitored. By clicking the "Switch Process" button, the monitored process can be changed to the tld.monitor.app application. Measurements will be performed each second (1000 ms) and 12 reading points will be kept in the model (the ProcessMonitor is a model). The model is used below to display the bar charts.

Memory and frame rate reporting need to be enabled, since we are interested in both. For frame rate measurements the list of monitoredWindows has to be filled with windows that we are interested in. For the System-UI this is the single root window itself and for the tld.monitor.app process it is the currently selected window. Hence the list will always include just one element. The memory and frame rate changed signal handlers will update the current values of the corresponding views.

The Connections type is used to implement signal handlers for the property changes of the SystemMonitor. The current values of the corresponding views are updated, as well.

The SystemMonitor is a singleton type. It is set-up in the Component.onCompleted handler. Like above, measurements will be performed each second (1000 ms) and 12 reading points will be kept in the model (the SystemMonitor is a model, as well).

The very last line will start the only application that is provided by this example. Note that, if the application-manager is running in single-process mode, the application will run within the System-UI process and consequently the ProcessMonitor will not report anything and the associated ProcessMonitor::processId will be set to 0.

The User Interface

import QtQuick 2.6
import QtQuick.Window 2.0
import QtApplicationManager 1.0

Window {
    id: root

    property var primaryWindow
    property var secondaryWindow

    width: 720
    height: 600
    color: "black"

    Column {
        x: 10
        padding: 20

        Tile {
            Column {
                id: systemOverview
                spacing: 10

                MonitorText { text: "System"; font.pixelSize: 26 }
                MonitorText { text: "CPU Cores: " + SystemMonitor.cpuCores }
                MonitorText { text: "Total Memory: " + (SystemMonitor.totalMemory / 1e9).toFixed(1) + " GB" }
                MonitorText { id: systemMem; text: "Used Memory:"  }
                MonitorText { text: "Idle Threshold: " + SystemMonitor.idleLoadThreshold * 100 + " %" }
                MonitorText { text: "Idle: " + SystemMonitor.idle }
                MonitorText { text: "GPU Load: " + SystemMonitor.gpuLoad * 100 + " %" }
            }
        }

        Tile {
            MonitorChart {
                id: systemLoad
                title: "CPU Load"
                model: SystemMonitor
                delegate: Rectangle {
                    width: 11
                    height: parent.height
                    gradient: Gradient {
                        GradientStop { position: 0.5; color: "#FF0000" }
                        GradientStop { position: 1.0; color: "#00FF00" }
                    }
                    Rectangle {
                        width: parent.width
                        height: parent.height - cpuLoad * parent.height
                        color: "black"
                    }
                }
            }
        }
    }

    Rectangle {
        x: 246; y: 25
        width: 1; height: root.height - 50
        color: "white"
    }

    Grid {
        columns: 2
        padding: 20
        x: 260

        Tile {
            Column {
                spacing: 20

                MonitorText {
                    text: processMon.applicationId === "" ? "System-UI" : processMon.applicationId
                    font.pixelSize: 26
                }
                MonitorText { text: "Process ID: " + processMon.processId }

                Switch {
                    onToggle: {
                        processMon.applicationId = processMon.applicationId === ""
                                                         ? ApplicationManager.application(0).id : ""
                        if (processMon.applicationId !== "") {
                            if (ApplicationManager.singleProcess)
                                console.warn("There is no dedicated application process in single-process mode");

                            if (primaryWindow && secondaryWindow) {
                                processMon.monitoredWindows = primary.active ? [primaryWindow] : [secondaryWindow]
                            } else {
                                processMon.monitoredWindows = []
                                console.warn("No application window available. Please check your QtWayland configuration.");
                            }
                        } else {
                            processMon.monitoredWindows = [root]
                        }
                    }
                }
            }
        }

        Tile {
            Column {
                visible: processMon.applicationId !== ""
                spacing: 20
                Item {
                    width: 200; height: 65
                    MonitorText { anchors.bottom: parent.bottom; text: "Application Windows:" }
                }

                WindowContainer {
                    id: primary
                    active: true
                    onActivated: {
                        processMon.monitoredWindows = [primaryWindow];
                        secondary.active = false;
                    }
                }

                WindowContainer {
                    id: secondary
                    onActivated: {
                        processMon.monitoredWindows = [secondaryWindow];
                        primary.active = false;
                    }
                }
            }
        }

        Tile {
            MonitorChart {
                id: processPss
                title: "Memory PSS"
                model: processMon
                delegate: Rectangle {
                    width: 11
                    height: memoryPss.total / 5e6
                    anchors.bottom: parent.bottom
                    color: "lightsteelblue"
                }
            }
        }

        Tile {
            MonitorChart {
                id: frameChart
                title: "Frame rate"
                model: processMon
                delegate: Rectangle {
                    width: 11
                    height: frameRate[0] ? frameRate[0].average * 3 : 0
                    anchors.bottom: parent.bottom
                    color.r: frameRate[0] ? (frameRate[0].average >= 60 ? 0 : 1 - frameRate[0].average / 60) : 0
                    color.g: frameRate[0] ? (frameRate[0].average >= 60 ? 1 : frameRate[0].average / 60) : 0
                    color.b: 0

                }
            }
        }
    }

    Connections {
        target: WindowManager
        onWindowReady:  {
            if (WindowManager.windowProperty(window, "windowType") === "primary") {
                primaryWindow = window
                window.parent = primary.container
                window.anchors.fill = primary.container
            } else {
                secondaryWindow = window
                window.parent = secondary.container
                window.anchors.fill = secondary.container
            }
        }

        onWindowLost: {
            processMon.monitoredWindows = []
            WindowManager.releaseWindow(window);
        }
    }
    ...

We won't go into much detail, because it's rather conventional QML code. The MonitorChart is a ListView that uses either an instance of a ProcessMonitor or the SystemMonitor singleton as its model. The reading values are represented as bars. Their height is determined by the corresponding model role, e.g. for the PSS memory consumption it is memoryPss.total.

When the tld.monitor.app process is monitored, its two windows will be shown (primary and secondary). The window that is monitored in terms of frame rate can be selected and is highlighted with a white border.

Files:

© 2018 Pelagicore AG. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.