diff options
author | Daniel d'Andrada <daniel.dandrada@luxoft.com> | 2018-12-03 11:25:34 +0100 |
---|---|---|
committer | Robert Griebl <robert.griebl@pelagicore.com> | 2018-12-07 15:04:24 +0000 |
commit | 05a3bb55ce78d785a77bc8c8aaab2fd82b0bc0b5 (patch) | |
tree | ea33f8f0b2288af890d666ad74509b8fba33b181 | |
parent | adb57812ee1cf89f3311f8a27ef97abf5d424bde (diff) | |
download | qtapplicationmanager-05a3bb55ce78d785a77bc8c8aaab2fd82b0bc0b5.tar.gz |
Introduce MonitorModel & friends
A better, more flexible, API for monitor-lib.
Fixes: AUTOSUITE-692
Fixes: AUTOSUITE-250
Fixes: AUTOSUITE-686
Change-Id: Ib672e6a19beca4e83a51bcdca530c50be1bf00b7
Reviewed-by: Robert Griebl <robert.griebl@pelagicore.com>
79 files changed, 4215 insertions, 3872 deletions
diff --git a/doc/migration-guide-5.12.qdoc b/doc/migration-guide-5.12.qdoc index ac42e770..499167df 100644 --- a/doc/migration-guide-5.12.qdoc +++ b/doc/migration-guide-5.12.qdoc @@ -244,40 +244,77 @@ QtObject { \section1 SystemMonitor -SystemMonitor is no longer a singleton. That way it's simpler to bind expressions to its properties and -you have control over the lifecycle of that object, so that you only create an instance of it when you -actually need it. It's also simpler to test code that uses a regular QML object instead of a singleton, -as you can apply patterns such as dependency injection. +The SystemMonitor singleton no longer exists. The functionality that it provided has been split into several new +components, namely: MonitorModel, CpuStatus, GpuStatus, MemoryStatus, FrameTimer and IoStatus. \oldcode Item { id: root ... - Label { - text: "CPU Cores: " + SystemMonitor.cpuCores - } - ... Component.onCompleted: { - SystemMonitor.reportingInterval = 1000; - SystemMonitor.count = 12; - SystemMonitor.idleLoadThreshold = 0.05; + SystemMonitor.reportingInterval = 1500; + SystemMonitor.count = 20; } Binding { target: SystemMonitor; property: "cpuLoadReportingEnabled"; value: root.visible } + + // Draw a graph of CPU usage + ListView { + model: SystemMonitor + ... + } } \newcode Item { id: root ... - Label { - text: "CPU Cores: " + systemMonitor.cpuCores + // Draw a graph of CPU usage + ListView { + model: MonitorModel { + interval: 1500 + running: root.visible + maximumCount: 20 + CpuStatus {} + } + ... } +} +\endcode + +\section1 ProcessMonitor + +The ProcessMonitor component no longer exists. The functionality that it provided has been split into a +couple of new components, namely: ProcessStatus, MonitorModel and FrameTimer. + +\oldcode +Item { + id: root + ... + // Draw a graph of CPU usage + ListView { + model: ProcessMonitor { + count: 20 + cpuReportingEnabled: root.visible + reportingInterval: 1500 + applicationId: "foo.app" + } + ... + } +} +\newcode +Item { + id: root ... - SystemMonitor { - id: systemMonitor - reportingInterval: 1000 - count: 12 - idleLoadThreshold: 0.05 - cpuLoadReportingEnabled: root.visible + // Draw a graph of CPU usage + ListView { + model: MonitorModel { + maximumCount: 20 + running: root.visible + interval: 1500 + ProcessStatus { + applicationId: "foo.app" + } + } + ... } } \endcode diff --git a/examples/applicationmanager/applicationmanager.pro b/examples/applicationmanager/applicationmanager.pro index 6b7b8034..8231c457 100644 --- a/examples/applicationmanager/applicationmanager.pro +++ b/examples/applicationmanager/applicationmanager.pro @@ -2,10 +2,11 @@ TEMPLATE = subdirs SUBDIRS = \ animated-windows \ + frame-timer \ hello-world \ minidesk \ - monitor \ multi-views \ + process-status \ startup-plugin \ intents \ diff --git a/examples/applicationmanager/monitor/am-config.yaml b/examples/applicationmanager/frame-timer/am-config.yaml index dc5216c5..5db21e36 100644 --- a/examples/applicationmanager/monitor/am-config.yaml +++ b/examples/applicationmanager/frame-timer/am-config.yaml @@ -1,13 +1,8 @@ formatVersion: 1 formatType: am-configuration --- - applications: builtinAppsManifestDir: "${CONFIG_PWD}/apps" ui: mainQml: "${CONFIG_PWD}/system-ui/main.qml" - -# development setup: -flags: - noUiWatchdog: yes diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/app.qml b/examples/applicationmanager/frame-timer/apps/frame-timer.fish/fish.qml index a199d467..d6ae4bc3 100644 --- a/examples/applicationmanager/monitor/apps/tld.monitor.app/app.qml +++ b/examples/applicationmanager/frame-timer/apps/frame-timer.fish/fish.qml @@ -50,34 +50,36 @@ ** ****************************************************************************/ -import QtQuick 2.6 +import QtQuick 2.4 import QtApplicationManager.Application 1.0 -ApplicationWindow { - title: "Primary Window" - windowType: "primary" +ApplicationManagerWindow { + id: root + color: mouseArea.pressed ? "red" : "darkblue" - ApplicationWindow { - title: "Secondary Window" - windowType: "secondary" - visible: true - idle: 500 - } + Rectangle { + id: rectangle + anchors.centerIn: parent + width: 180; height: 180; radius: width/4 + color: "aqua" + + Image { + source: ApplicationInterface.icon + anchors.centerIn: parent + } - Timer { - property int count: 0 - running: true - repeat: true - onTriggered: { - if (count < 100) { - var arr = []; - var idx = 200000; - while (idx > 0) - arr[idx] = idx--; - count++; - } else { - repeat = false; + Timer { + running: true + repeat: true + interval: 1000 / 25 // 25 frames per second + onTriggered: { + rectangle.rotation = (rectangle.rotation + 5) % 360; } } } + + MouseArea { + id: mouseArea + anchors.fill: parent + } } diff --git a/examples/applicationmanager/frame-timer/apps/frame-timer.fish/fish.svg b/examples/applicationmanager/frame-timer/apps/frame-timer.fish/fish.svg new file mode 100644 index 00000000..69a95ea7 --- /dev/null +++ b/examples/applicationmanager/frame-timer/apps/frame-timer.fish/fish.svg @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + id="svg8" + version="1.1" + viewBox="0 0 51.879999 51.880002" + height="51.880001mm" + width="51.880001mm"> + <title + id="title4518">U+144A5 ANATOLIAN HIEROGLYPH A138</title> + <defs + id="defs2" /> + <metadata + id="metadata5"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>U+144A5 ANATOLIAN HIEROGLYPH A138 from Noto Sans font</dc:title> + <cc:license + rdf:resource="http://scripts.sil.org/OFL" /> + </cc:Work> + <cc:License + rdf:about="http://scripts.sil.org/OFL"> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/Reproduction" /> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/Distribution" /> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/Embedding" /> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/DerivativeWorks" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/Notice" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/Attribution" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/ShareAlike" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/DerivativeRenaming" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/BundlingWhenSelling" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + transform="translate(-47.560265,-26.871213)" + id="layer1"> + <g + id="text4538" + style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;image-rendering:auto"> + <path + id="path814" + style="font-size:42.33333206px;stroke-width:0.26458332" + d="m 71.320099,66.590713 q -2.032,0 -3.852334,-0.804334 -1.777999,-0.762 -3.217333,-2.032 -1.439333,-1.227666 -2.413,-2.667 -3.090333,-0.973666 -6.095999,-3.090333 -3.005667,-2.116666 -6.180667,-4.699 V 52.32438 q 3.175,-2.582334 6.180667,-4.699 3.005666,-2.116667 6.095999,-3.090333 0.973667,-1.439334 2.413,-2.667 1.439334,-1.27 3.217333,-2.032 1.820334,-0.804334 3.852334,-0.804334 0.677333,0 1.058333,0.169334 0.423333,0.127 0.423333,0.592666 0,0.592667 -0.465666,1.058334 -0.465667,0.423333 -1.143,0.846666 -0.677334,0.423334 -1.312334,0.973667 -0.635,0.508 -0.973666,1.312333 l 1.820333,0.254 q 1.439333,-1.27 3.259667,-2.074333 1.862666,-0.846667 3.937,-0.846667 0.677333,0 1.058333,0.169334 0.423333,0.127 0.423333,0.592666 0,0.677334 -0.635,1.227667 -0.592666,0.550333 -1.397,1.100667 -0.804333,0.508 -1.439333,1.227666 0.465667,0.127 0.889,0.296334 0.423333,0.127 0.846667,0.254 1.269999,-0.846667 2.751666,-1.439334 1.524,-0.592666 3.090333,-0.592666 0.677334,0 1.058334,0.169333 0.423333,0.169333 0.423333,0.592667 0,0.889 -0.931333,1.439333 -0.931334,0.550333 -1.862667,1.312333 1.143,0.296333 2.201333,0.508 1.100667,0.211667 2.116667,0.211667 1.354667,0 2.582333,-0.635 1.227667,-0.677334 2.243667,-1.481667 1.058333,-0.846666 1.778,-1.397 0.296333,-0.211666 0.846666,-0.719666 0.550334,-0.550334 1.143,-1.016 0.592667,-0.465667 0.973667,-0.465667 0.635,0 0.973667,0.719667 0.381,0.677333 0.381,1.227666 0,0.973667 -0.550334,2.074333 -0.550333,1.100667 -1.312333,2.201334 -0.719667,1.058333 -1.27,2.032 -0.550333,0.931333 -0.550333,1.608666 v 0.550334 q 0,0.677333 0.550333,1.651 0.550333,0.931333 1.27,2.032 0.762,1.058333 1.312333,2.158999 0.550334,1.100667 0.550334,2.074334 0,0.592666 -0.381,1.27 -0.338667,0.677333 -0.973667,0.677333 -0.381,0 -0.973667,-0.465667 -0.592666,-0.465666 -1.143,-0.973666 -0.550333,-0.550334 -0.846666,-0.762 -0.719667,-0.550334 -1.778,-1.354667 -1.016,-0.846667 -2.243667,-1.481667 -1.227666,-0.677333 -2.582333,-0.677333 -1.016,0 -2.116667,0.211667 -1.058333,0.169333 -2.201333,0.508 0.931333,0.762 1.862667,1.312333 0.931333,0.550333 0.931333,1.439333 0,0.465667 -0.423333,0.635 -0.381,0.127 -1.058334,0.127 -1.566333,0 -3.090333,-0.592666 -1.481667,-0.635 -2.751666,-1.439334 -0.423334,0.127 -0.846667,0.296334 -0.423333,0.127 -0.889,0.254 0.635,0.677333 1.439333,1.227666 0.804334,0.550334 1.397,1.100667 0.635,0.550333 0.635,1.227667 0,0.423333 -0.423333,0.592666 -0.381,0.169334 -1.058333,0.169334 -2.074334,0 -3.937,-0.846667 -1.820334,-0.804333 -3.259667,-2.074333 l -1.820333,0.254 q 0.338666,0.804333 0.973666,1.312333 0.635,0.550333 1.312334,0.973667 0.677333,0.423333 1.143,0.846666 0.465666,0.465667 0.465666,1.058334 0,0.423333 -0.423333,0.592666 -0.381,0.169334 -1.058333,0.169334 z m -7.493,-22.563666 q 0.550333,-0.08467 1.143,-0.127 0.592667,-0.08467 1.185333,-0.08467 0.423334,0 0.804334,0 0.380999,0 0.804333,0.04233 0.508,-1.439333 1.524,-2.159 1.016,-0.762 1.947333,-1.566333 -0.127,-0.04233 -0.381,-0.04233 -1.735667,0 -3.640667,1.100666 -1.904999,1.100667 -3.386666,2.836334 z m 11.006666,1.227666 q 0.592667,-0.846666 1.439334,-1.524 0.846666,-0.719666 1.566333,-1.312333 -0.127,-0.04233 -0.381,-0.04233 -1.227667,0 -2.624667,0.635 -1.397,0.592666 -2.624666,1.566333 0.677333,0.169333 1.312333,0.338667 0.677333,0.169333 1.312333,0.338666 z m 21.209,16.298333 q 0.127,-0.254 0.127,-0.508 0,-0.804333 -0.592667,-1.820333 -0.550333,-1.058334 -1.354667,-2.159 -0.762,-1.143 -1.354666,-2.159 -0.550334,-1.058333 -0.550334,-1.820333 v -0.550334 q 0,-0.762 0.550334,-1.778 0.592666,-1.058333 1.354666,-2.159 0.804334,-1.143 1.354667,-2.159 0.592667,-1.058333 0.592667,-1.862666 0,-0.254 -0.127,-0.508 -1.100667,1.100667 -2.497667,2.370666 -1.397,1.227667 -3.090333,2.201334 -1.651,0.931333 -3.513667,1.016 v 6.307666 q 1.862667,0.127 3.513667,1.058334 1.693333,0.931333 3.090333,2.201333 1.397,1.27 2.497667,2.328333 z m -29.844999,-1.016 q 3.174999,0 5.799666,-0.635 2.667,-0.635 4.953,-1.481667 2.328333,-0.846666 4.445,-1.523999 2.159,-0.719667 4.275666,-0.889 v -6.392334 q -2.116666,-0.169333 -4.275666,-0.846666 -2.116667,-0.719667 -4.445,-1.566334 -2.286,-0.846666 -4.953,-1.481666 -2.624667,-0.635 -5.799666,-0.635 -2.878667,0 -5.418667,1.100667 -2.54,1.100666 -4.953,2.878666 -2.413,1.778 -4.868333,3.767667 2.413,1.989666 4.826,3.767666 2.413,1.735667 4.953,2.836333 2.582333,1.100667 5.461,1.100667 z M 80.972098,47.286713 q 0.592667,-0.592667 1.227667,-1.058333 0.635,-0.465667 1.227667,-0.973667 -0.127,-0.04233 -0.381,-0.04233 -0.973667,0 -2.032,0.381 -1.058334,0.381 -2.116667,1.016 0.550333,0.169333 1.058333,0.338666 0.508,0.169334 1.016,0.338667 z m -17.017999,9.440333 q 0.08467,-0.804333 0.635,-1.439333 0.550333,-0.635 0.550333,-1.354667 0,-0.550333 -0.423333,-0.973666 -0.423333,-0.423334 -0.846667,-0.931334 -0.381,-0.508 -0.381,-1.354666 0,-0.846667 0.423334,-1.481667 0.465666,-0.677333 0.550333,-1.439333 l 1.227667,0.127 q -0.08467,0.592666 -0.508,1.312333 -0.423334,0.719667 -0.423334,1.354667 0,0.635 0.381,1.143 0.423334,0.465666 0.846667,1.016 0.423333,0.508 0.423333,1.227666 0,0.550334 -0.296333,0.973667 -0.296333,0.423333 -0.592667,0.846667 -0.296333,0.423333 -0.296333,0.889 z m 8.466666,0 q 0.08467,-0.804333 0.635,-1.439333 0.550334,-0.635 0.550334,-1.354667 0,-0.550333 -0.423334,-0.973666 -0.423333,-0.423334 -0.846666,-0.931334 -0.381,-0.508 -0.381,-1.354666 0,-0.846667 0.423333,-1.481667 0.465667,-0.677333 0.550333,-1.439333 l 1.227667,0.127 q -0.08467,0.592666 -0.508,1.312333 -0.423333,0.719667 -0.423333,1.354667 0,0.635 0.381,1.143 0.423333,0.465666 0.846666,1.016 0.423334,0.508 0.423334,1.227666 0,0.550334 -0.296334,0.973667 -0.296333,0.423333 -0.592666,0.846667 -0.296334,0.423333 -0.296334,0.889 z m -4.233333,0 q 0.08467,-0.804333 0.635,-1.439333 0.550333,-0.635 0.550333,-1.354667 0,-0.550333 -0.423333,-0.973666 -0.423333,-0.423334 -0.846667,-0.931334 -0.381,-0.508 -0.381,-1.354666 0,-0.846667 0.423334,-1.481667 0.465666,-0.677333 0.550333,-1.439333 l 1.227667,0.127 q -0.08467,0.592666 -0.508,1.312333 -0.423334,0.719667 -0.423334,1.354667 0,0.635 0.381,1.143 0.423334,0.465666 0.846667,1.016 0.423333,0.508 0.423333,1.227666 0,0.550334 -0.296333,0.973667 -0.296333,0.423333 -0.592667,0.846667 -0.296333,0.423333 -0.296333,0.889 z m -10.879666,-3.429 q -0.804334,0 -1.354667,-0.508 -0.508,-0.550333 -0.508,-1.354666 0,-0.762 0.508,-1.312334 0.550333,-0.592666 1.354667,-0.592666 0.762,0 1.312333,0.592666 0.592667,0.550334 0.592667,1.312334 0,0.804333 -0.592667,1.354666 -0.550333,0.508 -1.312333,0.508 z m 25.738666,7.112 q 0.254,0 0.381,-0.04233 -0.592667,-0.508 -1.227667,-0.973667 -0.635,-0.465667 -1.227667,-1.058333 -0.508,0.169333 -1.016,0.338666 -0.508,0.127 -1.058333,0.338667 1.058333,0.635 2.116667,1.016 1.058333,0.381 2.032,0.381 z m -5.588,2.836333 q 0.254,0 0.381,-0.04233 -0.719667,-0.592667 -1.566333,-1.312333 -0.846667,-0.719667 -1.439334,-1.524 -0.635,0.211666 -1.312333,0.381 -0.635,0.127 -1.312333,0.296333 1.227666,0.973667 2.624666,1.608667 1.397,0.592666 2.624667,0.592666 z m -6.604,2.286 q 0.254,0 0.381,-0.04233 -0.931333,-0.804333 -1.947333,-1.566333 -1.016,-0.719667 -1.524,-2.159 -0.423334,0.04233 -0.804333,0.04233 -0.381,0 -0.804334,0 -0.592666,0 -1.185333,-0.04233 -0.592667,-0.08467 -1.143,-0.169334 1.481667,1.693334 3.386666,2.794 1.905,1.143 3.640667,1.143 z" /> + </g> + </g> +</svg> diff --git a/examples/applicationmanager/frame-timer/apps/frame-timer.fish/info.yaml b/examples/applicationmanager/frame-timer/apps/frame-timer.fish/info.yaml new file mode 100644 index 00000000..2178b68e --- /dev/null +++ b/examples/applicationmanager/frame-timer/apps/frame-timer.fish/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'frame-timer.fish' +icon: 'fish.svg' +code: 'fish.qml' +runtime: 'qml' +name: + en: 'Fish' diff --git a/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/info.yaml b/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/info.yaml new file mode 100644 index 00000000..a50f99ad --- /dev/null +++ b/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'frame-timer.rabbit' +icon: 'rabbit.svg' +code: 'rabbit.qml' +runtime: 'qml' +name: + en: 'Rabbit' diff --git a/examples/applicationmanager/monitor/system-ui/Switch.qml b/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/rabbit.qml index 6ed638fb..4e38ce61 100644 --- a/examples/applicationmanager/monitor/system-ui/Switch.qml +++ b/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/rabbit.qml @@ -50,23 +50,30 @@ ** ****************************************************************************/ -import QtQuick 2.6 +import QtQuick 2.4 +import QtApplicationManager.Application 1.0 -Rectangle { - signal toggle() +ApplicationManagerWindow { + id: root + color: mouseArea.pressed ? "red" : "forestgreen" - width: 180; height: 30; radius: 4 - color: 'white' - - MonitorText { + Rectangle { anchors.centerIn: parent - text: 'Switch Process' - font.bold: true - color: 'black' + width: 180; height: 180; radius: width/4 + color: "palegreen" + + Image { + source: ApplicationInterface.icon + anchors.centerIn: parent + } + + RotationAnimation on rotation { + from: 0; to: 360; loops: Animation.Infinite; duration: 4000 + } } MouseArea { + id: mouseArea anchors.fill: parent - onClicked: toggle(); } } diff --git a/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/rabbit.svg b/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/rabbit.svg new file mode 100644 index 00000000..639841df --- /dev/null +++ b/examples/applicationmanager/frame-timer/apps/frame-timer.rabbit/rabbit.svg @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + width="51.880001mm" + height="51.880001mm" + viewBox="0 0 51.879999 51.879998" + version="1.1" + id="svg4565"> + <title + id="title815">U+1448C ANATOLIAN HIEROGLYPH A115A from Noto Sans font</title> + <defs + id="defs4559" /> + <metadata + id="metadata4562"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title>U+1448C ANATOLIAN HIEROGLYPH A115A from Noto Sans font</dc:title> + <cc:license + rdf:resource="http://scripts.sil.org/OFL" /> + </cc:Work> + <cc:License + rdf:about="http://scripts.sil.org/OFL"> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/Reproduction" /> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/Distribution" /> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/Embedding" /> + <cc:permits + rdf:resource="http://scripts.sil.org/pub/OFL/DerivativeWorks" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/Notice" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/Attribution" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/ShareAlike" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/DerivativeRenaming" /> + <cc:requires + rdf:resource="http://scripts.sil.org/pub/OFL/BundlingWhenSelling" /> + </cc:License> + </rdf:RDF> + </metadata> + <g + id="layer1" + transform="matrix(1.1970749,0,0,1.1970749,-89.370004,-96.553259)"> + <g + id="text5112" + style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"> + <path + id="path815" + style="font-size:42.33333206px;stroke-width:0.26458332" + d="m 109.57681,120.95381 q -1.43934,0 -1.905,-0.508 -0.46567,-0.508 -0.46567,-1.18533 0,-0.254 0.254,-0.67733 0.29633,-0.46567 0.55033,-0.93134 0.29634,-0.508 0.29634,-0.889 0,-0.59266 -0.93134,-0.59266 -0.635,0 -1.56633,0.16933 -0.889,0.127 -1.69333,0.127 -1.10067,0 -1.905,-0.97367 -0.762,-0.97366 -1.10067,-2.49766 -1.05833,0.42333 -3.217334,0.84666 -2.159,0.42334 -4.487334,0.42334 -0.635,0 -1.524,-0.127 -0.846666,-0.16934 -1.523999,-0.16934 -0.931334,0 -1.439334,0.635 -0.465666,0.59267 -0.804333,1.524 -0.296333,0.889 -0.677333,1.82034 -0.381,0.93133 -1.100667,1.56633 -0.677333,0.59267 -1.947333,0.59267 h -2.794 q -0.931334,0 -1.566334,-0.59267 -0.592666,-0.635 -0.592666,-1.18533 0,-0.93134 0.677333,-1.48167 0.677333,-0.59267 1.566333,-1.016 0.931334,-0.46567 1.608667,-0.97367 0.169333,-1.31233 0.719667,-3.048 0.592666,-1.778 2.370666,-3.25966 0.550334,-0.46567 1.016,-0.55034 0.465667,-0.127 0.465667,-0.93133 0,-0.16933 -0.127,-0.42333 -0.08467,-0.254 -0.296333,-0.254 -0.762,0 -1.481667,0.42333 -0.677333,0.381 -1.439333,0.80433 -0.719667,0.381 -1.524,0.381 -0.931334,0 -1.820334,-0.59266 -0.846666,-0.59267 -1.396999,-1.48167 -0.550334,-0.889 -0.550334,-1.778 0,-1.82033 1.143,-3.25967 1.143,-1.481662 2.667,-2.582329 -0.338667,-1.058333 -0.550333,-2.582333 -0.169334,-1.524 -0.296334,-3.048 -0.08467,-1.524 -0.08467,-2.582333 0,-1.481667 0.254,-2.963333 0.254,-1.481667 0.889,-2.455334 0.635,-0.973666 1.735666,-0.973666 0.931334,0 1.439334,0.846666 0.550333,0.846667 0.762,1.989667 0.211666,1.100667 0.211666,1.905 0,1.947333 -0.465666,3.81 -0.465667,1.862666 -0.465667,3.81 0,0.889 0.169333,1.27 0.169334,0.381 0.677334,0.381 1.016,0 1.651,-1.016 0.635,-1.058334 1.524,-2.751667 0.338666,-0.635 0.931333,-1.862667 0.635,-1.27 1.524,-2.624666 0.889,-1.354667 2.074333,-2.286 1.227667,-0.931333 2.751667,-0.931333 0.592666,0 1.143,0.762 0.550333,0.719666 0.550333,1.862666 0,1.27 -0.804333,2.667 -0.804334,1.397 -1.989667,2.794 -1.185333,1.397 -2.370667,2.709333 -1.185333,1.312334 -1.989666,2.413 -0.804334,1.100669 -0.804334,1.947329 0,0.55034 0.381,0.80434 0.423334,0.21166 0.846667,0.21166 4.318,0 7.366005,-0.0847 3.09033,-0.127 5.207,-0.46567 0.71967,-0.42333 1.60867,-0.71967 0.93133,-0.29633 2.11666,-0.29633 1.778,0 2.921,1.35467 1.18534,1.31233 1.18534,3.429 0,1.35466 -0.42334,1.905 -0.42333,0.508 -1.05833,0.635 -0.59267,0.0847 -1.18533,0.127 -0.59267,0.0423 -1.016,0.381 -0.42334,0.33866 -0.42334,1.397 0,0.889 0.635,1.397 0.635,0.508 1.22767,0.762 1.73567,0.80433 2.11667,1.35466 0.42333,0.508 0.42333,1.22767 0,1.05833 -0.42333,2.159 -0.42334,1.05833 -1.05834,1.98967 -0.635,0.889 -1.31233,1.43933 -0.635,0.55033 -1.05833,0.55033 z m 0.0847,-1.397 q 0.29634,0 0.84667,-0.71966 0.55033,-0.762 0.97367,-1.778 0.46566,-1.016 0.46566,-1.905 0,-0.71967 -0.381,-1.05834 -0.33866,-0.33866 -1.31233,-0.762 -1.05833,-0.508 -1.778,-1.43933 -0.71967,-0.93133 -0.71967,-2.07433 0,-1.18534 0.381,-1.69334 0.42334,-0.55033 1.016,-0.71966 0.635,-0.21167 1.22767,-0.29634 0.635,-0.0847 1.016,-0.33866 0.42333,-0.29634 0.42333,-1.016 0,-1.524 -0.84666,-2.58234 -0.80434,-1.05833 -2.11667,-1.05833 -1.10067,0 -1.73567,0.29633 -0.59266,0.254 -1.35466,0.67734 -1.48167,0.254 -3.64067,0.381 -2.11667,0.127 -4.572001,0.16933 -2.455333,0.0423 -4.953,0.0423 -1.143,0 -1.778,-0.508 -0.592666,-0.508 -0.592666,-1.82033 0,-1.05833 0.804333,-2.243665 0.804333,-1.227667 1.989666,-2.54 1.185334,-1.312334 2.328334,-2.624667 1.185333,-1.354667 1.989666,-2.624667 0.804334,-1.27 0.804334,-2.370666 0,-0.338667 -0.127,-0.889 -0.127,-0.550333 -0.762,-0.550333 -0.804334,0 -1.651,0.804333 -0.846667,0.804333 -1.693334,2.074333 -0.846666,1.227667 -1.608666,2.582333 -0.719667,1.312334 -1.312333,2.413 -0.592667,1.058334 -0.973667,1.862667 -0.381,0.762 -1.016,1.185333 -0.592667,0.423334 -1.820333,0.423334 -0.973667,0 -1.439334,-0.635 -0.423333,-0.677334 -0.423333,-1.524 0,-1.989667 0.381,-3.894667 0.423333,-1.947333 0.423333,-3.894667 0,-0.719666 -0.08467,-1.650999 -0.04233,-0.931334 -0.296334,-1.608667 -0.211666,-0.677333 -0.677333,-0.677333 -0.677333,0 -1.058333,0.889 -0.338667,0.846666 -0.508,2.032 -0.127,1.142999 -0.127,2.031999 0,1.143 0.04233,2.667 0.04233,1.524 0.169333,3.005667 0.127,1.481667 0.423334,2.497667 0.127,0.254 0.127,0.465666 0,0.169334 -0.127,0.296334 -0.08467,0.08467 -0.211667,0.211666 -0.677333,0.550333 -1.439333,1.227669 -0.762,0.67733 -1.312334,1.56633 -0.508,0.889 -0.508,2.20133 0,0.80434 0.677334,1.73567 0.677333,0.93133 1.735666,0.93133 0.804334,0 1.524,-0.381 0.719667,-0.42333 1.439334,-0.80433 0.762,-0.42333 1.651,-0.42333 0.719666,0 1.185333,0.508 0.465667,0.508 0.465667,1.22766 0,0.97367 -0.381,1.397 -0.338667,0.42334 -0.889,0.67734 -0.508,0.254 -1.058334,0.80433 -1.185333,1.18533 -1.651,2.49767 -0.423333,1.31233 -0.592666,2.667 -0.08467,0.635 -0.592667,1.016 -0.508,0.381 -1.185333,0.71966 -0.677334,0.29634 -1.27,0.67734 -0.550334,0.381 -0.804334,1.05833 0.211667,0.21167 0.423334,0.33867 0.254,0.127 1.058333,0.127 h 2.54 q 0.677333,0 1.058333,-0.59267 0.423334,-0.635 0.762,-1.56633 0.338667,-0.93134 0.762,-1.86267 0.423334,-0.93133 1.100667,-1.524 0.677333,-0.635 1.778,-0.635 0.592667,0 1.397,0.21167 0.804333,0.21166 1.481666,0.21166 2.413,0 4.572,-0.42333 2.159,-0.46567 3.217335,-0.889 -0.0423,-0.21167 -0.0423,-0.42333 0,-0.254 0,-0.46567 l 1.18534,0.254 q 0,1.86267 0.55033,2.96333 0.55033,1.10067 1.905,1.10067 0.33867,0 1.10067,-0.0847 0.762,-0.0847 1.48166,-0.127 0.71967,-0.0847 0.93134,-0.0847 0.762,0 1.05833,0.42333 0.33867,0.381 0.33867,0.84667 0,1.05833 -0.508,1.69333 -0.46567,0.635 -0.46567,0.84667 0,0.59266 0.29633,0.71966 0.33867,0.127 0.889,0.127 z m -24.722664,-17.272 q -0.465667,0 -0.846667,-0.33866 -0.338666,-0.381 -0.338666,-0.84667 0,-0.508 0.338666,-0.84667 0.381,-0.380996 0.846667,-0.380996 0.508,0 0.846667,0.380996 0.381,0.33867 0.381,0.84667 0,0.46567 -0.381,0.84667 -0.338667,0.33866 -0.846667,0.33866 z" /> + </g> + </g> +</svg> diff --git a/examples/applicationmanager/frame-timer/doc/images/frame-timer-example.png b/examples/applicationmanager/frame-timer/doc/images/frame-timer-example.png Binary files differnew file mode 100644 index 00000000..6654660f --- /dev/null +++ b/examples/applicationmanager/frame-timer/doc/images/frame-timer-example.png diff --git a/examples/applicationmanager/frame-timer/doc/src/frame-timer-example.qdoc b/examples/applicationmanager/frame-timer/doc/src/frame-timer-example.qdoc new file mode 100644 index 00000000..a6f90b72 --- /dev/null +++ b/examples/applicationmanager/frame-timer/doc/src/frame-timer-example.qdoc @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + +\example applicationmanager/frame-timer +\title Displaying Frame-Rate of System-UI and Applications Example +\image frame-timer-example.png The "FrameTimer" example with two applications running. +\brief How to use FrameTimer to display frame-rate information. +\ingroup applicationmanager-examples + +This example shows you how to use the \l FrameTimer component to display frame-rate information +of both System-UI and application windows. + +The System-UI is comprised of a column of application icons on the left side and a graph on the top +right side, displaying the average frame-rate of the System-UI itself (actually of System-UI's top-level +Window, to be more precise). If there are no applications running, System-UI's frame rate will mostly +stay at 1 frame per second. That's because a Qt QML application window is only redrawn when needed. If +nothing changed on it, it won't redraw (zero frames per second then). The only reason that System-UI +stays around 1 FPS when there are no apps running is because of the FPS graph itself, which is updated +once every second and thus causes a redraw of the System-UI (an +\l{https://en.wikipedia.org/wiki/Observer_effect_(information_technology)}{Observer effect}). + +The Fish application animates (and therefore redraws) at a rate of 25 frames per second. So running it will +instantly raise the frame-rate of System-UI to 25 FPS (frames per second) as well. + +The Rabbit application animates at native speed (ie, as fast as the system can or is configured to do, which +is usually 60 FPS). So running it will raise System-UI's FPS further, to 60 FPS. + +*/ diff --git a/examples/applicationmanager/monitor/monitor.pro b/examples/applicationmanager/frame-timer/frame-timer.pro index c77dbd4f..e35a8a9d 100644 --- a/examples/applicationmanager/monitor/monitor.pro +++ b/examples/applicationmanager/frame-timer/frame-timer.pro @@ -3,14 +3,15 @@ CONFIG += am-systemui OTHER_FILES += \ am-config.yaml \ - monitor.qmlproject \ - doc/src/*.qdoc \ - doc/images/*.png \ system-ui/*.qml \ - apps/tld.monitor.app/*.yaml \ - apps/tld.monitor.app/*.qml + apps/frame-timer.fish/*.yaml \ + apps/frame-timer.fish/*.qml \ + apps/frame-timer.fish/*.svg \ + apps/frame-timer.rabbit/*.yaml \ + apps/frame-timer.rabbit/*.qml \ + apps/frame-timer.rabbit/*.svg \ -target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/monitor +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/frame-timer INSTALLS += target AM_COPY_DIRECTORIES += apps system-ui @@ -19,8 +20,8 @@ AM_COPY_FILES += am-config.yaml prefix_build:tpath = $$target.path else:tpath = $$_PRO_FILE_PWD_ -AM_DEFAULT_ARGS = -c $$tpath/am-config.yaml --start-session-dbus --verbose +AM_DEFAULT_ARGS = -c $$tpath/am-config.yaml --start-session-dbus --verbose -r example_sources.path = $$target.path -example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES doc +example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES INSTALLS += example_sources diff --git a/examples/applicationmanager/frame-timer/system-ui/main.qml b/examples/applicationmanager/frame-timer/system-ui/main.qml new file mode 100644 index 00000000..2c13f84c --- /dev/null +++ b/examples/applicationmanager/frame-timer/system-ui/main.qml @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.11 +import QtQuick.Layouts 1.11 +import QtQuick.Window 2.11 +import QtApplicationManager 1.0 +import QtApplicationManager.SystemUI 1.0 + +Window { + id: sysuiWindow + + width: 1024 + height: 640 + + Rectangle { + anchors.fill: parent + color: "linen" + + + // A graph displaying the FPS of the System UI itself + Rectangle { + id: fpsGraph + anchors.top: parent.top + anchors.right: parent.right + width: 500 + height: 200 + color: "grey" + ListView { + id: listView + anchors.fill: parent + orientation: ListView.Horizontal + spacing: (fpsGraph.width / model.count) * 0.2 + clip: true + interactive: false + + model: MonitorModel { + id: monitorModel + running: true + // Just put a FrameTimer inside a MonitorModel and you can use it as a data source + // There's no need to set its running property as the MonitorModel will take care of asking + // it to update itself when appropriate. + FrameTimer { window: sysuiWindow } + } + + delegate: Rectangle { + width: (fpsGraph.width / monitorModel.count) * 0.8 + height: (model.averageFps / 100) * fpsGraph.height + y: fpsGraph.height - height + color: "blue" + } + } + + Text { + anchors.top: parent.top + text: "100" + font.pixelSize: 15 + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: "50" + font.pixelSize: 15 + } + + Text { + anchors.bottom: parent.bottom + text: "0" + font.pixelSize: 15 + } + } + + + // Application icons. + // Click on an icon to start its application and click on it again to stop it. + ColumnLayout { + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: 10 + spacing: 15 + Repeater { + model: ApplicationManager + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + Rectangle { + Layout.alignment: Qt.AlignHCenter + width: 100; height: 100; radius: width/4 + color: model.isRunning ? "darkgrey" : "lightgrey" + Image { + anchors.fill: parent + source: icon + sourceSize.width: 100 + sourceSize.height: 100 + } + MouseArea { + anchors.fill: parent + onClicked: { + if (model.isRunning) + application.stop(); + else + application.start(); + } + } + } + Text { + Layout.alignment: Qt.AlignHCenter + text: model.name + " application" + horizontalAlignment: Text.AlignHCenter + } + } + } + } + + // Application windows. + // Each WindowObject is put in a WindowItem decorated with a border and a title bar. + Repeater { + model: ListModel { id: windowsModel } + + delegate: Rectangle { + id: winChrome + + width: 400; height: 320 + z: model.index + color: "tan" + + // Title bar text + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: model.window.application.name("en") + " application window" + } + + // Raises the window when the title bar is clicked and moves it around when dragged. + MouseArea { + anchors.fill: parent + drag.target: parent + onPressed: windowsModel.move(model.index, windowsModel.count-1, 1); + } + + // Close button + Rectangle { + width: 25; height: 25 + color: "chocolate" + Text { + anchors.fill: parent + fontSizeMode: Text.Fit; minimumPixelSize: 10; font.pixelSize: 25 + horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter + text: "x" + } + + MouseArea { + anchors.fill: parent + onClicked: model.window.close() + } + } + + // The window itself + WindowItem { + id: windowItem + anchors.fill: parent + anchors.margins: 3 + anchors.topMargin: 25 + window: model.window + + Rectangle { + anchors.fill: fpsOverlay + color: "black" + opacity: 0.3 + } + + // And its FPS overlay + Column { + id: fpsOverlay + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 4 + Text { + text: "averageFps: " + Number(frameTimer.averageFps).toLocaleString(Qt.locale("en_US"), 'f', 1) + font.pixelSize: 16; color: "white" + } + Text { + text: "maximumFps: " + Number(frameTimer.maximumFps).toLocaleString(Qt.locale("en_US"), 'f', 1) + font.pixelSize: 16; color: "white" + } + Text { + text: "minimumFps: " + Number(frameTimer.minimumFps).toLocaleString(Qt.locale("en_US"), 'f', 1) + font.pixelSize: 16; color: "white" + } + Text { + text: "jitterFps: " + Number(frameTimer.jitterFps).toLocaleString(Qt.locale("en_US"), 'f', 1) + font.pixelSize: 16; color: "white" + } + } + } + + // FrameTimer will calculate the frame rate numbers for the given window + FrameTimer { + id: frameTimer + // no sense in trying to update the FrameTimer while the window has no surface (or has just an empty one) + running: window && window.contentState === WindowObject.SurfaceWithContent + window: model.window + } + + Component.onCompleted: { + winChrome.x = 300 + model.index * 50; + winChrome.y = 10 + model.index * 30; + } + + readonly property bool shouldBeRemoved: model.window && model.window.contentState === WindowObject.NoSurface + onShouldBeRemovedChanged: if (shouldBeRemoved) windowsModel.remove(model.index, 1) + } + } + + // Populates the windowsModel + Connections { + target: WindowManager + onWindowAdded: windowsModel.append({"window":window}) + } + } +} diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/dummyicon b/examples/applicationmanager/monitor/apps/tld.monitor.app/dummyicon deleted file mode 100644 index e69de29b..00000000 --- a/examples/applicationmanager/monitor/apps/tld.monitor.app/dummyicon +++ /dev/null diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/info.yaml b/examples/applicationmanager/monitor/apps/tld.monitor.app/info.yaml deleted file mode 100644 index cd0a58c8..00000000 --- a/examples/applicationmanager/monitor/apps/tld.monitor.app/info.yaml +++ /dev/null @@ -1,9 +0,0 @@ -formatVersion: 1 -formatType: am-application ---- -id: 'tld.monitor.app' -icon: 'dummyicon' -code: 'app.qml' -runtime: 'qml' -name: - en: 'Application' diff --git a/examples/applicationmanager/monitor/doc/images/monitor.png b/examples/applicationmanager/monitor/doc/images/monitor.png Binary files differdeleted file mode 100644 index d3b3cddd..00000000 --- a/examples/applicationmanager/monitor/doc/images/monitor.png +++ /dev/null diff --git a/examples/applicationmanager/monitor/doc/src/monitor.qdoc b/examples/applicationmanager/monitor/doc/src/monitor.qdoc deleted file mode 100644 index 5fc0faa3..00000000 --- a/examples/applicationmanager/monitor/doc/src/monitor.qdoc +++ /dev/null @@ -1,108 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:FDL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - -\example applicationmanager/monitor -\title Performance Monitoring Example -\image monitor.png Screenshot -\brief A resource and performance monitor. -\ingroup applicationmanager-examples - -\section1 Introduction - -This example shows the basic usage of system and process monitoring APIs offered by the -application-manager: - \list - \li SystemMonitor - \li ProcessMonitor - \endlist - -The example monitors performance (frame rate), as well as rescource usage (memory and CPU). - -\section2 Invocation -The example can be started from within the "monitor" folder with: -\badcode -path/to/bin/appman -c am-config.yaml -\endcode - -\note The application-manager attempts to register a \c 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 -\c --start-session-dbus option. - -\section1 Walkthrough - -We will start from the bottom, because this is the essential part. - -\section2 SystemMonitor and ProcessMonitor Usage - -\quotefromfile applicationmanager/monitor/system-ui/main.qml -\skipto ProcessMonitor -\printuntil ApplicationManager.application(0).start() -\printline } -The ProcessMonitor will monitor the process that is associated with -its \l {ProcessMonitor::applicationId}{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 \c tld.monitor.app application. Measurements will be performed each -second (1000 ms) and 12 reading points will be kept in the model (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 \c 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. - -For the SystemMonitor, 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. - -\section2 The User Interface - -\quotefromfile applicationmanager/monitor/system-ui/main.qml -\skipto import Qt -\printto ProcessMonitor -\dots - -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 \c 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. - -*/ diff --git a/examples/applicationmanager/monitor/monitor.qmlproject b/examples/applicationmanager/monitor/monitor.qmlproject deleted file mode 100644 index 5ce6345e..00000000 --- a/examples/applicationmanager/monitor/monitor.qmlproject +++ /dev/null @@ -1,17 +0,0 @@ -import QmlProject 1.1 - -Project { - mainFile: "system-ui/main.qml" - - QmlFiles { - directory: "." - } - ImageFiles { - directory: "apps" - } - Files { - directory: "." - filter: "*.yaml" - } -} - diff --git a/examples/applicationmanager/monitor/system-ui/MonitorChart.qml b/examples/applicationmanager/monitor/system-ui/MonitorChart.qml deleted file mode 100644 index 0d49e346..00000000 --- a/examples/applicationmanager/monitor/system-ui/MonitorChart.qml +++ /dev/null @@ -1,97 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:BSD-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: BSD-3-Clause -** -****************************************************************************/ - -import QtQuick 2.6 - -Rectangle { - property alias title: title.text - property alias reading: reading.text - property alias model: listview.model - property alias delegate: listview.delegate - - width: 185 - height: 250 - color: "black" - border { width: 1; color: "white" } - - Rectangle { - width: parent.width - height: 30 - color: "black" - border { width: 1; color: "white" } - - MonitorText { - id: title - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.verticalCenter: parent.verticalCenter - } - - MonitorText { - id: reading - anchors.right: parent.right - anchors.rightMargin: 10 - anchors.verticalCenter: parent.verticalCenter - } - } - - ListView { - id: listview - anchors.fill: parent - anchors.margins: 10 - anchors.topMargin: 40 - orientation: ListView.Horizontal - layoutDirection: Qt.RightToLeft - spacing: 3 - clip: true - } -} diff --git a/examples/applicationmanager/monitor/system-ui/MonitorText.qml b/examples/applicationmanager/monitor/system-ui/MonitorText.qml deleted file mode 100644 index 263a22ec..00000000 --- a/examples/applicationmanager/monitor/system-ui/MonitorText.qml +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:BSD-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: BSD-3-Clause -** -****************************************************************************/ - -import QtQuick 2.6 - -Text { - font.pixelSize: 14 - color: "white" -} diff --git a/examples/applicationmanager/monitor/system-ui/Tile.qml b/examples/applicationmanager/monitor/system-ui/Tile.qml deleted file mode 100644 index d3a06b18..00000000 --- a/examples/applicationmanager/monitor/system-ui/Tile.qml +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:BSD-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: BSD-3-Clause -** -****************************************************************************/ - -import QtQuick 2.6 - -Item { - width: 225 - height: 300 -} diff --git a/examples/applicationmanager/monitor/system-ui/main.qml b/examples/applicationmanager/monitor/system-ui/main.qml deleted file mode 100644 index 8d3a1327..00000000 --- a/examples/applicationmanager/monitor/system-ui/main.qml +++ /dev/null @@ -1,260 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:BSD-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: BSD-3-Clause -** -****************************************************************************/ - -import QtQuick 2.6 -import QtQuick.Window 2.0 -import QtApplicationManager.SystemUI 1.0 - -Window { - id: root - - property alias primaryWindow: primaryContainer.window - property alias secondaryWindow: secondaryContainer.window - - width: 720 - height: 600 - color: "black" - - SystemMonitor { - id: systemMonitor - reportingInterval: 1000 - count: 12 - idleLoadThreshold: 0.05 - cpuLoadReportingEnabled: true - gpuLoadReportingEnabled: true - memoryReportingEnabled: true - } - - Column { - x: 10 - padding: 20 - - Tile { - Column { - 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 { text: "Used Memory: " + (systemMonitor.memoryUsed / 1e9).toFixed(1) + " GB" } - MonitorText { text: "Idle Threshold: " + systemMonitor.idleLoadThreshold * 100 + " %" } - MonitorText { text: "Idle: " + systemMonitor.idle } - MonitorText { text: "GPU Load: " + systemMonitor.gpuLoad * 100 + " %" } - } - } - - Tile { - MonitorChart { - title: "CPU Load" - model: systemMonitor - reading: (systemMonitor.cpuLoad * 100).toFixed(1) + " %" - 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 = primaryContainer.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: primaryContainer - active: true - window: root.primaryWindow - onActivated: { - processMon.monitoredWindows = [primaryWindow]; - secondaryContainer.active = false; - } - } - - WindowContainer { - id: secondaryContainer - window: root.secondaryWindow - onActivated: { - processMon.monitoredWindows = [secondaryWindow]; - primaryContainer.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 - onWindowAdded: { - if (window.windowProperty("windowType") === "primary") { - primaryWindow = window - } else { - secondaryWindow = window - } - } - - onWindowAboutToBeRemoved: { - if (primaryWindow === window) { - primaryWindow = null; - } else if (secondaryWindow === window) { - secondaryWindow = null; - } - } - } - - 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"; - } - } - - Component.onCompleted: { - ApplicationManager.application(0).start(); - } -} diff --git a/examples/applicationmanager/process-status/am-config.yaml b/examples/applicationmanager/process-status/am-config.yaml new file mode 100644 index 00000000..5db21e36 --- /dev/null +++ b/examples/applicationmanager/process-status/am-config.yaml @@ -0,0 +1,8 @@ +formatVersion: 1 +formatType: am-configuration +--- +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + +ui: + mainQml: "${CONFIG_PWD}/system-ui/main.qml" diff --git a/examples/applicationmanager/process-status/apps/process-status.cpu-hog/icon.png b/examples/applicationmanager/process-status/apps/process-status.cpu-hog/icon.png Binary files differnew file mode 100644 index 00000000..04ca44dd --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.cpu-hog/icon.png diff --git a/examples/applicationmanager/process-status/apps/process-status.cpu-hog/info.yaml b/examples/applicationmanager/process-status/apps/process-status.cpu-hog/info.yaml new file mode 100644 index 00000000..89807bd3 --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.cpu-hog/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'process-status.cpu-hog' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'CPU Hog' diff --git a/examples/applicationmanager/process-status/apps/process-status.cpu-hog/main.qml b/examples/applicationmanager/process-status/apps/process-status.cpu-hog/main.qml new file mode 100644 index 00000000..f38964cc --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.cpu-hog/main.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.Application 1.0 + +ApplicationManagerWindow { + color: "red" + + Text { + anchors.fill: parent + text: "This application consumes a lot of CPU time." + font.pixelSize: 15 + color: "white" + } + + Rectangle { + id: rectangle + width: Math.min(parent.width, parent.height) / 2 + height: width + anchors.centerIn: parent + color: "grey" + Timer { + interval: 10 + repeat: true + running: true + onTriggered: { + rectangle.rotation += 1; + doBusyWork(); + } + function doBusyWork() { + var i = 1; + var n = 1; + for (i = 1; i < 100000; i++) { + n = n + n * n / 1.5; + } + } + } + } +} diff --git a/examples/applicationmanager/process-status/apps/process-status.mem-hog/icon.png b/examples/applicationmanager/process-status/apps/process-status.mem-hog/icon.png Binary files differnew file mode 100644 index 00000000..b149340c --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.mem-hog/icon.png diff --git a/examples/applicationmanager/process-status/apps/process-status.mem-hog/info.yaml b/examples/applicationmanager/process-status/apps/process-status.mem-hog/info.yaml new file mode 100644 index 00000000..a437b108 --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.mem-hog/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'process-status.mem-hog' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Memory Hog' diff --git a/examples/applicationmanager/monitor/system-ui/WindowContainer.qml b/examples/applicationmanager/process-status/apps/process-status.mem-hog/main.qml index 66215eb9..ba62b988 100644 --- a/examples/applicationmanager/monitor/system-ui/WindowContainer.qml +++ b/examples/applicationmanager/process-status/apps/process-status.mem-hog/main.qml @@ -50,29 +50,37 @@ ** ****************************************************************************/ -import QtQuick 2.6 -import QtApplicationManager.SystemUI 1.0 +import QtQuick 2.4 +import QtApplicationManager.Application 1.0 -Rectangle { - property bool active: false - property alias window: windowItem.window - signal activated() +ApplicationManagerWindow { + id: root + color: "green" - width: 180; height: 65 - - color: active ? "white" : "transparent" - - WindowItem { - id: windowItem - anchors.margins: 2 + Text { anchors.fill: parent + text: "This application consumes a lot of memory." + font.pixelSize: 15 + color: "white" } - MouseArea { - anchors.fill: parent - onClicked: { - active = true; - activated(); + Rectangle { + id: rectangle + width: Math.min(parent.width, parent.height) / 2 + height: width + anchors.centerIn: parent + color: "grey" + Timer { + interval: 10 + repeat: true + running: true + onTriggered: { + rectangle.rotation += 1; + root.contentItem.grabToImage(function(result) { + foo.push(result); + }); + } + property var foo: [] } } } diff --git a/examples/applicationmanager/process-status/apps/process-status.slim/icon.png b/examples/applicationmanager/process-status/apps/process-status.slim/icon.png Binary files differnew file mode 100644 index 00000000..be6ffc57 --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.slim/icon.png diff --git a/examples/applicationmanager/process-status/apps/process-status.slim/info.yaml b/examples/applicationmanager/process-status/apps/process-status.slim/info.yaml new file mode 100644 index 00000000..3c148ed4 --- /dev/null +++ b/examples/applicationmanager/process-status/apps/process-status.slim/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'process-status.slim' +icon: 'icon.png' +code: 'main.qml' +runtime: 'qml' +name: + en: 'Slim' diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/ApplicationWindow.qml b/examples/applicationmanager/process-status/apps/process-status.slim/main.qml index bcafcc7a..5967789b 100644 --- a/examples/applicationmanager/monitor/apps/tld.monitor.app/ApplicationWindow.qml +++ b/examples/applicationmanager/process-status/apps/process-status.slim/main.qml @@ -50,31 +50,33 @@ ** ****************************************************************************/ -import QtQuick 2.4 +import QtQuick 2.11 import QtApplicationManager.Application 1.0 ApplicationManagerWindow { - id: root + color: "blue" - property alias title: title.text - property alias idle: pause.duration - property string windowType - - color: "darkgreen" - - Text{ - id: title + Text { + anchors.fill: parent + text: "This a slim application that just animates." + font.pixelSize: 15 color: "white" - anchors.centerIn: parent } - SequentialAnimation on color { - loops: Animation.Infinite - ColorAnimation { to: "darkolivegreen"; duration: 500 } - PauseAnimation { id: pause; duration: 0 } - ColorAnimation { to: "darkgreen"; duration: 500 } - PauseAnimation { duration: pause.duration } - } + Rectangle { + id: rectangle + width: Math.min(parent.width, parent.height) / 2 + height: width + anchors.centerIn: parent + color: "grey" + RotationAnimation { + target: rectangle + from: 0 + to: 360 + duration: 1000 + running: true + loops: Animation.Infinite + } - onWindowTypeChanged: root.setWindowProperty("windowType", windowType) + } } diff --git a/examples/applicationmanager/process-status/doc/images/process-status-example.png b/examples/applicationmanager/process-status/doc/images/process-status-example.png Binary files differnew file mode 100644 index 00000000..6c7df5e4 --- /dev/null +++ b/examples/applicationmanager/process-status/doc/images/process-status-example.png diff --git a/examples/applicationmanager/process-status/doc/src/process-status-example.qdoc b/examples/applicationmanager/process-status/doc/src/process-status-example.qdoc new file mode 100644 index 00000000..0cad1578 --- /dev/null +++ b/examples/applicationmanager/process-status/doc/src/process-status-example.qdoc @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + +\example applicationmanager/process-status +\title Displaying Information About Application Processes +\image process-status-example.png +\brief How to use ProcessStatus to display application process information. +\ingroup applicationmanager-examples + +This example shows you how to use the \l ProcessStatus component to display information +about an application's process. + +This example is based on the simpler \l {"Hello World!" System-UI Example} {Hello World} one. You might +want to start from there if you haven't seen it already. + +On the left side the built-in applications are listed in a column, where each application has a row containing +its icon and name next to a tabbed view that shows information about the application's process (in case the +application is actually running). + +On the right side of the System-UI the windows of the running applications are stacked in a column, in order of +appearance (oldest window at the top and youngest at the bottom). + +There are three applications available, a red one called "CPU Hog" which consumes a lot of CPU, a green one +called "Memory Hog" that continually increases its memory consumption (so don't leave it running for too long +as it will eat up all available RAM eventually) and a blue one called "Slim", which behaves normally. + +*/ diff --git a/examples/applicationmanager/process-status/process-status.pro b/examples/applicationmanager/process-status/process-status.pro new file mode 100644 index 00000000..e2a794d0 --- /dev/null +++ b/examples/applicationmanager/process-status/process-status.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +CONFIG += am-systemui + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/process-status +INSTALLS += target + +AM_COPY_DIRECTORIES += apps system-ui +AM_COPY_FILES += am-config.yaml + +prefix_build:tpath = $$target.path +else:tpath = $$_PRO_FILE_PWD_ + +AM_DEFAULT_ARGS = -c $$tpath/am-config.yaml --start-session-dbus --verbose -r + +example_sources.path = $$target.path +example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES +INSTALLS += example_sources diff --git a/examples/applicationmanager/process-status/system-ui/ApplicationDisplay.qml b/examples/applicationmanager/process-status/system-ui/ApplicationDisplay.qml new file mode 100644 index 00000000..652cffd4 --- /dev/null +++ b/examples/applicationmanager/process-status/system-ui/ApplicationDisplay.qml @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager.SystemUI 1.0 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.11 + +Frame { + id: root + property string name + property var application + + width: 450 + + RowLayout { + width: parent.width + + Column { + id: iconAndText + + Image { + source: root.application.icon + MouseArea { + anchors.fill: parent + onClicked: root.application.runState === Am.Running ? application.stop() : application.start() + } + } + Label { + font.pixelSize: 18 + text: root.name + } + + } + + Frame { + opacity: root.application.runState === Am.Running ? 1 : 0 + Layout.fillWidth: true + + ColumnLayout { + width: parent.width + TabBar { + id: tabBar + Layout.fillWidth: true + TabButton { + text: "Stats" + font.pixelSize: 15 + } + TabButton { + text: "CPU Graph" + font.pixelSize: 15 + } + } + + StackLayout { + Layout.fillWidth: true + currentIndex: tabBar.currentIndex + Stats { + application: root.application + } + CpuGraph { + application: root.application + } + } + } + } + + } +} diff --git a/examples/applicationmanager/process-status/system-ui/CpuGraph.qml b/examples/applicationmanager/process-status/system-ui/CpuGraph.qml new file mode 100644 index 00000000..109e902b --- /dev/null +++ b/examples/applicationmanager/process-status/system-ui/CpuGraph.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.11 +import QtQuick.Controls 2.4 +import QtApplicationManager 1.0 +import QtApplicationManager.SystemUI 1.0 + +/* + This file shows how to use ProcessStatus inside a MonitorModel to draw a graph + */ +Pane { + id: root + + property var application + + ListView { + id: listView + anchors.fill: parent + orientation: ListView.Horizontal + spacing: (root.width / model.count) * 0.2 + clip: true + interactive: false + + model: MonitorModel { + id: monitorModel + running: root.visible && root.application.runState === Am.Running + ProcessStatus { + applicationId: root.application.id + } + } + + delegate: Rectangle { + width: (root.width / monitorModel.count) * 0.8 + height: model.cpuLoad * root.height + y: root.height - height + color: root.palette.highlight + } + } + + Label { + anchors.top: parent.top + text: "100%" + font.pixelSize: 15 + } + + Label { + anchors.verticalCenter: parent.verticalCenter + text: "50%" + font.pixelSize: 15 + } + + Label { + anchors.bottom: parent.bottom + text: "0%" + font.pixelSize: 15 + } +} diff --git a/examples/applicationmanager/process-status/system-ui/MemoryText.qml b/examples/applicationmanager/process-status/system-ui/MemoryText.qml new file mode 100644 index 00000000..fc745d55 --- /dev/null +++ b/examples/applicationmanager/process-status/system-ui/MemoryText.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.4 + +Label { + property var value + property string name + text: name + ": " + (value / 1e6).toFixed(0) + " MB" + font.pixelSize: 15 +} diff --git a/examples/applicationmanager/process-status/system-ui/Stats.qml b/examples/applicationmanager/process-status/system-ui/Stats.qml new file mode 100644 index 00000000..69e4d3ea --- /dev/null +++ b/examples/applicationmanager/process-status/system-ui/Stats.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.4 +import QtApplicationManager.SystemUI 1.0 + +/* + This file shows how to use ProcessStatus alongside a Timer (instead of putting it inside a MonitorModel) + when all that is needed is the latest information on a given application process. + */ +Grid { + spacing: 10 + columns: 2 + rows: 5 + + property var application + + property var status: ProcessStatus { + id: processStatus + applicationId: root.application.id + } + property var timer: Timer { + id: updateTimer + interval: 500 + repeat: true + running: root.visible && root.application.runState === Am.Running + onTriggered: processStatus.update() + } + + + Label { text: "processId: " + processStatus.processId; font.pixelSize: 15 } + Label { + property string loadPercent: Number(processStatus.cpuLoad * 100).toLocaleString(Qt.locale("en_US"), 'f', 1) + text: "cpuLoad: " + loadPercent + "%" + font.pixelSize: 15 + } + MemoryText { name: "PSS.total"; value: processStatus.memoryPss.total } + MemoryText { name: "PSS.text"; value: processStatus.memoryPss.text } + MemoryText { name: "PSS.heap"; value: processStatus.memoryPss.heap } + MemoryText { name: "RSS.total"; value: processStatus.memoryRss.total } + MemoryText { name: "RSS.text"; value: processStatus.memoryRss.text } + MemoryText { name: "RSS.heap"; value: processStatus.memoryRss.heap } + MemoryText { name: "Virtual.total"; value: processStatus.memoryVirtual.total } +} diff --git a/examples/applicationmanager/process-status/system-ui/main.qml b/examples/applicationmanager/process-status/system-ui/main.qml new file mode 100644 index 00000000..5bc7cf68 --- /dev/null +++ b/examples/applicationmanager/process-status/system-ui/main.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Controls 2.4 +import QtApplicationManager.SystemUI 1.0 + +Pane { + width: 900 + height: appsColumn.y + appsColumn.height + + // Show name, icon and ProcessStatus data for each application + Column { + id: appsColumn + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: 2 + spacing: 10 + Repeater { + model: ApplicationManager + ApplicationDisplay { + name: model.name + application: model.application + } + } + } + + // Show windows of running applications + Column { + id: windowsColumn + anchors.left: appsColumn.right + anchors.right: parent.right + anchors.margins: 10 + Repeater { + model: WindowManager + WindowItem { + width: windowsColumn.width + height: 200 + window: model.window + } + } + } +} diff --git a/src/main-lib/main.cpp b/src/main-lib/main.cpp index 2323ecd0..1c81d28a 100644 --- a/src/main-lib/main.cpp +++ b/src/main-lib/main.cpp @@ -133,11 +133,18 @@ #include "crashhandler.h" #include "qmllogger.h" #include "startuptimer.h" -#include "systemmonitor.h" -#include "processmonitor.h" #include "applicationipcmanager.h" #include "unixsignalhandler.h" +// monitor-lib +#include "cpustatus.h" +#include "frametimer.h" +#include "gpustatus.h" +#include "iostatus.h" +#include "memorystatus.h" +#include "monitormodel.h" +#include "processstatus.h" + #include "../plugin-interfaces/startupinterface.h" @@ -616,8 +623,15 @@ void Main::setupQmlEngine(const QStringList &importPaths, const QString &quickCo #if !defined(AM_HEADLESS) qmlRegisterType<QmlInProcessApplicationManagerWindow>("QtApplicationManager.Application", 1, 0, "ApplicationManagerWindow"); #endif - qmlRegisterType<ProcessMonitor>("QtApplicationManager.SystemUI", 1, 0, "ProcessMonitor"); - qmlRegisterType<SystemMonitor>("QtApplicationManager.SystemUI", 1, 0, "SystemMonitor"); + + // monitor-lib + qmlRegisterType<CpuStatus>("QtApplicationManager", 1, 0, "CpuStatus"); + qmlRegisterType<FrameTimer>("QtApplicationManager", 1, 0, "FrameTimer"); + qmlRegisterType<GpuStatus>("QtApplicationManager", 1, 0, "GpuStatus"); + qmlRegisterType<IoStatus>("QtApplicationManager", 1, 0, "IoStatus"); + qmlRegisterType<MemoryStatus>("QtApplicationManager", 1, 0, "MemoryStatus"); + qmlRegisterType<MonitorModel>("QtApplicationManager", 1, 0, "MonitorModel"); + qmlRegisterType<ProcessStatus>("QtApplicationManager.SystemUI", 1, 0, "ProcessStatus"); StartupTimer::instance()->checkpoint("after QML registrations"); diff --git a/src/monitor-lib/cpustatus.cpp b/src/monitor-lib/cpustatus.cpp new file mode 100644 index 00000000..5bf2729c --- /dev/null +++ b/src/monitor-lib/cpustatus.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "cpustatus.h" + +#include <QThread> + +/*! + \qmltype CpuStatus + \inqmlmodule QtApplicationManager + \ingroup common-instantiatable + \brief Provides information on the CPU status. + + As the name implies, CpuStatus provides information on the status of the CPU. Its property values + are updated whenever the method update() is called. + + You can use this component as a MonitorModel data source if you want to plot its + previous values over time. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + MonitorModel { + CpuStatus {} + } + \endqml + + You can also use it alongside a Timer for instance, when you're only interested in its current value. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + CpuStatus { id: cpuStatus } + Timer { + interval: 500 + running: true + repeat: true + onTriggered: cpuStatus.update() + } + Text { + property string loadPercent: Number(cpuStatus.cpuLoad * 100).toLocaleString(Qt.locale("en_US"), 'f', 1) + text: "cpuLoad: " + loadPercent + "%" + } + \endqml +*/ + +QT_USE_NAMESPACE_AM + +CpuStatus::CpuStatus(QObject *parent) + : QObject(parent) + , m_cpuReader(new CpuReader) + , m_cpuLoad(0) +{ +} + +/*! + \qmlproperty real CpuStatus::cpuLoad + \readonly + + The CPU utilization when update() was last called, as a value ranging from 0 (inclusive, completely + idle) to 1 (inclusive, fully busy). + + \sa CpuStatus::update +*/ +qreal CpuStatus::cpuLoad() const +{ + return m_cpuLoad; +} + +/*! + \qmlproperty int CpuStatus::cpuCores + \readonly + + The number of physical CPU cores that are installed on the system. +*/ +int CpuStatus::cpuCores() const +{ + return QThread::idealThreadCount(); +} + +/*! + \qmlmethod CpuStatus::update + + Updates the cpuLoad property. + + \sa CpuStatus::cpuLoad +*/ +void CpuStatus::update() +{ + qreal newLoad = m_cpuReader->readLoadValue(); + if (newLoad != m_cpuLoad) { + m_cpuLoad = newLoad; + emit cpuLoadChanged(); + } +} + +/*! + \qmlproperty list<string> CpuStatus::roleNames + \readonly + + Names of the roles provided by CpuStatus when used as a MonitorModel data source. + + \sa MonitorModel +*/ +QStringList CpuStatus::roleNames() const +{ + return { qSL("cpuLoad") }; +} diff --git a/src/monitor-lib/cpustatus.h b/src/monitor-lib/cpustatus.h new file mode 100644 index 00000000..e87a016d --- /dev/null +++ b/src/monitor-lib/cpustatus.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QtAppManCommon/global.h> + +#include <QtAppManManager/systemreader.h> + +#include <QObject> +#include <QScopedPointer> + +QT_BEGIN_NAMESPACE_AM + +class CpuStatus : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal cpuLoad READ cpuLoad NOTIFY cpuLoadChanged) + Q_PROPERTY(int cpuCores READ cpuCores CONSTANT) + + Q_PROPERTY(QStringList roleNames READ roleNames CONSTANT) + +public: + CpuStatus(QObject *parent = nullptr); + + qreal cpuLoad() const; + int cpuCores() const; + + QStringList roleNames() const; + + Q_INVOKABLE void update(); + +signals: + void cpuLoadChanged(); + +private: + QScopedPointer<CpuReader> m_cpuReader; + qreal m_cpuLoad; +}; + +QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/frametimer.cpp b/src/monitor-lib/frametimer.cpp index ef483736..534c3033 100644 --- a/src/monitor-lib/frametimer.cpp +++ b/src/monitor-lib/frametimer.cpp @@ -41,12 +41,75 @@ #include "frametimer.h" +#include <QQuickWindow> +#include <qqmlinfo.h> + +#include "waylandwindow.h" +#include "inprocesswindow.h" + +/*! + \qmltype FrameTimer + \inqmlmodule QtApplicationManager + \ingroup common-instantiatable + \brief Provides frame-rate information about a given window. + + FrameTimer is used to get frame-rate information for a given window. The window can be either + a toplevel Window (from the QtQuick.Window module) or a WindowObject + (from the QtApplicationManager.SystemUI module). + + The following snippet shows how to use FrameTimer to display the frame-rate of a Window: + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + + Window { + id: toplevelWindow + ... + FrameTimer { + id: frameTimer + running: topLevelWindow.visible + window: toplevelWindow + } + Text { + text: "FPS: " + Number(frameTimer.averageFps).toLocaleString(Qt.locale("en_US"), 'f', 1) + } + } + \endqml + + You can also use this component as a MonitorModel data source if you want to plot its + previous values over time: + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + + Window { + id: toplevelWindow + ... + MonitorModel { + running: true + FrameTimer { + window: toplevelWindow + } + } + } + \endqml + + Please note that when using FrameTimer as a MonitorModel data source there's no need to set it + to \l{FrameTimer::running}{running} as MonitorModel will already call update() as needed. +*/ + QT_BEGIN_NAMESPACE_AM const qreal FrameTimer::MicrosInSec = qreal(1000 * 1000); -FrameTimer::FrameTimer() -{ } +FrameTimer::FrameTimer(QObject *parent) + : QObject(parent) +{ + m_updateTimer.setInterval(1000); + connect(&m_updateTimer, &QTimer::timeout, this, &FrameTimer::update); +} void FrameTimer::newFrame() { @@ -67,24 +130,239 @@ void FrameTimer::reset() m_min = std::numeric_limits<int>::max(); } +/*! + \qmlproperty real FrameTimer::averageFps + \readonly + + The average frame rate of the given \l window, in frames per second, since update() + was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true). + + \sa window running update() +*/ qreal FrameTimer::averageFps() const { - return m_sum ? MicrosInSec * m_count / m_sum : qreal(0); + return m_averageFps; } +/*! + \qmlproperty real FrameTimer::minimumFps + \readonly + + The minimum frame rate of the given \l window, in frames per second, since update() + was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true). + + \sa window running update() +*/ qreal FrameTimer::minimumFps() const { - return m_max ? MicrosInSec / m_max : qreal(0); + return m_minimumFps; } +/*! + \qmlproperty real FrameTimer::maximumFps + \readonly + + The maximum frame rate of the given \l window, in frames per second, since update() + was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true). + + \sa window running update() +*/ qreal FrameTimer::maximumFps() const { - return m_min ? MicrosInSec / m_min : qreal(0); + return m_maximumFps; } +/*! + \qmlproperty real FrameTimer::jitterFps + \readonly + + The frame rate jitter of the given \l window, in frames per second, since update() + was last called (either manually or automatically in case \l{FrameTimer::running}{running} is set to true). + + \sa window running update() +*/ qreal FrameTimer::jitterFps() const { - return m_count ? m_jitter / m_count : qreal(0); + return m_jitterFps; +} + +/*! + \qmlproperty Object FrameTimer::window + + The window to be monitored, from which frame-rate information will be gathered. + It can be either a toplevel Window (from the QtQuick.Window module) or a WindowObject + (from the QtApplicationManager.SystemUI module). + + \sa WindowObject +*/ +QObject *FrameTimer::window() const +{ + return m_window; +} + +void FrameTimer::setWindow(QObject *value) +{ + if (m_window == value) + return; + +#if defined(AM_MULTI_PROCESS) + disconnectFromWaylandSurface(); +#endif + + if (m_window) + disconnect(m_window, nullptr, this, nullptr); + + m_window = value; + + if (m_window) { + if (!connectToQuickWindow() && !connectToAppManWindow()) + qmlWarning(this) << "The given window is neither a QQuickWindow nor a WindowObject."; + } + + emit windowChanged(); +} + +bool FrameTimer::connectToQuickWindow() +{ + QQuickWindow *quickWindow = qobject_cast<QQuickWindow*>(m_window); + if (!quickWindow) + return false; + + connect(quickWindow, &QQuickWindow::frameSwapped, this, &FrameTimer::newFrame, Qt::UniqueConnection); + return true; +} + +bool FrameTimer::connectToAppManWindow() +{ + Window *appManWindow = qobject_cast<Window*>(m_window); + if (!appManWindow) + return false; + + if (qobject_cast<InProcessWindow*>(appManWindow)) { + qmlWarning(this) << "It makes no sense to measure the FPS of a WindowObject in single-process mode." + " FrameTimer won't operate with the given window."; + return true; + } + +#if defined(AM_MULTI_PROCESS) + WaylandWindow *waylandWindow = qobject_cast<WaylandWindow*>(m_window); + Q_ASSERT(waylandWindow); + + connect(waylandWindow, &WaylandWindow::waylandSurfaceChanged, + this, &FrameTimer::connectToWaylandSurface, Qt::UniqueConnection); + + connectToWaylandSurface(); +#endif + + return true; +} + +#if defined(AM_MULTI_PROCESS) +void FrameTimer::connectToWaylandSurface() +{ + WaylandWindow *waylandWindow = qobject_cast<WaylandWindow*>(m_window); + Q_ASSERT(waylandWindow); + + disconnectFromWaylandSurface(); + + m_waylandSurface = waylandWindow->waylandSurface(); + if (m_waylandSurface) + connect(m_waylandSurface, &QWaylandQuickSurface::redraw, this, &FrameTimer::newFrame, Qt::UniqueConnection); +} + +void FrameTimer::disconnectFromWaylandSurface() +{ + if (!m_waylandSurface) + return; + + disconnect(m_waylandSurface, nullptr, this, nullptr); + + m_waylandSurface = nullptr; +} +#endif + +/*! + \qmlproperty list<string> FrameTimer::roleNames + \readonly + + Names of the roles provided by FrameTimer when used as a MonitorModel data source. + + \sa MonitorModel +*/ +QStringList FrameTimer::roleNames() const +{ + return { qSL("averageFps"), qSL("minimumFps"), qSL("maximumFps"), qSL("jitterFps") }; +} + +/*! + \qmlmethod FrameTimer::update + + Updates the properties averageFps, minimumFps, maximumFps and jitterFps. Then resets internal + counters so that new numbers can be taken for the new time period starting from the moment + this method is called. + + Note that you normally don't have to call this method directly, as FrameTimer does it automatically + every \l interval milliseconds while \l{FrameTimer::running}{running} is set to true. + + \sa running +*/ +void FrameTimer::update() +{ + m_averageFps = m_sum ? MicrosInSec * m_count / m_sum : qreal(0); + m_minimumFps = m_max ? MicrosInSec / m_max : qreal(0); + m_maximumFps = m_min ? MicrosInSec / m_min : qreal(0); + m_jitterFps = m_count ? m_jitter / m_count : qreal(0); + + // Start counting again for the next sampling period but keep m_timer running because + // we still need the diff between the last rendered frame and the upcoming one. + reset(); + + emit updated(); +} + +/*! + \qmlproperty bool FrameTimer::running + + If \c true, update() will get called automatically every \l interval milliseconds. + + When using FrameTimer as a MonitorModel data source, this property should be kept as \c false. + + \sa update() interval +*/ +bool FrameTimer::running() const +{ + return m_updateTimer.isActive(); +} + +void FrameTimer::setRunning(bool value) +{ + if (value && !m_updateTimer.isActive()) { + m_updateTimer.start(); + emit runningChanged(); + } else if (!value && m_updateTimer.isActive()) { + m_updateTimer.stop(); + emit runningChanged(); + } +} + +/*! + \qmlproperty int FrameTimer::interval + + The interval, in milliseconds, between update() calls while \l{FrameTimer::running}{running} is \c true. + + \sa update() running +*/ +int FrameTimer::interval() const +{ + return m_updateTimer.interval(); +} + +void FrameTimer::setInterval(int value) +{ + if (value != m_updateTimer.interval()) { + m_updateTimer.setInterval(value); + emit intervalChanged(); + } } QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/frametimer.h b/src/monitor-lib/frametimer.h index 17339e53..0a36a1b8 100644 --- a/src/monitor-lib/frametimer.h +++ b/src/monitor-lib/frametimer.h @@ -42,35 +42,92 @@ #pragma once #include <QElapsedTimer> +#include <QObject> +#include <QPointer> +#include <QTimer> #include <QtAppManCommon/global.h> #include <limits> +#if defined(AM_MULTI_PROCESS) +# include <QtWaylandCompositor/QWaylandQuickSurface> +#endif + QT_BEGIN_NAMESPACE_AM -class FrameTimer +class FrameTimer : public QObject { + Q_OBJECT + + Q_PROPERTY(qreal averageFps READ averageFps NOTIFY updated) + Q_PROPERTY(qreal minimumFps READ minimumFps NOTIFY updated) + Q_PROPERTY(qreal maximumFps READ maximumFps NOTIFY updated) + Q_PROPERTY(qreal jitterFps READ jitterFps NOTIFY updated) + + Q_PROPERTY(QObject* window READ window WRITE setWindow NOTIFY windowChanged) + + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) + + Q_PROPERTY(QStringList roleNames READ roleNames CONSTANT) public: - FrameTimer(); + FrameTimer(QObject *parent = nullptr); - void newFrame(); + QStringList roleNames() const; - void reset(); + Q_INVOKABLE void update(); qreal averageFps() const; qreal minimumFps() const; qreal maximumFps() const; qreal jitterFps() const; + QObject *window() const; + void setWindow(QObject *value); + + bool running() const; + void setRunning(bool value); + + int interval() const; + void setInterval(int value); + +signals: + void updated(); + void intervalChanged(); + void runningChanged(); + void windowChanged(); + +private slots: + void newFrame(); + private: + void reset(); + bool connectToQuickWindow(); + bool connectToAppManWindow(); + +#if defined(AM_MULTI_PROCESS) + void disconnectFromWaylandSurface(); + void connectToWaylandSurface(); + QPointer<QWaylandQuickSurface> m_waylandSurface; +#endif + int m_count = 0; int m_sum = 0; int m_min = std::numeric_limits<int>::max(); int m_max = 0; qreal m_jitter = 0.0; + QPointer<QObject> m_window; + QElapsedTimer m_timer; + QTimer m_updateTimer; + + qreal m_averageFps; + qreal m_minimumFps; + qreal m_maximumFps; + qreal m_jitterFps; + static const int IdealFrameTime = 16667; // usec - could be made configurable via an env variable static const qreal MicrosInSec; }; diff --git a/src/monitor-lib/gpustatus.cpp b/src/monitor-lib/gpustatus.cpp new file mode 100644 index 00000000..7e375917 --- /dev/null +++ b/src/monitor-lib/gpustatus.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "gpustatus.h" + +/*! + \qmltype GpuStatus + \inqmlmodule QtApplicationManager + \ingroup common-instantiatable + \brief Provides information on the GPU status. + + GpuStatus provides information on the status of the GPU. Its property values + are updated whenever the method update() is called. + + You can use it alongside a Timer for instance to periodically query the status of the GPU: + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + GpuStatus { id: gpuStatus } + Timer { + interval: 500 + running: true + repeat: true + onTriggered: gpuStatus.update() + } + Text { + property string loadPercent: Number(gpuStatus.gpuLoad * 100).toLocaleString(Qt.locale("en_US"), 'f', 1) + text: "GPU load: " + loadPercent + "%" + } + \endqml + + You can also use this component as a MonitorModel data source if you want to plot its + previous values over time: + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + MonitorModel { + GpuStatus {} + } + \endqml + +*/ + +QT_USE_NAMESPACE_AM + +GpuStatus::GpuStatus(QObject *parent) + : QObject(parent) + , m_gpuReader(new GpuReader) + , m_gpuLoad(0) +{ +} + +/*! + \qmlproperty real GpuStatus::gpuLoad + \readonly + + GPU utilization when update() was last called, as a value ranging from 0 (inclusive, + completely idle) to 1 (inclusive, fully busy). + + \note This is dependent on tools from the graphics hardware vendor and might not work on + every system. + + Currently, this only works on \e Linux with either \e Intel or \e NVIDIA chipsets, plus the + tools from the respective vendors have to be installed: + + \table + \header + \li Hardware + \li Tool + \li Notes + \row + \li NVIDIA + \li \c nvidia-smi + \li The utilization will only be shown for the first GPU of the system, in case multiple GPUs + are installed. + \row + \li Intel + \li \c intel_gpu_top + \li The binary has to be made set-UID root, e.g. via \c{sudo chmod +s $(which intel_gpu_top)}, + or the application-manager has to be run as the \c root user. + \endtable + + \sa \l{GpuStatus::update}{update()} +*/ +qreal GpuStatus::gpuLoad() const +{ + return m_gpuLoad; +} + +/*! + \qmlmethod GpuStatus::update + + Updates the gpuLoad property. + + \sa gpuLoad +*/ +void GpuStatus::update() +{ + qreal newLoad = m_gpuReader->readLoadValue(); + if (newLoad != m_gpuLoad) { + m_gpuLoad = newLoad; + emit gpuLoadChanged(); + } +} + +/*! + \qmlproperty list<string> GpuStatus::roleNames + \readonly + + Names of the roles provided by GpuStatus when used as a MonitorModel data source. + + \sa MonitorModel +*/ +QStringList GpuStatus::roleNames() const +{ + return { qSL("gpuLoad") }; +} + diff --git a/src/monitor-lib/gpustatus.h b/src/monitor-lib/gpustatus.h new file mode 100644 index 00000000..e843bb72 --- /dev/null +++ b/src/monitor-lib/gpustatus.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QtAppManCommon/global.h> + +#include <QtAppManManager/systemreader.h> + +#include <QObject> +#include <QScopedPointer> + +QT_BEGIN_NAMESPACE_AM + +class GpuStatus : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal gpuLoad READ gpuLoad NOTIFY gpuLoadChanged) + + Q_PROPERTY(QStringList roleNames READ roleNames CONSTANT) + +public: + GpuStatus(QObject *parent = nullptr); + + qreal gpuLoad() const; + + QStringList roleNames() const; + + Q_INVOKABLE void update(); + +signals: + void gpuLoadChanged(); + +private: + QScopedPointer<GpuReader> m_gpuReader; + qreal m_gpuLoad; +}; + +QT_END_NAMESPACE_AM + diff --git a/src/monitor-lib/iostatus.cpp b/src/monitor-lib/iostatus.cpp new file mode 100644 index 00000000..1fada231 --- /dev/null +++ b/src/monitor-lib/iostatus.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "iostatus.h" + +#include <QFile> + +/*! + \qmltype IoStatus + \inqmlmodule QtApplicationManager + \ingroup common-instantiatable + \brief Provides information on the status of I/O devices. + + IoStatus provides information on the status of I/O devices. + Its property values are updated whenever the method update() is called. + + You can use this component as a MonitorModel data source if you want to plot its + previous values over time. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + MonitorModel { + IoStatus { + deviceNames: ["sda", "sdb"] + } + } + \endqml + + You can also use it alongside a Timer for instance, when you're only interested in its current value. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + IoStatus { + id: ioStatus + deviceNames: ["sda", "sdb"] + } + Timer { + interval: 500 + running: true + repeat: true + onTriggered: ioStatus.update() + } + Text { + property string loadPercent: Number(ioStatus.ioLoad.sda * 100).toLocaleString(Qt.locale("en_US"), 'f', 1) + text: "sda load: " + loadPercent + "%" + } + \endqml +*/ + +QT_USE_NAMESPACE_AM + +IoStatus::IoStatus(QObject *parent) + : QObject(parent) +{ +} + +IoStatus::~IoStatus() +{ + qDeleteAll(m_ioHash); +} + +/*! + \qmlproperty list<string> IoStatus::deviceNames + + Names of the I/O devices to be probed. + + \note Currently this is only supported on Linux: device names have to match to filenames in + the \c /sys/block directory. +*/ +QStringList IoStatus::deviceNames() const +{ + return m_deviceNames; +} + +void IoStatus::setDeviceNames(const QStringList &value) +{ + qDeleteAll(m_ioHash); + m_ioHash.clear(); + + m_deviceNames = value; + + for (auto it = m_deviceNames.cbegin(); it != m_deviceNames.cend(); ++it) + addIoReader(*it); + + emit deviceNamesChanged(); +} + +/*! + \qmlproperty var IoStatus::ioLoad + \readonly + + A map of devices registered in deviceNames and their corresponding I/O loads in the + range [0, 1]. For instance the load of a device named "sda" can be accessed through + \c ioLoad.sda. + + Devices whose status could not be fetched won't be present in this property. + + The value of this property is updated when update() is called. + + \sa update +*/ +QVariantMap IoStatus::ioLoad() const +{ + return m_ioLoad; +} + +/*! + \qmlproperty list<string> IoStatus::roleNames + \readonly + + Names of the roles provided by IoStatus when used as a MonitorModel data source. + + \sa MonitorModel +*/ +QStringList IoStatus::roleNames() const +{ + return { qSL("ioLoad") }; +} + +/*! + \qmlmethod IoStatus::update + + Updates the ioLoad property. + + \sa ioLoad +*/ +void IoStatus::update() +{ + m_ioLoad.clear(); + for (auto it = m_ioHash.cbegin(); it != m_ioHash.cend(); ++it) { + qreal ioVal = it.value()->readLoadValue(); + m_ioLoad.insert(it.key(), ioVal); + } + emit ioLoadChanged(); +} + +void IoStatus::addIoReader(const QString &deviceName) +{ + if (!QFile::exists(qSL("/dev/") + deviceName)) + return; + if (m_ioHash.contains(deviceName)) + return; + + IoReader *ior = new IoReader(deviceName.toLocal8Bit().constData()); + m_ioHash.insert(deviceName, ior); +} diff --git a/src/monitor-lib/systemmonitor_p.h b/src/monitor-lib/iostatus.h index 871216a6..26e0fe7e 100644 --- a/src/monitor-lib/systemmonitor_p.h +++ b/src/monitor-lib/iostatus.h @@ -41,94 +41,49 @@ #pragma once -#include <QObject> -#include <QHash> +#include <QtAppManCommon/global.h> -#include "frametimer.h" -#include "global.h" -#include "systemreader.h" +#include <QHash> +#include <QObject> +#include <QStringList> +#include <QVariant> -QT_FORWARD_DECLARE_CLASS(QQuickWindow) +#include <QtAppManManager/systemreader.h> QT_BEGIN_NAMESPACE_AM -class SystemMonitor; - -class SystemMonitorPrivate : public QObject // clazy:exclude=missing-qobject-macro +class IoStatus : public QObject { + Q_OBJECT + Q_PROPERTY(QStringList deviceNames READ deviceNames WRITE setDeviceNames NOTIFY deviceNamesChanged) + Q_PROPERTY(QVariantMap ioLoad READ ioLoad NOTIFY ioLoadChanged) + + Q_PROPERTY(QStringList roleNames READ roleNames CONSTANT) + public: - SystemMonitorPrivate(SystemMonitor *q) - : q_ptr(q) - { } - - static const SystemMonitorPrivate* get(const SystemMonitor *sysMon) { return sysMon->d_func(); } - - SystemMonitor *q_ptr; - Q_DECLARE_PUBLIC(SystemMonitor) - - // idle - qreal idleThreshold = 0.1; - CpuReader *idleCpu = nullptr; - int idleTimerId = 0; - bool isIdle = false; - - // memory thresholds - qreal memoryLowWarning = -1; - qreal memoryCriticalWarning = -1; - MemoryWatcher *memoryWatcher = nullptr; - - // fps - QHash<QObject *, FrameTimer *> frameTimer; - - // reporting - MemoryReader *memory = nullptr; - CpuReader *cpu = nullptr; - GpuReader *gpu = nullptr; - QHash<QString, IoReader *> ioHash; - int reportingInterval = 1000; - int count = 10; - int reportingTimerId = 0; - bool reportCpu = false; - bool reportGpu = false; - bool reportMem = false; - bool reportFps = false; - bool windowManagerConnectionCreated = false; - - struct Report - { - qreal cpuLoad = 0; - qreal gpuLoad = 0; - qreal fpsAvg = 0; - qreal fpsMin = 0; - qreal fpsMax = 0; - qreal fpsJitter = 0; - quint64 memoryUsed = 0; - QVariantMap ioLoad; - }; - QVector<Report> reports; - int reportPos = 0; - - // model - QHash<int, QByteArray> roleNames; - - void makeNewReport(); - - int latestReportPos() const; - -#if !defined(AM_HEADLESS) - void registerNewView(QQuickWindow *view); -#endif - - void setupFpsReporting(); - void setupTimer(); - - void timerEvent(QTimerEvent *te) override; - - const Report &reportForRow(int row) const; - - void updateModel(bool clear); - - void setReportingInterval(int intervalInMSec); + IoStatus(QObject *parent = nullptr); + virtual ~IoStatus(); + + QStringList deviceNames() const; + void setDeviceNames(const QStringList &value); + + QVariantMap ioLoad() const; + + QStringList roleNames() const; + + Q_INVOKABLE void update(); + +signals: + void deviceNamesChanged(); + void ioLoadChanged(); + +private: + void addIoReader(const QString &deviceName); + + QStringList m_deviceNames; + QHash<QString, IoReader *> m_ioHash; + QVariantMap m_ioLoad; }; QT_END_NAMESPACE_AM + diff --git a/src/monitor-lib/memorystatus.cpp b/src/monitor-lib/memorystatus.cpp new file mode 100644 index 00000000..3947bb0d --- /dev/null +++ b/src/monitor-lib/memorystatus.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "memorystatus.h" + +/*! + \qmltype MemoryStatus + \inqmlmodule QtApplicationManager + \ingroup common-instantiatable + \brief Provides information on the status of the RAM. + + MemoryStatus provides information on the status of the system's RAM (random-access memory). + Its property values are updated whenever the method update() is called. + + You can use this component as a MonitorModel data source if you want to plot its + previous values over time. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + MonitorModel { + MemoryStatus {} + } + \endqml + + You can also use it alongside a Timer for instance, when you're only interested in its current value. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + ... + MemoryStatus { id: memoryStatus } + Timer { + interval: 500 + running: true + repeat: true + onTriggered: memoryStatus.update() + } + Text { + text: "memory used: " + (memoryStatus.memoryUsed / 1e6).toFixed(0) + " MB" + } + \endqml +*/ + +QT_USE_NAMESPACE_AM + +MemoryStatus::MemoryStatus(QObject *parent) + : QObject(parent) + , m_memoryReader(new MemoryReader) + , m_memoryUsed(0) +{ +} + +/*! + \qmlproperty int MemoryStatus::totalMemory + \readonly + + The total amount of physical memory (RAM) installed on the system in bytes. + + \sa MemoryStatus::memoryUsed +*/ +quint64 MemoryStatus::totalMemory() const +{ +#if defined(Q_OS_LINUX) + auto limit = m_memoryReader->groupLimit(); + if (limit > 0 && limit < m_memoryReader->totalValue()) + return limit; + else + return m_memoryReader->totalValue(); +#else + return m_memoryReader->totalValue(); +#endif +} + +/*! + \qmlproperty int MemoryStatus::memoryUsed + \readonly + + The amount of physical memory (RAM) used in bytes. + + The value of this property is updated when MemoryStatus::update is called. + + \sa totalMemory +*/ +quint64 MemoryStatus::memoryUsed() const +{ + return m_memoryUsed; +} + +/*! + \qmlproperty list<string> MemoryStatus::roleNames + \readonly + + Names of the roles provided by MemoryStatus when used as a MonitorModel data source. + + \sa MonitorModel +*/ +QStringList MemoryStatus::roleNames() const +{ + return { qSL("memoryUsed") }; +} + +/*! + \qmlmethod MemoryStatus::update + + Updates the memoryUsed property. + + \sa memoryUsed +*/ +void MemoryStatus::update() +{ + quint64 newReading = m_memoryReader->readUsedValue(); + if (m_memoryUsed != newReading) { + m_memoryUsed = newReading; + emit memoryUsedChanged(); + } +} diff --git a/src/monitor-lib/memorystatus.h b/src/monitor-lib/memorystatus.h new file mode 100644 index 00000000..95e40807 --- /dev/null +++ b/src/monitor-lib/memorystatus.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QtAppManCommon/global.h> + +#include <QtAppManManager/systemreader.h> + +#include <QObject> +#include <QScopedPointer> + +QT_BEGIN_NAMESPACE_AM + +class MemoryStatus : public QObject +{ + Q_OBJECT + Q_PROPERTY(quint64 totalMemory READ totalMemory CONSTANT) + Q_PROPERTY(quint64 memoryUsed READ memoryUsed NOTIFY memoryUsedChanged) + + Q_PROPERTY(QStringList roleNames READ roleNames CONSTANT) + +public: + MemoryStatus(QObject *parent = nullptr); + + quint64 totalMemory() const; + quint64 memoryUsed() const; + + QStringList roleNames() const; + + Q_INVOKABLE void update(); + +signals: + void memoryUsedChanged(); + +private: + QScopedPointer<MemoryReader> m_memoryReader; + quint64 m_memoryUsed; +}; + +QT_END_NAMESPACE_AM + diff --git a/src/monitor-lib/monitor-lib.pro b/src/monitor-lib/monitor-lib.pro index 6bc837e5..792c85de 100644 --- a/src/monitor-lib/monitor-lib.pro +++ b/src/monitor-lib/monitor-lib.pro @@ -15,16 +15,23 @@ QT_FOR_PRIVATE *= \ CONFIG *= static internal_module HEADERS += \ - systemmonitor.h \ - systemmonitor_p.h \ - processmonitor.h \ - processmonitor_p.h \ - frametimer.h + cpustatus.h \ + frametimer.h \ + gpustatus.h \ + iostatus.h \ + memorystatus.h \ + monitormodel.h \ + processreader.h \ + processstatus.h \ SOURCES += \ - systemmonitor.cpp \ - processmonitor.cpp \ - processmonitor_p.cpp \ - frametimer.cpp + cpustatus.cpp \ + frametimer.cpp \ + gpustatus.cpp \ + iostatus.cpp \ + memorystatus.cpp \ + monitormodel.cpp \ + processreader.cpp \ + processstatus.cpp \ load(qt_module) diff --git a/src/monitor-lib/monitormodel.cpp b/src/monitor-lib/monitormodel.cpp new file mode 100644 index 00000000..55fcd8b3 --- /dev/null +++ b/src/monitor-lib/monitormodel.cpp @@ -0,0 +1,473 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "monitormodel.h" + +#include <QMetaProperty> +#include <qqmlinfo.h> + +#include <QDebug> +#include <QQmlProperty> +#include <QJSValue> +#include <QQmlEngine> + +/*! + \qmltype MonitorModel + \inqmlmodule QtApplicationManager + \ingroup common-instantiatable + \brief A model that can fetch data from various sources and keep a history of their values. + + MonitorModel can fetch data from various sources at regular intervals and keep a history of + their values. Its main use is having it as a model to plot historical data in a graph for + monitoring purposes, such as a CPU usage graph. + + The snippet below shows how to use it for plotting a system's CPU load in a simple bar graph: + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + + ListView { + id: listView + width: 400 + height: 100 + orientation: ListView.Horizontal + spacing: (width / model.count) * 0.2 + clip: true + interactive: false + + model: MonitorModel { + id: monitorModel + running: listView.visible + CpuStatus {} + } + + delegate: Rectangle { + width: (listView.width / monitorModel.count) * 0.8 + height: model.cpuLoad * listView.height + y: listView.height - height + color: "blue" + } + } + \endqml + + To add a data source to MonitorModel just declare it inside the model, as done in the example above with + the CpuStatus component. Alternatively (such as from imperative javscript code) you can add data sources + by assigning them to MonitorModel's dataSources property. + + A data source can be any QtObject with the following characteristics: + \list + \li A \c roleNames property: it's a list of strings naming the roles that this data source provides. Those role + names will be available on each row created by MonitorModel. + \li Properties matching the names provided in the \c roleNames property: MonitorModel will query their values + when building each new model row. + \li An \c update() function: MonitorModel will call it before creating each new model row, so that the data source + can update the values of its properties. + \endlist + + The following snippet shows MonitorModel using a custom data source written in QML: + + \qml + MonitorModel { + running: true + QtObject { + property var roleNames: ["foo", "bar"] + + function update() { + // foo will have ever increasing values + foo += 1; + + // bar will keep oscillating between 0 and 10 + if (up) { + bar += 1; + if (bar == 10) + up = false; + } else { + bar -= 1; + if (bar == 0) + up = true; + } + } + + property int foo: 0 + property int bar: 10 + property bool up: false + } + } + \endqml + + Thus, in the MonitorModel above, every row will have two roles: \c foo and \c bar. If plotted, you would see + an ever incresing foo and an oscillating bar. + + QtApplicationManager comes with a number of components that are readily usable as data sources, namely: + \list + \li CpuStatus + \li FrameTimer + \li GpuStatus + \li IoStatus + \li MemoryStatus + \li ProcessStatus + \endlist + + While \l{MonitorModel::running}{running} is true, MonitorModel will probe its data sources every + \l{MonitorModel::interval}{interval} milliseconds, creating a new row every time up to + \l{MonitorModel::maximumCount}{maximumCount}. Once that value is reached the oldest row (the first one) + is discarded whenever a new row comes in, so that \l{MonitorModel::count}{count} doesn't exceed + \l{MonitorModel::maximumCount}{maximumCount}. New rows are always appended to the model, so rows are + ordered chronologically from oldest (index 0) to newest (index count-1). +*/ + +QT_USE_NAMESPACE_AM + +MonitorModel::MonitorModel(QObject *parent) + : QAbstractListModel(parent) +{ + m_timer.setInterval(1000); + connect(&m_timer, &QTimer::timeout, this, &MonitorModel::readDataSourcesAndAddRow); +} + +MonitorModel::~MonitorModel() +{ + qDeleteAll(m_rows); +} + +/*! + \qmlproperty list<Object> MonitorModel::dataSources + + List of data sources to be used by the MonitorModel. A data source can be any QtObject + containing at least a \c roleNames property and a \c update() function. See MonitorModel's description + for more information. +*/ +QQmlListProperty<QObject> MonitorModel::dataSources() +{ + return QQmlListProperty<QObject>(this, nullptr, &MonitorModel::dataSources_append, + &MonitorModel::dataSources_count, + &MonitorModel::dataSources_at, + &MonitorModel::dataSources_clear); +} + +void MonitorModel::dataSources_append(QQmlListProperty<QObject> *property, QObject *dataSource) +{ + auto *that = static_cast<MonitorModel*>(property->object); + that->appendDataSource(dataSource); +} + +int MonitorModel::dataSources_count(QQmlListProperty<QObject> *property) +{ + auto *that = static_cast<MonitorModel*>(property->object); + return that->m_dataSources.count(); +} + +QObject *MonitorModel::dataSources_at(QQmlListProperty<QObject> *property, int index) +{ + auto *that = static_cast<MonitorModel*>(property->object); + return that && that->m_dataSources.count() > index && index >= 0 ? that->m_dataSources.at(index)->obj : nullptr; +} + +void MonitorModel::dataSources_clear(QQmlListProperty<QObject> *property) +{ + auto *that = static_cast<MonitorModel*>(property->object); + that->clearDataSources(); +} + +void MonitorModel::clearDataSources() +{ + qDeleteAll(m_dataSources); + m_dataSources.clear(); + m_roleNamesList.clear(); + m_roleNameToIndex.clear(); + + clear(); +} + +void MonitorModel::appendDataSource(QObject *dataSourceObj) +{ + DataSource *dataSource = new DataSource; + dataSource->obj = dataSourceObj; + m_dataSources.append(dataSource); + + if (!extractRoleNamesFromJsArray(dataSource) + && !extractRoleNamesFromStringList(dataSource)) + qmlWarning(this) << "Could not find a roleNames property containing an array or list of strings."; +} + +bool MonitorModel::extractRoleNamesFromJsArray(DataSource *dataSource) +{ + QQmlEngine *engine = qmlEngine(this); + + QJSValue jsDataSource = engine->toScriptValue<QObject*>(dataSource->obj); + + if (!jsDataSource.hasProperty(qSL("roleNames"))) + return false; + + QJSValue jsRoleNames = jsDataSource.property(qSL("roleNames")); + if (!jsRoleNames.isArray()) + return false; + + int length = jsRoleNames.property(qSL("length")).toInt(); + for (int i = 0; i < length; i++) + addRoleName(jsRoleNames.property(i).toString().toLatin1(), dataSource); + + return true; +} + +bool MonitorModel::extractRoleNamesFromStringList(DataSource *dataSource) +{ + const QMetaObject *metaObj = dataSource->obj->metaObject(); + + int index = metaObj->indexOfProperty("roleNames"); + if (index == -1) + return false; + + QMetaProperty property = metaObj->property(index); + + QVariant variant = property.read(dataSource->obj); + + if (!variant.canConvert<QStringList>()) + return false; + + QList<QString> roleNames = variant.toStringList(); + for (int i = 0; i < roleNames.count(); i++) + addRoleName(roleNames[i].toLatin1(), dataSource); + + return true; +} + +void MonitorModel::addRoleName(QByteArray roleName, DataSource *dataSource) +{ + dataSource->roleNames.append(roleName); + + if (m_roleNamesList.contains(roleName)) + qmlWarning(this) << "roleName" << roleName << "already exists. Model won't function correctly."; + + m_roleNamesList.append(dataSource->roleNames.last()); + m_roleNameToIndex[dataSource->roleNames.last()] = m_roleNamesList.count() - 1; +} + +/*! + \qmlproperty int MonitorModel::count + \readonly + + Number of rows in the model. It ranges from zero up to \l MonitorModel::maximumCount. + + \sa MonitorModel::maximumCount, MonitorModel::clear +*/ +int MonitorModel::count() const +{ + return m_rows.count(); +} + +int MonitorModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) { + // this model is not a tree + return 0; + } + + return count(); +} + +QVariant MonitorModel::data(const QModelIndex &index, int role) const +{ + if (index.parent().isValid() || !index.isValid() || index.row() < 0 || index.row() >= m_rows.count()) + return QVariant(); + + return m_rows.at(index.row())->dataFromRoleIndex[role]; +} + +QHash<int, QByteArray> MonitorModel::roleNames() const +{ + QHash<int, QByteArray> result; + for (int i = 0; i < m_roleNamesList.count(); i++) { + result[i] = m_roleNamesList.at(i); + } + + return result; +} + +/*! + \qmlproperty bool MonitorModel::running + + While true, MonitorModel will keep probing its data sources and adding new rows every + \l MonitorModel::interval milliseconds. + + Normally you have this property set to true only while the data is being displayed. + + \sa MonitorModel::interval +*/ +bool MonitorModel::running() const +{ + return m_timer.isActive(); +} + +void MonitorModel::setRunning(bool value) +{ + if (value && !m_timer.isActive()) { + m_timer.start(); + emit runningChanged(); + } else if (!value && m_timer.isActive()) { + m_timer.stop(); + emit runningChanged(); + } +} + +/*! + \qmlproperty int MonitorModel::interval + + Interval, in milliseconds, between each row addition while MonitorModel is \l MonitorModel::running. + + \sa MonitorModel::running +*/ +int MonitorModel::interval() const +{ + return m_timer.interval(); +} + +void MonitorModel::setInterval(int value) +{ + if (value != m_timer.interval()) { + m_timer.setInterval(value); + emit intervalChanged(); + } +} + +void MonitorModel::readDataSourcesAndAddRow() +{ + if (m_dataSources.count() == 0) + return; + + if (m_rows.count() < m_maximumCount) { + // create a new row + DataRow *dataRow = new DataRow; + fillDataRow(dataRow); + beginInsertRows(QModelIndex(), /* first */ m_rows.count(), /* last */ m_rows.count()); + m_rows.append(dataRow); + endInsertRows(); + emit countChanged(); + } else { + // recycle the oldest row + beginMoveRows(QModelIndex(), /* sourceFirst */ 0, /* sourceLast */ 0, + QModelIndex(), /* destination */ m_rows.count()); + m_rows.append(m_rows.takeFirst()); + endMoveRows(); + + { + fillDataRow(m_rows.last()); + QModelIndex modelIndex = index(m_rows.count() - 1 /* row */, 0 /* column */); + emit dataChanged(modelIndex, modelIndex); + } + } +} + +void MonitorModel::fillDataRow(DataRow *dataRow) +{ + for (int i = 0; i < m_dataSources.count(); ++i) { + readDataSource(m_dataSources[i], dataRow); + } +} + +void MonitorModel::readDataSource(DataSource *dataSource, DataRow *dataRow) +{ + // TODO: check if successful + QMetaObject::invokeMethod(dataSource->obj, "update", Qt::DirectConnection); + + for (int i = 0; i < dataSource->roleNames.count(); i++) { + // TODO: check index exists + int roleIndex = m_roleNameToIndex[dataSource->roleNames[i]]; + + QVariant variant = QQmlProperty::read(dataSource->obj, QLatin1String(dataSource->roleNames[i])); + dataRow->dataFromRoleIndex[roleIndex] = variant; + } +} + +/*! + \qmlproperty int MonitorModel::maximumCount + + The maximum number of rows that the MonitorModel will keep. After this limit is reached the oldest rows + start to get discarded to make room for the new ones coming in. + + \sa MonitorModel::count, MonitorModel::clear +*/ +int MonitorModel::maximumCount() const +{ + return m_maximumCount; +} + +void MonitorModel::setMaximumCount(int value) +{ + if (m_maximumCount == value) + return; + + m_maximumCount = value; + trimHistory(); + emit maximumCountChanged(); +} + +void MonitorModel::trimHistory() +{ + int excess = m_rows.count() - m_maximumCount; + if (excess <= 0) + return; + + beginRemoveRows(QModelIndex(), /* first */ 0, /* last */ excess - 1); + + while (m_rows.count() > m_maximumCount) + delete m_rows.takeFirst(); + + endRemoveRows(); +} + +/*! + \qmlmethod MonitorModel::clear + + Empties the model, removing all exising rows. + + \sa MonitorModel::count +*/ +void MonitorModel::clear() +{ + beginResetModel(); + qDeleteAll(m_rows); + m_rows.clear(); + endResetModel(); + + emit countChanged(); +} diff --git a/src/monitor-lib/monitormodel.h b/src/monitor-lib/monitormodel.h new file mode 100644 index 00000000..3dbadcf1 --- /dev/null +++ b/src/monitor-lib/monitormodel.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QAbstractListModel> +#include <QtAppManCommon/global.h> +#include <QtQml/qqmllist.h> +#include <QList> +#include <QStringList> +#include <QTimer> + +QT_BEGIN_NAMESPACE_AM + +class MonitorModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QQmlListProperty<QObject> dataSources READ dataSources) + Q_CLASSINFO("DefaultProperty", "dataSources") + + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(int maximumCount READ maximumCount WRITE setMaximumCount NOTIFY maximumCountChanged) + + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) + +public: + MonitorModel(QObject *parent = nullptr); + ~MonitorModel() override; + + QQmlListProperty<QObject> dataSources(); + + static void dataSources_append(QQmlListProperty<QObject> *property, QObject *value); + static int dataSources_count(QQmlListProperty<QObject> *property); + static QObject *dataSources_at(QQmlListProperty<QObject> *property, int index); + static void dataSources_clear(QQmlListProperty<QObject> *property); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + QHash<int, QByteArray> roleNames() const override; + + int count() const; + + bool running() const; + void setRunning(bool value); + + int interval() const; + void setInterval(int value); + + int maximumCount() const; + void setMaximumCount(int value); + + Q_INVOKABLE void clear(); + +signals: + void countChanged(); + void intervalChanged(); + void runningChanged(); + void maximumCountChanged(); + +private slots: + void readDataSourcesAndAddRow(); + +private: + struct DataRow { + QHash<int, QVariant> dataFromRoleIndex; + }; + + struct DataSource { + QObject *obj; + QVector<QByteArray> roleNames; + }; + + void clearDataSources(); + void appendDataSource(QObject *dataSource); + void fillDataRow(DataRow *dataRow); + void readDataSource(DataSource *dataSource, DataRow *dataRow); + void trimHistory(); + bool extractRoleNamesFromJsArray(DataSource *dataSource); + bool extractRoleNamesFromStringList(DataSource *dataSource); + void addRoleName(QByteArray roleName, DataSource *dataSource); + + QList<DataSource*> m_dataSources; + QList<QByteArray> m_roleNamesList; // also maps a role index to its name + QHash<QByteArray, int> m_roleNameToIndex; + + QList<DataRow*> m_rows; + + QTimer m_timer; + int m_maximumCount = 10; +}; + +QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/processmonitor.cpp b/src/monitor-lib/processmonitor.cpp deleted file mode 100644 index bb978d85..00000000 --- a/src/monitor-lib/processmonitor.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:LGPL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: LGPL-3.0 -** -****************************************************************************/ - -#include "processmonitor.h" -#include "processmonitor_p.h" -#include "logging.h" - -#include <QtAppManManager/applicationmanager.h> - - -/*! - \qmltype ProcessMonitor - \inqmlmodule QtApplicationManager.SystemUI - \ingroup system-ui - \brief A type for monitoring process resource usage. - - The ProcessMonitor type provides statistics about the resource usage and performance for a process known to the - application-manager. Currently, CPU load, memory usage and frame rate can be monitored. This type is - available in the System-UI only. - - The ProcessMonitor is dedicated to Linux in particular, since this is currently the only OS - that supports multi-process mode. Other OS's are supported only rudimenatary. - - Here is an example, how the ProcessMonitor can be used: - - \qml - import QtApplicationManager.SystemUI 1.0 - - ProcessMonitor { - applicationId: "" - reportingInterval: 1000 - memoryReportingEnabled: true - - onMemoryReportingChanged: { - console.log("Total PSS: " + (memoryPss.total / 1e6).toFixed(0) + " MB"); - } - } - \endqml - - The type is derived from \c QAbstractListModel, so it can be used directly as a model in an - appropriate view. - - \target role-names - Here is the list of roles that the model provides: - - \table - \header - \li Role name - \li Type - \li Description - \row - \li \c cpuLoad - \target cpuLoad-role - \li real - \li The process's CPU utilization during the last reporting interval. A value of 0 means - that the process was idle, a value of 1 means it fully used the equivalent of one core - (which may be split over several ones). - \row - \li \c memoryVirtual - \target memoryVirtual-role - \li var - \li A map of the process's virtual memory usage. See below for a list of supported keys. - The total amount of virtual memory is provided through \c memoryVirtual.total for - example. - \row - \li \c memoryRss - \target memoryRss-role - \li var - \li A map of the process's RSS (Resident Set Size) memory usage. This is the amount of - memory that is actually mapped to physical RAM. See below for a list of supported - keys. - \row - \li \c memoryPss - \target memoryPss-role - \li var - \li A map of the process's PSS (Proportional Set Size) memory usage. This is the - proportional share of the RSS value above. For instance if two processes share 2 MB - the RSS value will be 2 MB for each process and the PSS value 1 MB for each process. - As the name implies, the code section of shared libraries is generally shared between - processes. Memory may also be shared by other means provided by the OS (e.g. through - \c mmap on Linux). See below for a list of supported keys. - - \row - \li \c frameRate - \target frameRate-role - \li var - \li A list of frame rate measurements where each entry corresponds to a window - and is a map with the following keys: average, maximum, minimum and jitter. - See below for a list of supported keys. - - \sa monitoredWindows - \endtable - - These are the supported keys in the memory maps: - - \table - \header - \li Key - \li Description - \row - \li total - \li The amount of memory used in total in bytes. - \row - \li text - \li The amount of memory used by the code section in bytes. - \row - \li heap - \li The amount of memory used by the heap in bytes. This is private, dynamically allocated - memory (for example through \c malloc or \c mmap on Linux). - \endtable - - These are the supported keys in each entry of the frameRate list: - - \table - \header - \li Key - \li Description - \row - \li average - \li The average frame rate within the reporting interval. - \row - \li maximum - \li The maximum frame rate within the reporting interval. - \row - \li minimum - \li The minimum frame rate within the reporting interval. - \row - \li jitter - \li The jitter within the reporting interval. - \endtable - - \note The model will be updated each \l reportingInterval milliseconds. Note that the roles - will only be populated, if the corresponding reporting parts have been enabled. -*/ - -/*! - \qmlproperty int ProcessMonitor::count - - This property holds the number of reading points that will be kept in the model. The minimum - value that can be set is 2 and the default value is 10. -*/ - -/*! - \qmlproperty int ProcessMonitor::processId - \readonly - - This property holds the OS specific process identifier (PID) that is monitored. This can be - used by external tools for example. The property is 0, if there is no process associated with - the \l applicationId. In particular, if the application-manager runs in single-process mode, - only the System-UI (identified by an empty \l applicationId) will have an associated process. -*/ - -/*! - \qmlproperty string ProcessMonitor::applicationId - - The ID of the application that will be monitored. It must be one of the ID's known to the - application-manager (\l ApplicationManager::applicationIds provides a list of valid IDs). There - is one exception: if the ID is set to an empty string, the System-UI process will be monitored. - Setting a new value will reset the model. -*/ - -/*! - \qmlproperty int ProcessMonitor::reportingInterval - - This property holds the interval in milliseconds between reporting updates. Note, that - reporting will only start once this property is set. Setting a new value will reset the model. - Valid values must be greater than zero. - - At least one of the reporting parts must be enabled to start the reporting. - - \sa cpuLoadReportingEnabled - \sa memoryReportingEnabled -*/ - -/*! - \qmlproperty bool ProcessMonitor::cpuLoadReportingEnabled - - A boolean value that determines whether periodic CPU load reporting is enabled. -*/ - -/*! - \qmlproperty bool ProcessMonitor::memoryReportingEnabled - - A boolean value that determines whether periodic memory reporting is enabled. -*/ - -/*! - \qmlproperty bool ProcessMonitor::frameRateReportingEnabled - - A boolean value that determines whether periodic frame rate reporting is enabled. - - \note In order to receive measurements, the \l {monitoredWindows} property needs to - be set to windows which are going to be monitored. - - \sa monitoredWindows -*/ - -/*! - \qmlproperty var ProcessMonitor::monitoredWindows - - This property holds a list of windows for which frame rate monitoring will be performed. - Mapped windows are advertised through the \l {WindowManager::windowReady()} - signal and the WindowManager type is itself a model which holds all windows of all - application processes. - - \note It is possible to monitor server side (System-UI) views, as well, - if the \l applicationId is empty (hence the System-UI process will be monitored). - - \sa frameRateReportingEnabled -*/ - -/*! - \qmlsignal ProcessMonitor::cpuLoadReportingChanged(real load) - - This signal is emitted periodically when CPU load reporting is enabled. The frequency is - defined by \l reportingInterval. The \a load parameter indicates the CPU utilization. Details - can be found in the description of the \l {cpuLoad-role} {cpuLoad} model role above. - - \sa cpuLoadReportingEnabled - \sa reportingInterval -*/ - -/*! - \qmlsignal ProcessMonitor::memoryReportingChanged(var memoryVritual, var memoryRss - , var memoryPss); - - This signal is emitted periodically when memory reporting is enabled. The frequency is defined - by \l reportingInterval. The parameters provide the same information as the model roles with - the same name described \l {memoryVirtual-role}{above}. - - \sa memoryReportingEnabled - \sa reportingInterval -*/ - -/*! - \qmlsignal ProcessMonitor::frameRateReportingChanged(var frameRate); - - This signal is emitted periodically when frame rate reporting is enabled and \l monitoredWindows - is set. The frequency is defined by \l reportingInterval. The \a frameRate parameter provides - the same information as described \l {frameRate-role}{above}. - - \sa frameRateReportingEnabled - \sa reportingInterval -*/ - - -QT_BEGIN_NAMESPACE_AM - -ProcessMonitor::ProcessMonitor(QObject *parent) - : QAbstractListModel(parent) - , d_ptr(new ProcessMonitorPrivate(this)) -{ - Q_D(ProcessMonitor); - - d->roles.insert(MemVirtual, "memoryVirtual"); - d->roles.insert(MemRss, "memoryRss"); - d->roles.insert(MemPss, "memoryPss"); - d->roles.insert(CpuLoad, "cpuLoad"); - d->roles.insert(FrameRate, "frameRate"); - - d->resetModel(); -} - -ProcessMonitor::~ProcessMonitor() -{ - Q_D(ProcessMonitor); - - delete d; -} - -int ProcessMonitor::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent) - Q_D(const ProcessMonitor); - - return d->modelData.size(); -} - -void ProcessMonitor::setCount(int count) -{ - Q_D(ProcessMonitor); - - if (count != d->count) { - count = qMax(2, count); - d->updateModelCount(count); - emit d->newCount(count); - emit countChanged(count); - } -} - -int ProcessMonitor::count() const -{ - Q_D(const ProcessMonitor); - - return d->count; -} - -qint64 ProcessMonitor::processId() const -{ - Q_D(const ProcessMonitor); - - return d->pid; -} - - -QString ProcessMonitor::applicationId() const -{ - Q_D(const ProcessMonitor); - - return d->appId; -} - -void ProcessMonitor::setApplicationId(const QString &appId) -{ - Q_D(ProcessMonitor); - - if (d->appId != appId || d->appId.isNull()) { - d->appId = appId; - d->resetModel(); - d->determinePid(); - d->setupFrameRateMonitoring(); - if (!appId.isEmpty() && ApplicationManager::instance()->indexOfApplication(appId) < 0) - qCWarning(LogSystem) << "ProcessMonitor: invalid application ID:" << appId; - emit applicationIdChanged(appId); - } -} - -void ProcessMonitor::setReportingInterval(int intervalInMSec) -{ - Q_D(ProcessMonitor); - - if (d->reportingInterval != intervalInMSec && intervalInMSec > 0) { - d->setupInterval(intervalInMSec); - d->resetModel(); - emit reportingIntervalChanged(intervalInMSec); - } -} - -int ProcessMonitor::reportingInterval() const -{ - Q_D(const ProcessMonitor); - - return d->reportingInterval; -} - -bool ProcessMonitor::isMemoryReportingEnabled() const -{ - Q_D(const ProcessMonitor); - - return d->reportMemory; -} - -void ProcessMonitor::setMemoryReportingEnabled(bool enabled) -{ - Q_D(ProcessMonitor); - - if (enabled != d->reportMemory) { - d->reportMemory = enabled; - d->memTail = enabled ? 0 : d->count; - d->setupInterval(); - emit d->newReadMem(enabled); - emit memoryReportingEnabledChanged(); - } -} - -bool ProcessMonitor::isCpuLoadReportingEnabled() const -{ - Q_D(const ProcessMonitor); - return d->reportCpu; -} - -void ProcessMonitor::setCpuLoadReportingEnabled(bool enabled) -{ - Q_D(ProcessMonitor); - - if (enabled != d->reportCpu) { - d->reportCpu = enabled; - d->cpuTail = enabled ? 0: d->count; - d->setupInterval(); - emit d->newReadCpu(enabled); - emit cpuLoadReportingEnabledChanged(); - } -} - -bool ProcessMonitor::isFrameRateReportingEnabled() const -{ - Q_D(const ProcessMonitor); - return d->reportFps; -} - -void ProcessMonitor::setFrameRateReportingEnabled(bool enabled) -{ - Q_D(ProcessMonitor); - - if (enabled != d->reportFps) { - d->reportFps = enabled; - d->fpsTail = enabled ? 0 : d->count; - d->setupFrameRateMonitoring(); - emit frameRateReportingEnabledChanged(); - } -} - -QVariant ProcessMonitor::data(const QModelIndex &index, int role) const -{ - Q_D(const ProcessMonitor); - - if (!index.isValid() || index.row() < 0 || index.row() >= d->modelData.size()) - return QVariant(); - - const ProcessMonitorPrivate::ModelData &reading = d->modelDataForRow(index.row()); - - switch (role) { - case MemVirtual: - return reading.vm; - case MemRss: - return reading.rss; - case MemPss: - return reading.pss; - case CpuLoad: - return reading.cpuLoad; - case FrameRate: - return reading.frameRate; - default: - return QVariant(); - } -} - -QHash<int, QByteArray> ProcessMonitor::roleNames() const -{ - Q_D(const ProcessMonitor); - - return d->roles; -} - -/*! - \qmlmethod object ProcessMonitor::get(int index) - - Returns the model data for the reading point identified by \a index as a JavaScript object. - See the \l {role-names} for the expected object elements. The \a index must be in the range - [0, \l count), returns an empty object if it is invalid. -*/ -QVariantMap ProcessMonitor::get(int row) const -{ - if (row < 0 || row >= count()) { - qCWarning(LogSystem) << "ProcessMonitor: invalid row:" << row; - return QVariantMap(); - } - - QVariantMap map; - QHash<int, QByteArray> roles = roleNames(); - for (auto it = roles.cbegin(); it != roles.cend(); ++it) { - map.insert(qL1S(it.value()), data(index(row), it.key())); - } - - return map; -} - -QList<QObject *> ProcessMonitor::monitoredWindows() const -{ - Q_D(const ProcessMonitor); - - return d->monitoredWindows(); -} - -void ProcessMonitor::setMonitoredWindows(QList<QObject *> windows) -{ - Q_D(ProcessMonitor); - - d->updateMonitoredWindows(windows); -} - -qreal ProcessMonitor::cpuLoad() const -{ - Q_D(const ProcessMonitor); - return d->modelData[d->latestReportPos()].cpuLoad; -} - -/*! - \qmlproperty QVariantMap ProcessMonitor::memoryVirtual - - A map of the process's virtual memory usage. - \sa memoryVirtual-role -*/ -QVariantMap ProcessMonitor::memoryVirtual() const -{ - Q_D(const ProcessMonitor); - return d->modelData[d->latestReportPos()].vm; -} - -/*! - \qmlproperty QVariantMap ProcessMonitor::memoryRss - - A map of the process's RSS (Resident Set Size) memory usage. - \sa memoryRss-role -*/ -QVariantMap ProcessMonitor::memoryRss() const -{ - Q_D(const ProcessMonitor); - return d->modelData[d->latestReportPos()].rss; -} - -/*! - \qmlproperty QVariantMap ProcessMonitor::memoryPss - - A map of the process's PSS (Proportional Set Size) memory usage. - \sa memoryPss-role -*/ -QVariantMap ProcessMonitor::memoryPss() const -{ - Q_D(const ProcessMonitor); - return d->modelData[d->latestReportPos()].pss; -} - -QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/processmonitor_p.cpp b/src/monitor-lib/processmonitor_p.cpp deleted file mode 100644 index 0f60b2cf..00000000 --- a/src/monitor-lib/processmonitor_p.cpp +++ /dev/null @@ -1,747 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:LGPL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: LGPL-3.0 -** -****************************************************************************/ - -#include <QCoreApplication> -#include <QtAppManWindow/windowmanager.h> -#include "logging.h" -#include "applicationmanager.h" -#include "abstractruntime.h" -#include "processmonitor_p.h" - -#if defined(Q_OS_MACOS) -# include <mach/mach.h> -#elif defined(Q_OS_LINUX) -# include <unistd.h> -#endif - - -QT_BEGIN_NAMESPACE_AM - - -ReadingTask::ReadingTask(QMutex &mutex, ReadingTask::Results &res) - : m_mutex(mutex) - , m_results(res) -{} - -void ReadingTask::cancelTimer() -{ - if (m_reportingTimerId) { - killTimer(m_reportingTimerId); - m_reportingTimerId = 0; - } -} - -void ReadingTask::setupTimer(bool enabled, int interval) -{ - if (interval != -1) - m_reportingInterval = interval; - - if (!enabled) { - cancelTimer(); - } else { - if (interval != -1) - cancelTimer(); - - if (!m_reportingTimerId && m_reportingInterval >= 0) - m_reportingTimerId = startTimer(m_reportingInterval); - } -} - -void ReadingTask::setNewPid(qint64 pid) -{ - m_pid = pid; - if (pid) { - openLoad(); - readLoad(); - } -} - -void ReadingTask::setNewReadCpu(bool enabled) -{ - m_readCpu = enabled; - if (enabled) - readLoad(); -} - -void ReadingTask::setNewReadMem(bool enabled) -{ - m_readMem = enabled; -} - -void ReadingTask::reset(int sync) -{ - m_sync = sync; -} - -void ReadingTask::timerEvent(QTimerEvent *event) -{ - if (event && event->timerId() == m_reportingTimerId && m_pid) { - ReadingTask::Results results; - - if (m_readMem) { - const QByteArray file = "/proc/" + QByteArray::number(m_pid) + "/smaps"; - if (!readMemory(file, results.memory)) - results.memory = ReadingTask::Results::Memory(); - results.memory.read = true; - } - - if (m_readCpu) { - results.cpu.load = readLoad(); - results.cpu.read = true; - } - - results.sync = m_sync; - - m_mutex.lock(); - m_results = results; - m_mutex.unlock(); - - emit newReadingAvailable(); - } -} - - -#if defined(Q_OS_LINUX) - -void ReadingTask::openLoad() -{ - const QByteArray fileName = "/proc/" + QByteArray::number(m_pid) + "/stat"; - m_statReader.reset(new SysFsReader(fileName)); - if (!m_statReader->isOpen()) - qCWarning(LogSystem) << "Cannot read CPU load from" << fileName; -} - -qreal ReadingTask::readLoad() -{ - qint64 elapsed; - if (m_elapsedTime.isValid()) { - elapsed = m_elapsedTime.restart(); - } else { - elapsed = 0; - m_elapsedTime.start(); - } - - if (m_statReader.isNull() || !m_statReader->isOpen()) { - m_lastCpuUsage = 0.0; - return 0.0; - } - - QByteArray str = m_statReader->readValue(); - int pos = 0; - int blanks = 0; - while (pos < str.size() && blanks < 13) { - if (isblank(str.at(pos))) - ++blanks; - ++pos; - } - - char *endPtr = nullptr; - quint64 utime = strtoull(str.constData() + pos, &endPtr, 10); // check missing for overflow - pos = int(endPtr - str.constData() + 1); - quint64 stime = strtoull(str.constData() + pos, nullptr, 10); // check missing for overflow - - qreal load = elapsed != 0 ? (utime + stime - m_lastCpuUsage) * 1000.0 / sysconf(_SC_CLK_TCK) / elapsed : 0.0; - m_lastCpuUsage = utime + stime; - return load; -} - -static uint parseValue(const char *pl) -{ - while (*pl && (*pl < '0' || *pl > '9')) - pl++; - return static_cast<uint>(strtoul(pl, nullptr, 10)); -} - -bool ReadingTask::readMemory(const QByteArray &smapsFile, ReadingTask::Results::Memory &results) -{ - struct ScopedFile { - ~ScopedFile() { if (file) fclose(file); } - FILE *file = nullptr; - }; - - ScopedFile sf; - sf.file = fopen(smapsFile.constData(), "r"); - - if (sf.file == nullptr) - return false; - - const int lineLen = 100; // we are not interested in full library paths - char line[lineLen + 5]; // padding for highly unlikely trailing perm flags below - char *pl; // pointer to chars within line - bool ok = true; - - if (fgets(line, lineLen, sf.file) == nullptr) - return false; - - // sanity checks - pl = line; - for (pl = line; pl < (line + 4) && ok; ++pl) - ok = ((*pl >= '0' && *pl <= '9') || (*pl >= 'a' && *pl <= 'f')); - while (strlen(line) == lineLen - 1 && line[lineLen - 2] != '\n') { - if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) - break; - } - if (fgets(line, lineLen, sf.file) == nullptr) - return false; - static const char strSize[] = "Size: "; - ok = ok && !qstrncmp(line, strSize, sizeof(strSize) - 1); - if (!ok) - return false; - - // Determine block size - ok = false; - int blockLen = 0; - while (fgets(line, lineLen, sf.file) != nullptr && !ok) { - if (!(line[0] < '0' || line[0] > '9') && (line[0] < 'a' || line[0] > 'f')) - ok = true; - ++blockLen; - } - if (!ok || blockLen < 12 || blockLen > 32) - return false; - - fseek(sf.file, 0, SEEK_SET); - bool wasPrivateOnly = false; - ok = false; - - while (true) { - if (Q_UNLIKELY(!(fgets(line, lineLen, sf.file) != nullptr))) { - ok = feof(sf.file); - break; - } - - // Determine permission flags - pl = line; - while (*pl && *pl != ' ') - ++pl; - char permissions[4]; - memcpy(permissions, ++pl, sizeof(permissions)); - - // Determine inode - int spaceCount = 0; - while (*pl && spaceCount < 3) { - if (*pl == ' ') - ++spaceCount; - ++pl; - } - bool hasInode = (*pl != '0'); - - // Determine library name - while (*pl && *pl != ' ') - ++pl; - while (*pl && *pl == ' ') - ++pl; - - static const char strStack[] = "stack]"; - bool isMainStack = (Q_UNLIKELY(*pl == '[' - && !qstrncmp(pl + 1, strStack, sizeof(strStack) - 1))); - // Skip rest of library path - while (strlen(line) == lineLen - 1 && line[lineLen - 2] != '\n') { - if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) - break; - } - - int skipLen = blockLen; - uint vm = 0; - uint rss = 0; - uint pss = 0; - const int sizeTag = 0x01; - const int rssTag = 0x02; - const int pssTag = 0x04; - const int allTags = sizeTag | rssTag | pssTag; - int foundTags = 0; - - while (foundTags < allTags && skipLen > 0) { - skipLen--; - if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) - break; - pl = line; - - static const char strSize[] = "ize:"; - static const char strXss[] = "ss:"; - - switch (*pl) { - case 'S': - if (!qstrncmp(pl + 1, strSize, sizeof(strSize) - 1)) { - foundTags |= sizeTag; - vm = parseValue(pl + sizeof(strSize)); - } - break; - case 'R': - if (!qstrncmp(pl + 1, strXss, sizeof(strXss) - 1)) { - foundTags |= rssTag; - rss = parseValue(pl + sizeof(strXss)); - } - break; - case 'P': - if (!qstrncmp(pl + 1, strXss, sizeof(strXss) - 1)) { - foundTags |= pssTag; - pss = parseValue(pl + sizeof(strXss)); - } - break; - } - } - - if (foundTags < allTags) - break; - - results.totalVm += vm; - results.totalRss += rss; - results.totalPss += pss; - - static const char permRXP[] = { 'r', '-', 'x', 'p' }; - static const char permRWP[] = { 'r', 'w', '-', 'p' }; - if (!memcmp(permissions, permRXP, sizeof(permissions))) { - results.textVm += vm; - results.textRss += rss; - results.textPss += pss; - } else if (!memcmp(permissions, permRWP, sizeof(permissions)) - && !isMainStack && (vm != 8192 || hasInode || !wasPrivateOnly) // try to exclude stack - && !hasInode) { - results.heapVm += vm; - results.heapRss += rss; - results.heapPss += pss; - } - - static const char permP[] = { '-', '-', '-', 'p' }; - wasPrivateOnly = !memcmp(permissions, permP, sizeof(permissions)); - - for (int skip = skipLen; skip; --skip) { - if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) - break; - } - } - - return ok; -} - -#elif defined(Q_OS_MACOS) - -void ReadingTask::openLoad() -{ -} - -qreal ReadingTask::readLoad() -{ - return 0.0; -} - -bool ReadingTask::readMemory(const QByteArray &smapsFile, ReadingTask::Results::Memory &results) -{ - Q_UNUSED(smapsFile) - struct task_basic_info t_info; - mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; - - if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count)) { - qCWarning(LogSystem) << "Could not read memory data"; - return false; - } - - results.totalRss = t_info.resident_size; - results.totalVm = t_info.virtual_size; - - return true; -} - -#else - -void ReadingTask::openLoad() -{ -} - -qreal ReadingTask::readLoad() -{ - return 0.0; -} - -bool ReadingTask::readMemory(const QByteArray &smapsFile, ReadingTask::Results::Memory &results) -{ - Q_UNUSED(smapsFile) - Q_UNUSED(results) - return false; -} - -#endif - - -ProcessMonitorPrivate::ProcessMonitorPrivate(ProcessMonitor *q) - : q_ptr(q) -{ - connect(ApplicationManager::instance(), &ApplicationManager::applicationRunStateChanged, - this, &ProcessMonitorPrivate::appRuntimeChanged); - - readingTask = new ReadingTask(mutex, readResults); - readingTask->moveToThread(&thread); - connect(this, &ProcessMonitorPrivate::newPid, readingTask, &ReadingTask::setNewPid); - connect(this, &ProcessMonitorPrivate::setupTimer, readingTask, &ReadingTask::setupTimer); - connect(this, &ProcessMonitorPrivate::newReadCpu, readingTask, &ReadingTask::setNewReadCpu); - connect(this, &ProcessMonitorPrivate::newReadMem, readingTask, &ReadingTask::setNewReadMem); - connect(this, &ProcessMonitorPrivate::reset, readingTask, &ReadingTask::reset); - connect(readingTask, &ReadingTask::newReadingAvailable, this, &ProcessMonitorPrivate::readingUpdate); - connect(&thread, &QThread::finished, readingTask, &ReadingTask::deleteLater); - thread.start(); -} - -ProcessMonitorPrivate::~ProcessMonitorPrivate() -{ - thread.quit(); - thread.wait(); -} - -void ProcessMonitorPrivate::appRuntimeChanged(const QString &id, Am::RunState state) -{ - if (id == appId && (state == Am::Running || state == Am::NotRunning)) - determinePid(); -} - -void ProcessMonitorPrivate::setupInterval(int interval) -{ - bool shouldBeOn = pid && (reportCpu || reportMemory || reportFps || cpuTail || memTail || fpsTail); - emit setupTimer(shouldBeOn, interval); -} - -void ProcessMonitorPrivate::determinePid() -{ - Q_Q(ProcessMonitor); - - qint64 newId; - if (appId.isEmpty()) { - newId = QCoreApplication::applicationPid(); - } else { - if (ApplicationManager::instance()->isSingleProcess()) { - newId = 0; - } else { - AbstractApplication *app = ApplicationManager::instance()->fromId(appId); - newId = (app && app->currentRuntime()) ? app->currentRuntime()->applicationProcessId() : 0; - } - } - - if (newId != pid) { - pid = newId; - setupInterval(); - emit newPid(pid); - emit q->processIdChanged(pid); - } -} - -const ProcessMonitorPrivate::ModelData &ProcessMonitorPrivate::modelDataForRow(int row) const -{ - int pos = reportPos - row - 1; - if (pos < 0) - pos += modelData.size(); - if (pos < 0 || pos >= modelData.size()) - return modelData.first(); - return modelData.at(pos); -} - - -void ProcessMonitorPrivate::resetModel() -{ - Q_Q(ProcessMonitor); - - q->beginResetModel(); - modelData.clear(); - modelData.resize(count); - reportPos = 0; - q->endResetModel(); - - cpuTail = memTail = fpsTail = 0; - - emit reset(++sync); -} - -void ProcessMonitorPrivate::updateModelCount(int newCount) -{ - Q_Q(ProcessMonitor); - - int diff = newCount - count; - q->beginResetModel(); - if (diff > 0) { - modelData.insert(reportPos, diff, ModelData()); - } else { - if (reportPos <= newCount) { - modelData.remove(reportPos, -diff); - if (reportPos == newCount) - reportPos = 0; - } else { - modelData.remove(reportPos, count - reportPos); - modelData.remove(0, reportPos - newCount); - reportPos = 0; - } - } - count = newCount; - q->endResetModel(); - - if (cpuTail > 0) - cpuTail += diff; - if (memTail > 0) - memTail += diff; - if (fpsTail > 0) - fpsTail += diff; -} - -void ProcessMonitorPrivate::readingUpdate() -{ - Q_Q(ProcessMonitor); - - QVector<int> roles; - ModelData data; - ReadingTask::Results results; - - mutex.lock(); - results = readResults; - mutex.unlock(); - - if (sync != results.sync) - return; - - if (cpuTail > 0) { - if (--cpuTail == 0) - setupInterval(); - roles.append(CpuLoad); - } else if (results.cpu.read) { - data.cpuLoad = results.cpu.load; - roles.append(CpuLoad); - } - - if (results.memory.read || memTail > 0) { - if (memTail > 0) { - if (--memTail == 0) - setupInterval(); - results.memory = ReadingTask::Results::Memory(); - } - - // Although smaps claims to report kB it's actually KiB (2^10 = 1024 Bytes) - data.vm.insert(qSL("total"), quint64(results.memory.totalVm) << 10); - data.vm.insert(qSL("text"), quint64(results.memory.textRss) << 10); - data.vm.insert(qSL("heap"), quint64(results.memory.heapPss) << 10); - data.rss.insert(qSL("total"), quint64(results.memory.totalRss) << 10); - data.rss.insert(qSL("text"), quint64(results.memory.textRss) << 10); - data.rss.insert(qSL("heap"), quint64(results.memory.heapRss) << 10); - data.pss.insert(qSL("total"), quint64(results.memory.totalPss) << 10); - data.pss.insert(qSL("text"), quint64(results.memory.textPss) << 10); - data.pss.insert(qSL("heap"), quint64(results.memory.heapPss) << 10); - - roles.append({ MemVirtual, MemRss, MemPss }); - } - - if (reportFps || fpsTail > 0) { - if (fpsTail > 0 && --fpsTail == 0) - setupInterval(); - - for (auto it = frameCounters.begin(); it != frameCounters.end(); ++it) { - QVariantMap frameMap; - FrameTimer *frameTimer = frameCounters.value(it.key()); - frameMap.insert(qSL("average"), frameTimer->averageFps()); - frameMap.insert(qSL("maximum"), frameTimer->maximumFps()); - frameMap.insert(qSL("minimum"), frameTimer->minimumFps()); - frameMap.insert(qSL("jitter"), frameTimer->jitterFps()); - frameTimer->reset(); - data.frameRate.append(frameMap); - } - - emit q->frameRateReportingChanged(data.frameRate); - - roles.append(FrameRate); - } - - int last = modelData.size() - 1; - q->beginMoveRows(QModelIndex(), last, last, QModelIndex(), 0); - modelData[reportPos++] = data; - if (reportPos > last) - reportPos = 0; - q->endMoveRows(); - q->dataChanged(q->index(0), q->index(0), roles); - - if (reportCpu) - emit q->cpuLoadReportingChanged(data.cpuLoad); - - if (results.memory.read || memTail > 0) - emit q->memoryReportingChanged(data.vm, data.rss, data.pss); -} - -void ProcessMonitorPrivate::setupFrameRateMonitoring() -{ - resetFrameRateMonitoring(); -#if !defined(AM_HEADLESS) - if (reportFps) { - if (ApplicationManager::instance()->isSingleProcess() || appId.isEmpty()) { - - for (auto it = frameCounters.begin(); it != frameCounters.end(); ++it) { - QQuickWindow *win = qobject_cast<QQuickWindow *>(it.key()); - if (win) { - connections << connect(win, &QQuickWindow::frameSwapped, - this, &ProcessMonitorPrivate::frameUpdated); - } - } - } else { -# if defined(AM_MULTI_PROCESS) - - for (auto it = frameCounters.begin(); it != frameCounters.end(); ++it) { - WaylandWindow *win = qobject_cast<WaylandWindow *>(it.key()); - if (win) { - // Check if this is valid window for this application - if (win->application() && win->application()->id() == appId) { - connections << connect(win, &WaylandWindow::frameUpdated, - this, &ProcessMonitorPrivate::frameUpdated); - } else { - qCWarning(LogSystem) << "Windows do not belong to this app"; - return; - } - } - } - - connect(WindowManager::instance(), &WindowManager::windowContentStateChanged, - this, &ProcessMonitorPrivate::onWindowContentStateChanged); -# endif - } - } else { -# if defined(AM_MULTI_PROCESS) - disconnect(WindowManager::instance(), &WindowManager::windowContentStateChanged, - this, &ProcessMonitorPrivate::onWindowContentStateChanged); -# endif - } -#endif // !AM_HEADLESS -} - -void ProcessMonitorPrivate::updateMonitoredWindows(const QList<QObject *> &windowObjs) -{ -#if !defined(AM_HEADLESS) - Q_Q(ProcessMonitor); - - clearMonitoredWindows(); - - for (auto windowObj : windowObjs) { - if (ApplicationManager::instance()->isSingleProcess() || appId.isEmpty()) { - QQuickWindow *view = qobject_cast<QQuickWindow *>(windowObj); - if (view) { - FrameTimer *frameTimer = new FrameTimer(); - frameCounters.insert(view, frameTimer); - } else - qCWarning(LogSystem) << "In single process only QQuickWindow can be monitored"; - } else { -# if defined(AM_MULTI_PROCESS) - auto window = qobject_cast<Window*>(windowObj); - if (window && window->application()->nonAliased()->id() == appId) { - FrameTimer *frameTimer = new FrameTimer(); - frameCounters.insert(window, frameTimer); - } else - continue; // TODO: Complain in a warning message -# endif - } - } - - emit q->monitoredWindowsChanged(); - setupFrameRateMonitoring(); -#else - Q_UNUSED(windows) -#endif -} - -QList<QObject *> ProcessMonitorPrivate::monitoredWindows() const -{ - QList<QObject *> list; - -#if !defined(AM_HEADLESS) - if (ApplicationManager::instance()->isSingleProcess() || appId.isEmpty()) { - for (auto it = frameCounters.begin(); it != frameCounters.end(); ++it) { - list.append(it.key()); - } - - return list; - } - -# if defined(AM_MULTI_PROCESS) - // Return window items - for (auto it = frameCounters.begin(); it != frameCounters.end(); ++it) { - WaylandWindow *win = qobject_cast<WaylandWindow *>(it.key()); - if (win) - list.append(win); - } -# endif -#endif - return list; -} - -#if defined(AM_MULTI_PROCESS) && !defined(AM_HEADLESS) - -void ProcessMonitorPrivate::onWindowContentStateChanged(Window *window) -{ - if (window->contentState() == Window::SurfaceWithContent) - return; - - for (auto it = frameCounters.cbegin(); it != frameCounters.cend(); ++it) { - auto win = qobject_cast<Window *>(it.key()); - if (win && win == window) { - frameCounters.remove(win); - break; - } - } -} - -#endif - -void ProcessMonitorPrivate::frameUpdated() -{ - FrameTimer *frameTimer = frameCounters.value(sender()); - if (!frameTimer) { - frameTimer = new FrameTimer(); - frameCounters.insert(sender(), frameTimer); - } - - frameTimer->newFrame(); -} - -void ProcessMonitorPrivate::resetFrameRateMonitoring() -{ - for (auto connection : connections) { - QObject::disconnect(connection); - } - - connections.clear(); -} - -void ProcessMonitorPrivate::clearMonitoredWindows() -{ - qDeleteAll(frameCounters); - frameCounters.clear(); -} - -QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/processmonitor_p.h b/src/monitor-lib/processmonitor_p.h deleted file mode 100644 index 6be04102..00000000 --- a/src/monitor-lib/processmonitor_p.h +++ /dev/null @@ -1,240 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:LGPL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: LGPL-3.0 -** -****************************************************************************/ - -#pragma once - -// -// W A R N I N G -// ------------- -// -// This file is not part of the application manager API. It exists purely as an -// implementation detail. This header file may change from version to version -// without notice, or even be removed. -// -// We mean it. -// - -#include <QObject> -#include <QElapsedTimer> -#include <QThread> -#include <QMutex> -#include <QString> -#include <QHash> -#include <QVector> -#if defined(Q_OS_LINUX) -# include <QScopedPointer> -# include "sysfsreader.h" -#endif -#if !defined(AM_HEADLESS) -# include <QQuickWindow> -# if defined(AM_MULTI_PROCESS) -# include <QtAppManWindow/waylandwindow.h> -# endif -#endif -#include "processmonitor.h" -#include "frametimer.h" -#include "applicationmanager.h" - -QT_BEGIN_NAMESPACE_AM - -class Window; - -class ReadingTask : public QObject -{ - Q_OBJECT - -public: - struct Results { - int sync; - - struct Memory { - bool read = false; - quint32 totalVm = 0; - quint32 totalRss = 0; - quint32 totalPss = 0; - quint32 textVm = 0; - quint32 textRss = 0; - quint32 textPss = 0; - quint32 heapVm = 0; - quint32 heapRss = 0; - quint32 heapPss = 0; - } memory; - - struct Cpu { - bool read = false; - qreal load = 0; - } cpu; - }; - - ReadingTask(QMutex &mutex, Results &res); - -protected: - void timerEvent(QTimerEvent *event) override; - bool readMemory(const QByteArray &smapsFile, Results::Memory &results); - -public slots: - void setupTimer(bool enabled, int interval); - void setNewPid(qint64 pid); - void setNewReadMem(bool enabled); - void setNewReadCpu(bool enabled); - void reset(int sync); - -signals: - void newReadingAvailable(); - -private: - void cancelTimer(); - void openLoad(); - qreal readLoad(); - - QMutex &m_mutex; - Results &m_results; - int m_sync = 0; - -#if defined(Q_OS_LINUX) - QScopedPointer<SysFsReader> m_statReader; -#endif - QElapsedTimer m_elapsedTime; - quint64 m_lastCpuUsage; - - qint64 m_pid; - - bool m_readCpu = false; - bool m_readMem = false; - - int m_reportingInterval = -1; - int m_reportingTimerId = 0; -}; - - -namespace { -enum Roles { - MemVirtual = Qt::UserRole, - MemRss, - MemPss, - CpuLoad, - FrameRate -}; -} - -class ProcessMonitorPrivate : public QObject -{ - Q_OBJECT -public: - ProcessMonitor *q_ptr; - Q_DECLARE_PUBLIC(ProcessMonitor) - - struct ModelData { - QVariantMap vm; - QVariantMap rss; - QVariantMap pss; - qreal cpuLoad = 0; - QVariantList frameRate; - }; - - QString appId; - qint64 pid = 0; - - int reportingInterval = -1; - int count = 10; - - int reportPos = 0; - - bool reportMemory = false; - bool reportCpu = false; - bool reportFps = false; - int cpuTail = 0; - int memTail = 0; - int fpsTail = 0; - - QHash<int, QByteArray> roles; - QVector<ModelData> modelData; - - // fps - QMap<QObject *, FrameTimer *> frameCounters; - - ReadingTask *readingTask; - ReadingTask::Results readResults; - QThread thread; - QMutex mutex; - int sync = 0; - - QList<QMetaObject::Connection> connections; - - ProcessMonitorPrivate(ProcessMonitor *q); - ~ProcessMonitorPrivate(); - - void setupInterval(int interval = -1); - void determinePid(); - const ModelData &modelDataForRow(int row) const; - void resetModel(); - void updateModelCount(int newCount); - void setupFrameRateMonitoring(); - void updateMonitoredWindows(const QList<QObject *> &windows); - QList<QObject *> monitoredWindows() const; - void resetFrameRateMonitoring(); - void clearMonitoredWindows(); - - int latestReportPos() const { - // show the one before reportPos, wrapping around if reportPos - // is already the very first index. - return reportPos == 0 ? modelData.size() - 1 : reportPos - 1; - } - -signals: - void setupTimer(bool enabled, int interval); - void newPid(qint64 pid); - void newReadMem(bool enabled); - void newReadCpu(bool enabled); - void newCount(int count); - void reset(int sync); - -public slots: - void readingUpdate(); - void appRuntimeChanged(const QString &id, Am::RunState state); -#if defined(AM_MULTI_PROCESS) && !defined(AM_HEADLESS) - void onWindowContentStateChanged(Window *window); -#endif - void frameUpdated(); -}; - - -QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/processreader.cpp b/src/monitor-lib/processreader.cpp new file mode 100644 index 00000000..fc73d000 --- /dev/null +++ b/src/monitor-lib/processreader.cpp @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "processreader.h" + +#include "logging.h" + +#if defined(Q_OS_MACOS) +# include <mach/mach.h> +#elif defined(Q_OS_LINUX) +# include <unistd.h> +#endif + +namespace { + static uint parseValue(const char *pl) { + while (*pl && (*pl < '0' || *pl > '9')) + pl++; + return static_cast<uint>(strtoul(pl, nullptr, 10)); + } +} + +QT_USE_NAMESPACE_AM + +void ProcessReader::setProcessId(qint64 pid) +{ + m_pid = pid; + if (pid) + openCpuLoad(); +} + +void ProcessReader::update() +{ + // read cpu + { + qreal cpuLoadFloat = readCpuLoad(); + quint32 value = ((qreal)std::numeric_limits<quint32>::max()) * cpuLoadFloat; + cpuLoad.store(value); + } + + { + if (!readMemory()) { + totalVm.store(0); + totalRss.store(0); + totalPss.store(0); + textVm.store(0); + textRss.store(0); + textPss.store(0); + heapVm.store(0); + heapRss.store(0); + heapPss.store(0); + } + } + + emit updated(); +} + +#if defined(Q_OS_LINUX) + +void ProcessReader::openCpuLoad() +{ + const QByteArray fileName = "/proc/" + QByteArray::number(m_pid) + "/stat"; + m_statReader.reset(new SysFsReader(fileName)); + if (!m_statReader->isOpen()) + qCWarning(LogSystem) << "Cannot read CPU load from" << fileName; +} + +qreal ProcessReader::readCpuLoad() +{ + qint64 elapsed; + if (m_elapsedTime.isValid()) { + elapsed = m_elapsedTime.restart(); + } else { + elapsed = 0; + m_elapsedTime.start(); + } + + if (m_statReader.isNull() || !m_statReader->isOpen()) { + m_lastCpuUsage = 0.0; + return 0.0; + } + + QByteArray str = m_statReader->readValue(); + int pos = 0; + int blanks = 0; + while (pos < str.size() && blanks < 13) { + if (isblank(str.at(pos))) + ++blanks; + ++pos; + } + + char *endPtr = nullptr; + quint64 utime = strtoull(str.constData() + pos, &endPtr, 10); // check missing for overflow + pos = int(endPtr - str.constData() + 1); + quint64 stime = strtoull(str.constData() + pos, nullptr, 10); // check missing for overflow + + qreal load = elapsed != 0 ? (utime + stime - m_lastCpuUsage) * 1000.0 / sysconf(_SC_CLK_TCK) / elapsed : 0.0; + m_lastCpuUsage = utime + stime; + return load; +} + + +bool ProcessReader::readMemory() +{ + QByteArray smapsFile = "/proc/" + QByteArray::number(m_pid) + "/smaps"; + return readSmaps(smapsFile); +} + +bool ProcessReader::readSmaps(const QByteArray &smapsFile) +{ + quint32 _totalVm = 0; + quint32 _totalRss = 0; + quint32 _totalPss = 0; + quint32 _textVm = 0; + quint32 _textRss = 0; + quint32 _textPss = 0; + quint32 _heapVm = 0; + quint32 _heapRss = 0; + quint32 _heapPss = 0; + + struct ScopedFile { + ~ScopedFile() { if (file) fclose(file); } + FILE *file = nullptr; + }; + + ScopedFile sf; + sf.file = fopen(smapsFile.constData(), "r"); + + if (sf.file == nullptr) + return false; + + const int lineLen = 100; // we are not interested in full library paths + char line[lineLen + 5]; // padding for highly unlikely trailing perm flags below + char *pl; // pointer to chars within line + bool ok = true; + + if (fgets(line, lineLen, sf.file) == nullptr) + return false; + + // sanity checks + pl = line; + for (pl = line; pl < (line + 4) && ok; ++pl) + ok = ((*pl >= '0' && *pl <= '9') || (*pl >= 'a' && *pl <= 'f')); + while (strlen(line) == lineLen - 1 && line[lineLen - 2] != '\n') { + if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) + break; + } + if (fgets(line, lineLen, sf.file) == nullptr) + return false; + static const char strSize[] = "Size: "; + ok = ok && !qstrncmp(line, strSize, sizeof(strSize) - 1); + if (!ok) + return false; + + // Determine block size + ok = false; + int blockLen = 0; + while (fgets(line, lineLen, sf.file) != nullptr && !ok) { + if (!(line[0] < '0' || line[0] > '9') && (line[0] < 'a' || line[0] > 'f')) + ok = true; + ++blockLen; + } + if (!ok || blockLen < 12 || blockLen > 32) + return false; + + fseek(sf.file, 0, SEEK_SET); + bool wasPrivateOnly = false; + ok = false; + + while (true) { + if (Q_UNLIKELY(!(fgets(line, lineLen, sf.file) != nullptr))) { + ok = feof(sf.file); + break; + } + + // Determine permission flags + pl = line; + while (*pl && *pl != ' ') + ++pl; + char permissions[4]; + memcpy(permissions, ++pl, sizeof(permissions)); + + // Determine inode + int spaceCount = 0; + while (*pl && spaceCount < 3) { + if (*pl == ' ') + ++spaceCount; + ++pl; + } + bool hasInode = (*pl != '0'); + + // Determine library name + while (*pl && *pl != ' ') + ++pl; + while (*pl && *pl == ' ') + ++pl; + + static const char strStack[] = "stack]"; + bool isMainStack = (Q_UNLIKELY(*pl == '[' + && !qstrncmp(pl + 1, strStack, sizeof(strStack) - 1))); + // Skip rest of library path + while (strlen(line) == lineLen - 1 && line[lineLen - 2] != '\n') { + if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) + break; + } + + int skipLen = blockLen; + uint vm = 0; + uint rss = 0; + uint pss = 0; + const int sizeTag = 0x01; + const int rssTag = 0x02; + const int pssTag = 0x04; + const int allTags = sizeTag | rssTag | pssTag; + int foundTags = 0; + + while (foundTags < allTags && skipLen > 0) { + skipLen--; + if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) + break; + pl = line; + + static const char strSize[] = "ize:"; + static const char strXss[] = "ss:"; + + switch (*pl) { + case 'S': + if (!qstrncmp(pl + 1, strSize, sizeof(strSize) - 1)) { + foundTags |= sizeTag; + vm = parseValue(pl + sizeof(strSize)); + } + break; + case 'R': + if (!qstrncmp(pl + 1, strXss, sizeof(strXss) - 1)) { + foundTags |= rssTag; + rss = parseValue(pl + sizeof(strXss)); + } + break; + case 'P': + if (!qstrncmp(pl + 1, strXss, sizeof(strXss) - 1)) { + foundTags |= pssTag; + pss = parseValue(pl + sizeof(strXss)); + } + break; + } + } + + if (foundTags < allTags) + break; + + _totalVm += vm; + _totalRss += rss; + _totalPss += pss; + + static const char permRXP[] = { 'r', '-', 'x', 'p' }; + static const char permRWP[] = { 'r', 'w', '-', 'p' }; + if (!memcmp(permissions, permRXP, sizeof(permissions))) { + _textVm += vm; + _textRss += rss; + _textPss += pss; + } else if (!memcmp(permissions, permRWP, sizeof(permissions)) + && !isMainStack && (vm != 8192 || hasInode || !wasPrivateOnly) // try to exclude stack + && !hasInode) { + _heapVm += vm; + _heapRss += rss; + _heapPss += pss; + } + + static const char permP[] = { '-', '-', '-', 'p' }; + wasPrivateOnly = !memcmp(permissions, permP, sizeof(permissions)); + + for (int skip = skipLen; skip; --skip) { + if (Q_UNLIKELY(!fgets(line, lineLen, sf.file))) + break; + } + } + + if (ok) { + // publish the readings + totalVm.store(_totalVm); + totalRss.store(_totalRss); + totalPss.store(_totalPss); + textVm.store(_textVm); + textRss.store(_textRss); + textPss.store(_textPss); + heapVm.store(_heapVm); + heapRss.store(_heapRss); + heapPss.store(_heapPss); + } + + return ok; +} + +#elif defined(Q_OS_MACOS) + +void ProcessReader::openCpuLoad() +{ +} + +qreal ProcessReader::readCpuLoad() +{ + return 0.0; +} + +bool ProcessReader::readMemory() +{ + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count)) { + qCWarning(LogSystem) << "Could not read memory data"; + return false; + } + + totalRss.store(t_info.resident_size); + totalVm.store(t_info.virtual_size); + + return true; +} + +#else + +void ProcessReader::openCpuLoad() +{ +} + +qreal ProcessReader::readCpuLoad() +{ + return 0.0; +} + +bool ProcessReader::readMemory() +{ + return false; +} + +#endif diff --git a/src/monitor-lib/processreader.h b/src/monitor-lib/processreader.h new file mode 100644 index 00000000..31b2f2f4 --- /dev/null +++ b/src/monitor-lib/processreader.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QAtomicInteger> +#include <QElapsedTimer> +#include <QObject> + +#include <QtAppManCommon/global.h> + +#if defined(Q_OS_LINUX) +# include <QScopedPointer> +# include <QtAppManManager/sysfsreader.h> +#endif + +QT_BEGIN_NAMESPACE_AM + +class ProcessReader : public QObject { + Q_OBJECT +public slots: + void update(); + void setProcessId(qint64 pid); + +signals: + void updated(); + +public: + QAtomicInteger<quint32> cpuLoad; + + QAtomicInteger<quint32> totalVm; + QAtomicInteger<quint32> totalRss; + QAtomicInteger<quint32> totalPss; + QAtomicInteger<quint32> textVm; + QAtomicInteger<quint32> textRss; + QAtomicInteger<quint32> textPss; + QAtomicInteger<quint32> heapVm; + QAtomicInteger<quint32> heapRss; + QAtomicInteger<quint32> heapPss; + +#if defined(Q_OS_LINUX) + // it's public solely for testing purposes + bool readSmaps(const QByteArray &smapsFile); +#endif + +private: + void openCpuLoad(); + qreal readCpuLoad(); + bool readMemory(); + +#if defined(Q_OS_LINUX) + QScopedPointer<SysFsReader> m_statReader; +#endif + QElapsedTimer m_elapsedTime; + quint64 m_lastCpuUsage = 0.0; + + qint64 m_pid = 0; +}; + +QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/processstatus.cpp b/src/monitor-lib/processstatus.cpp new file mode 100644 index 00000000..47296657 --- /dev/null +++ b/src/monitor-lib/processstatus.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include "processstatus.h" + +#include <QCoreApplication> +#include <QtQml/qqmlinfo.h> + + +#include "abstractruntime.h" +#include "applicationmanager.h" +#include "logging.h" + +#include <limits> + +/*! + \qmltype ProcessStatus + \inqmlmodule QtApplicationManager.SystemUI + \ingroup system-ui + \brief Provides information on the status of an application process. + + ProcessStatus provides information about the process of a given application. + + You can use it alongside a Timer for instance to periodically query the status of an + application process. + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + import QtApplicationManager.SystemUI 1.0 + + Item { + id: root + property var application: ApplicationManager.get(0) + ... + ProcessStatus { + id: processStatus + applicationId: root.application.id + } + Timer { + interval: 1000 + running: root.visible && root.application.runState === Am.Running + repeat: true + onTriggered: processStatus.update() + } + Text { + text: "PSS.total: " + (processStatus.memoryPss.total / 1e6).toFixed(0) + " MB" + } + } + \endqml + + You can also use this component as a MonitorModel data source if you want to plot its + previous values over time: + + \qml + import QtQuick 2.11 + import QtApplicationManager 1.0 + import QtApplicationManager.SystemUI 1.0 + ... + MonitorModel { + running: true + ProcessStatus { + applicationId: "some.app.id" + } + } + \endqml + + These are the supported keys in the memory properties (\c memoryVirtual, \c memoryRss and \c memoryPss): + + \table + \header + \li Key + \li Description + \row + \li \c total + \li The amount of memory used in total in bytes. + \row + \li \c text + \li The amount of memory used by the code section in bytes. + \row + \li \c heap + \li The amount of memory used by the heap in bytes. This is private, dynamically allocated + memory (for example through \c malloc or \c mmap on Linux). + \endtable +*/ + +QT_USE_NAMESPACE_AM + +QThread *ProcessStatus::m_workerThread = nullptr; + +ProcessStatus::ProcessStatus(QObject *parent) + : QObject(parent) +{ + if (!m_workerThread) { + m_workerThread = new QThread; + m_workerThread->start(); + } + + m_reader.reset(new ProcessReader); + connect(m_reader.data(), &ProcessReader::updated, this, [this]() { + emit cpuLoadChanged(); + fetchMemoryReadings(); + m_pendingUpdate = false; + }); + connect(this, &ProcessStatus::processIdChanged, m_reader.data(), &ProcessReader::setProcessId); +} + +/*! + \qmlmethod ProcessStatus::update + + Updates the properties cpuLoad, memoryVirtual, memoryRss and memoryPss. +*/ +void ProcessStatus::update() +{ + if (!m_pendingUpdate) { + m_pendingUpdate = true; + QMetaObject::invokeMethod(m_reader.data(), &ProcessReader::update); + } +} + +/*! + \qmlproperty string ProcessStatus::applicationId + + \l{ApplicationObject::id}{Id} of the \l{ApplicationObject}{application} whose process is to + be monitored. + + \sa ApplicationObject +*/ +QString ProcessStatus::applicationId() const +{ + return m_appId; +} + +void ProcessStatus::setApplicationId(const QString &appId) +{ + if (m_appId != appId || m_appId.isNull()) { + if (m_application) { + disconnect(m_application, nullptr, this, nullptr); + m_application = nullptr; + } + m_appId = appId; + if (!appId.isEmpty()) { + int appIndex = ApplicationManager::instance()->indexOfApplication(appId); + if (appIndex < 0) { + qmlWarning(this) << "Invalid application ID:" << appId; + } else { + m_application = ApplicationManager::instance()->application(appIndex); + connect(m_application, &AbstractApplication::runStateChanged, this, &ProcessStatus::onRunStateChanged); + } + } + determinePid(); + emit applicationIdChanged(appId); + } +} + +void ProcessStatus::onRunStateChanged(Am::RunState state) +{ + if (state == Am::Running || state == Am::NotRunning) + determinePid(); +} + +void ProcessStatus::determinePid() +{ + qint64 newId; + if (m_appId.isEmpty()) { + newId = QCoreApplication::applicationPid(); + } else { + Q_ASSERT(m_application); + if (ApplicationManager::instance()->isSingleProcess()) + newId = 0; + else + newId = m_application->currentRuntime() ? m_application->currentRuntime()->applicationProcessId() : 0; + } + + if (newId != m_pid) { + m_pid = newId; + emit processIdChanged(m_pid); + } +} + +/*! + \qmlproperty int ProcessStatus::processId + \readonly + + This property holds the OS specific process identifier (PID) that is monitored. This can be + used by external tools for example. The property is 0, if there is no process associated with + the \l applicationId. In particular, if the application-manager runs in single-process mode, + only the System-UI (identified by an empty \l applicationId) will have an associated process. +*/ +qint64 ProcessStatus::processId() const +{ + return m_pid; +} + +/*! + \qmlproperty real ProcessStatus::cpuLoad + \readonly + + The process's CPU utilization when update() was last called. A value of 0 means + that the process was idle, a value of 1 means it fully used the equivalent of one core + (which may be split over several ones). + + \sa ProcessStatus::update +*/ +qreal ProcessStatus::cpuLoad() +{ + quint32 value = m_reader->cpuLoad.load(); + return ((qreal)value) / ((qreal)std::numeric_limits<quint32>::max()); +} + +void ProcessStatus::fetchMemoryReadings() +{ + // Although smaps claims to report kB it's actually KiB (2^10 = 1024 Bytes) + m_memoryVirtual[qSL("total")] = quint64(m_reader->totalVm.load()) << 10; + m_memoryVirtual[qSL("text")] = quint64(m_reader->textVm.load()) << 10; + m_memoryVirtual[qSL("heap")] = quint64(m_reader->heapVm.load()) << 10; + m_memoryRss[qSL("total")] = quint64(m_reader->totalRss.load()) << 10; + m_memoryRss[qSL("text")] = quint64(m_reader->textRss.load()) << 10; + m_memoryRss[qSL("heap")] = quint64(m_reader->heapRss.load()) << 10; + m_memoryPss[qSL("total")] = quint64(m_reader->totalPss.load()) << 10; + m_memoryPss[qSL("text")] = quint64(m_reader->textPss.load()) << 10; + m_memoryPss[qSL("heap")] = quint64(m_reader->heapPss.load()) << 10; + + emit memoryReportingChanged(m_memoryVirtual, m_memoryRss, m_memoryPss); +} + +/*! + \qmlproperty var ProcessStatus::memoryVirtual + \readonly + + A map of the process's virtual memory usage. See ProcessStatus description for a list of supported keys. + The total amount of virtual memory is provided through \c memoryVirtual.total for + example. + + The value of this property is updated when ProcessStatus::update is called. + + \sa ProcessStatus::update +*/ +QVariantMap ProcessStatus::memoryVirtual() const +{ + return m_memoryVirtual; +} + +/*! + \qmlproperty var ProcessStatus::memoryRss + \readonly + + A map of the process's RSS (Resident Set Size) memory usage. This is the amount of memory that is + actually mapped to physical RAM. See ProcessStatus description for a list of supported keys. + + The value of this property is updated when ProcessStatus::update is called. + + \sa ProcessStatus::update +*/ +QVariantMap ProcessStatus::memoryRss() const +{ + return m_memoryRss; +} + +/*! + \qmlproperty var ProcessStatus::memoryPss + \readonly + + A map of the process's PSS (Proportional Set Size) memory usage. This is the + proportional share of the RSS value above. For instance if two processes share 2 MB + the RSS value will be 2 MB for each process and the PSS value 1 MB for each process. + As the name implies, the code section of shared libraries is generally shared between + processes. Memory may also be shared by other means provided by the OS (e.g. through + \c mmap on Linux). See ProcessStatus description for a list of supported keys. + + The value of this property is updated when ProcessStatus::update is called. + + \sa ProcessStatus::update +*/ +QVariantMap ProcessStatus::memoryPss() const +{ + return m_memoryPss; +} + +/*! + \qmlproperty list<string> ProcessStatus::roleNames + \readonly + + Names of the roles provided by ProcessStatus when used as a MonitorModel data source. + + \sa MonitorModel +*/ +QStringList ProcessStatus::roleNames() const +{ + return { qSL("cpuLoad"), qSL("memoryVirtual"), qSL("memoryRss"), qSL("memoryPss") }; +} diff --git a/src/monitor-lib/processmonitor.h b/src/monitor-lib/processstatus.h index 1123d870..a204b942 100644 --- a/src/monitor-lib/processmonitor.h +++ b/src/monitor-lib/processstatus.h @@ -41,90 +41,73 @@ #pragma once -#include <QAbstractListModel> -#include <QString> -#include <QByteArray> +#include <QAtomicInteger> +#include <QObject> +#include <QPointer> +#include <QThread> #include <QVariant> -#include <QHash> + #include <QtAppManCommon/global.h> +#include <QtAppManManager/amnamespace.h> +#include <QtAppManManager/application.h> +#include <QtAppManMonitor/processreader.h> QT_BEGIN_NAMESPACE_AM -class ProcessMonitorPrivate; - -class ProcessMonitor : public QAbstractListModel +// It's assumed that all ProcessStatus instances are created from the same thread (most likely the main one). +class ProcessStatus : public QObject { Q_OBJECT - Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) - Q_PROPERTY(qint64 processId READ processId NOTIFY processIdChanged) Q_PROPERTY(QString applicationId READ applicationId WRITE setApplicationId NOTIFY applicationIdChanged) - Q_PROPERTY(int reportingInterval READ reportingInterval WRITE setReportingInterval NOTIFY reportingIntervalChanged) - Q_PROPERTY(bool memoryReportingEnabled READ isMemoryReportingEnabled WRITE setMemoryReportingEnabled NOTIFY memoryReportingEnabledChanged) + Q_PROPERTY(qint64 processId READ processId NOTIFY processIdChanged) + Q_PROPERTY(qreal cpuLoad READ cpuLoad NOTIFY cpuLoadChanged) Q_PROPERTY(QVariantMap memoryVirtual READ memoryVirtual NOTIFY memoryReportingChanged) Q_PROPERTY(QVariantMap memoryRss READ memoryRss NOTIFY memoryReportingChanged) Q_PROPERTY(QVariantMap memoryPss READ memoryPss NOTIFY memoryReportingChanged) - Q_PROPERTY(bool cpuLoadReportingEnabled READ isCpuLoadReportingEnabled WRITE setCpuLoadReportingEnabled NOTIFY cpuLoadReportingEnabledChanged) - Q_PROPERTY(qreal cpuLoad READ cpuLoad NOTIFY cpuLoadReportingChanged) - Q_PROPERTY(bool frameRateReportingEnabled READ isFrameRateReportingEnabled WRITE setFrameRateReportingEnabled NOTIFY frameRateReportingEnabledChanged) - Q_PROPERTY(QList<QObject *> monitoredWindows READ monitoredWindows WRITE setMonitoredWindows NOTIFY monitoredWindowsChanged) - + Q_PROPERTY(QStringList roleNames READ roleNames CONSTANT) public: - ProcessMonitor(QObject *parent = nullptr); - ~ProcessMonitor() override; + ProcessStatus(QObject *parent = nullptr); - Q_INVOKABLE QVariantMap get(int index) const; + QStringList roleNames() const; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - QHash<int, QByteArray> roleNames() const override; - - void setCount(int count); - int count() const; + Q_INVOKABLE void update(); qint64 processId() const; QString applicationId() const; void setApplicationId(const QString &appId); - void setReportingInterval(int intervalInMSec); - int reportingInterval() const; - - bool isMemoryReportingEnabled() const; - void setMemoryReportingEnabled(bool enabled); - - bool isCpuLoadReportingEnabled() const; - void setCpuLoadReportingEnabled(bool enabled); - - bool isFrameRateReportingEnabled() const; - void setFrameRateReportingEnabled(bool enabled); - - QList<QObject *> monitoredWindows() const; - void setMonitoredWindows(QList<QObject *> windows); - - qreal cpuLoad() const; + qreal cpuLoad(); QVariantMap memoryVirtual() const; QVariantMap memoryRss() const; QVariantMap memoryPss() const; signals: - void countChanged(int count); - void processIdChanged(qint64 processId); void applicationIdChanged(const QString &applicationId); - void reportingIntervalChanged(int reportingInterval); - - void memoryReportingEnabledChanged(); - void cpuLoadReportingEnabledChanged(); - void frameRateReportingEnabledChanged(); - + void processIdChanged(qint64 processId); + void cpuLoadChanged(); void memoryReportingChanged(const QVariantMap &memoryVirtual, const QVariantMap &memoryRss, const QVariantMap &memoryPss); - void cpuLoadReportingChanged(qreal load); - void frameRateReportingChanged(QVariantList frameRate); - void monitoredWindowsChanged(); + +private slots: + void onRunStateChanged(Am::RunState state); private: - ProcessMonitorPrivate *d_ptr; - Q_DECLARE_PRIVATE(ProcessMonitor) + void fetchMemoryReadings(); + void determinePid(); + + QString m_appId; + qint64 m_pid = 0; + + QVariantMap m_memoryVirtual; + QVariantMap m_memoryRss; + QVariantMap m_memoryPss; + + QPointer<AbstractApplication> m_application; + + bool m_pendingUpdate = false; + QScopedPointer<ProcessReader> m_reader; + static QThread *m_workerThread; }; QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/systemmonitor.cpp b/src/monitor-lib/systemmonitor.cpp deleted file mode 100644 index b2d695f4..00000000 --- a/src/monitor-lib/systemmonitor.cpp +++ /dev/null @@ -1,968 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:LGPL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: LGPL-3.0 -** -****************************************************************************/ - -#include <QSysInfo> -#include <QThread> -#include <QFile> -#include <QTimerEvent> -#include <QElapsedTimer> -#include <vector> -#include <QtQml/qqmlinfo.h> -#if !defined(AM_HEADLESS) -# include <QGuiApplication> -# include <QQuickView> -#endif - -#include "global.h" -#include "logging.h" -#include "qml-utilities.h" -#include "applicationmanager.h" -#include "systemmonitor.h" -#include "systemmonitor_p.h" -#include <QtAppManWindow/windowmanager.h> - - -/*! - \qmltype SystemMonitor - \inqmlmodule QtApplicationManager.SystemUI - \ingroup system-ui - \brief The system monitoring model, giving access to a range of measurements, e.g. CPU load, - frame rate, etc. - - The SystemMonitor component provides information about system resources and performance, - like CPU and memory usage, I/O load and frame rate. - - It's derived from \c QAbstractListModel, so it can be used directly as a model in an - appropriate view. - - \target role names - - The following roles are available in this model: - - \table - \header - \li Role name - \li Type - \li Description - \row - \li \c cpuLoad - \li real - \li The current CPU utilization in the range 0 (completely idle) to 1 (fully busy). - \row - \li \c gpuLoad - \li real - \li The current GPU utilization in the range 0 (completely idle) to 1 (fully busy). - This is dependent on tools from the graphics hardware vendor and might not work on - every system. - \row - \li \c memoryUsed - \li int - \li The amount of physical system memory used in bytes. - \row - \li \c ioLoad - \li var - \li A map of devices registered with \l addIoLoadReporting() and their I/O load in the - range [0, 1]. For instance the load of a registered device "sda" can be accessed - through \c ioLoad.sda. - \row - \li \c averageFps - \li real - \li The average frame rate during the last \l reportingInterval in frames per second. - \row - \li \c minimumFps - \li real - \li The minimum frame rate during the last \l reportingInterval in frames per second. - \row - \li \c maximumFps - \li real - \li The maximum frame rate during the last \l reportingInterval in frames per second. - \row - \li \c fpsJitter - \li real - \li A measure for the average deviation from the ideal frame rate of 60 fps during the last - \l reportingInterval. - \endtable - - \note The model will be updated each \l reportingInterval milliseconds. The roles will only - be populated, if the corresponding reporting parts (memory, CPU, etc.) have been enabled. - - After importing \c QtApplicationManager.SystemUI you could use the SystemMonitor component as follows: - - \qml - import QtQuick 2.4 - import QtApplicationManager.SystemUI 1.0 - - ListView { - id: view - width: 200; height: 200 - - model: SystemMonitor { - fpsReportingInterval: 1000 - fpsReportingEnabled: view.visible - } - - delegate: Text { text: averageFps } - } - \endqml -*/ - -/*! - \qmlproperty int SystemMonitor::count - - This property holds the number of reading points that will be kept in the model. The minimum - value that can be set is 2 and the default value is 10. -*/ - -/*! - \qmlproperty int SystemMonitor::reportingInterval - - This property holds the interval in milliseconds between reporting updates. Note, that - reporting will only start once this property is set. Setting a new value will reset the model. - Valid values must be greater than zero. - - At least one of the reporting parts (memory, CPU load etc.) must be enabled, respectively - registered to start the reporting. -*/ - -/*! - \qmlproperty real SystemMonitor::idleLoadThreshold - - A value in the range [0, 1]. If the CPU load is greater than this threshold the \l idle - property will be \c false, otherwise \c true. This property also influences when the - application manager quick-launches application processes. - - The default value is read from the \l {Configuration}{configuration YAML file} - (\c quicklaunch/idleLoad), respectively 0.1, if this configuration option is not provided. - - \sa idle -*/ - -/*! - \qmlproperty int SystemMonitor::totalMemory - \readonly - - This property holds the total amount of physical memory (RAM) installed on the system in bytes. -*/ - -/*! - \qmlproperty int SystemMonitor::memoryUsed - \readonly - - This property holds the amount of physical memory (RAM) used in bytes. -*/ - -/*! - \qmlproperty int SystemMonitor::cpuCores - \readonly - - This property holds the number of physical CPU cores that are installed on the system. -*/ - -/*! - \qmlproperty int SystemMonitor::cpuLoad - \readonly - - This property holds the current CPU utilization as a value ranging from 0 (inclusive, completely - idle) to 1 (inclusive, fully busy). -*/ - -/*! - \qmlproperty int SystemMonitor::gpuLoad - \readonly - - This property holds the current GPU utilization as a value ranging from 0 (inclusive, completely - idle) to 1 (inclusive, fully busy). - - \note This is dependent on tools from the graphics hardware vendor and might not work on - every system. - - Currently, this only works on \e Linux with either \e Intel or \e NVIDIA chipsets, plus the - tools from the respective vendors have to be installed: - - \table - \header - \li Hardware - \li Tool - \li Notes - \row - \li NVIDIA - \li \c nvidia-smi - \li The utilization will only be shown for the first GPU of the system, in case multiple GPUs - are installed. - \row - \li Intel - \li \c intel_gpu_top - \li The binary has to be made set-UID root, e.g. via \c{sudo chmod +s $(which intel_gpu_top)}, - or the application-manager has to be run as the \c root user. - \endtable -*/ - -/*! - \qmlproperty bool SystemMonitor::memoryReportingEnabled - - A boolean value that determines whether periodic memory reporting is enabled. -*/ - -/*! - \qmlproperty bool SystemMonitor::cpuLoadReportingEnabled - - A boolean value that determines whether periodic CPU load reporting is enabled. -*/ - -/*! - \qmlproperty bool SystemMonitor::gpuLoadReportingEnabled - - A boolean value that determines whether periodic GPU load reporting is enabled. - - GPU load reporting is only supported on selected hardware: please see gpuLoad for more - information. -*/ - -/*! - \qmlproperty bool SystemMonitor::fpsReportingEnabled - - A boolean value that determines whether periodic frame rate reporting is enabled. -*/ - -/*! - \qmlproperty bool SystemMonitor::idle - \readonly - - A boolean value that defines, whether the system is idle. If the CPU load is greater than - \l idleLoadThreshold, this property will be set to \c false, otherwise to \c true. The value is - evaluated every second and reflects whether the average load during the last second was below - or above the threshold. - - \sa idleLoadThreshold -*/ - - -/*! - \qmlsignal SystemMonitor::memoryReportingChanged(int used); - - This signal is emitted periodically when memory reporting is enabled. The frequency is defined - by \l reportingInterval. The \a used physical system memory in bytes is provided as argument. - - \sa memoryReportingEnabled - \sa reportingInterval -*/ - -/*! - \qmlsignal SystemMonitor::cpuLoadReportingChanged(real load) - - This signal is emitted periodically when CPU load reporting is enabled. The frequency is - defined by \l reportingInterval. The \a load parameter indicates the CPU utilization in the - range 0 (completely idle) to 1 (fully busy). - - \sa cpuLoadReportingEnabled - \sa reportingInterval -*/ - -/*! - \qmlsignal SystemMonitor::gpuLoadReportingChanged(real load) - - This signal is emitted periodically when GPU load reporting is enabled. The frequency is - defined by \l reportingInterval. The \a load parameter indicates the GPU utilization in the - range 0 (completely idle) to 1 (fully busy). - - \sa gpuLoadReportingEnabled - \sa reportingInterval -*/ - -/*! - \qmlsignal SystemMonitor::ioLoadReportingChanged(string device, real load); - - This signal is emitted periodically for each I/O device that has been registered with - \l addIoLoadReporting. The frequency is defined by \l reportingInterval. The string \a device - holds the name of the device that is monitored. The \a load parameter indicates the - utilization of the \a device in the range 0 (completely idle) to 1 (fully busy). - - \sa addIoLoadReporting - \sa removeIoLoadReporting - \sa ioLoadReportingDevices - \sa reportingInterval -*/ - -/*! - \qmlsignal SystemMonitor::fpsReportingChanged(real average, real minimum, real maximum, real jitter); - - This signal is emitted periodically when frame rate reporting is enabled. The update frequency - is defined by \l reportingInterval. The arguments denote the \a average, \a minimum and - \a maximum frame rate during the last \l reportingInterval in frames per second. Additionally, - \a jitter is a measure for the average deviation from the ideal frame rate of 60 fps. -*/ - - -QT_BEGIN_NAMESPACE_AM - -namespace { -enum Roles -{ - CpuLoad = Qt::UserRole + 5000, - MemoryUsed, - IoLoad, - GpuLoad, - - AverageFps = Qt::UserRole + 6000, - MinimumFps, - MaximumFps, - FpsJitter -}; -} - - - -int SystemMonitorPrivate::latestReportPos() const -{ - if (reportPos == 0) - return reports.size() - 1; - else - return reportPos - 1; -} - -#if !defined(AM_HEADLESS) -void SystemMonitorPrivate::registerNewView(QQuickWindow *view) -{ - Q_Q(SystemMonitor); - if (reportFps) - connect(view, &QQuickWindow::frameSwapped, q, &SystemMonitor::reportFrameSwap); -} -#endif - -void SystemMonitorPrivate::setupFpsReporting() -{ -#if !defined(AM_HEADLESS) - Q_Q(SystemMonitor); - if (!windowManagerConnectionCreated) { - connect(WindowManager::instance(), &WindowManager::compositorViewRegistered, this, &SystemMonitorPrivate::registerNewView); - windowManagerConnectionCreated = true; - } - - for (const QQuickWindow *view : WindowManager::instance()->compositorViews()) { - if (reportFps) - connect(view, &QQuickWindow::frameSwapped, q, &SystemMonitor::reportFrameSwap); - else - disconnect(view, &QQuickWindow::frameSwapped, q, &SystemMonitor::reportFrameSwap); - } -#endif -} - -void SystemMonitorPrivate::setupTimer() -{ - bool shouldBeOn = reportCpu || reportGpu || reportMem || reportFps || !ioHash.isEmpty(); - - if (!shouldBeOn && reportingTimerId) { - killTimer(reportingTimerId); - reportingTimerId = 0; - } else if (shouldBeOn && !reportingTimerId) { - updateModel(true /* clear */); - reportingTimerId = startTimer(reportingInterval); - } -} - -void SystemMonitorPrivate::makeNewReport() -{ - Q_Q(SystemMonitor); - - Report r; - QVector<int> changedRoles; - { - const Report ¤tReport = reports.at(reportPos); - - if (reportCpu) - r.cpuLoad = cpu->readLoadValue(); - if (currentReport.cpuLoad != r.cpuLoad) - changedRoles.append(CpuLoad); - - if (reportGpu) - r.gpuLoad = gpu->readLoadValue(); - if (currentReport.gpuLoad != r.gpuLoad) - changedRoles.append(GpuLoad); - - if (reportMem) - r.memoryUsed = memory->readUsedValue(); - if (currentReport.memoryUsed != r.memoryUsed) - changedRoles.append(MemoryUsed); - - for (auto it = ioHash.cbegin(); it != ioHash.cend(); ++it) { - qreal ioVal = it.value()->readLoadValue(); - emit q->ioLoadReportingChanged(it.key(), ioVal); - r.ioLoad.insert(it.key(), ioVal); - } - if (currentReport.ioLoad != r.ioLoad) - changedRoles.append(IoLoad); - - if (reportFps) { - if (FrameTimer *ft = frameTimer.value(nullptr)) { - r.fpsAvg = ft->averageFps(); - r.fpsMin = ft->minimumFps(); - r.fpsMax = ft->maximumFps(); - r.fpsJitter = ft->jitterFps(); - ft->reset(); - emit q->fpsReportingChanged(r.fpsAvg, r.fpsMin, r.fpsMax, r.fpsJitter); - } - } - if (currentReport.fpsAvg != r.fpsAvg) - changedRoles.append(AverageFps); - if (currentReport.fpsMin != r.fpsMin) - changedRoles.append(MinimumFps); - if (currentReport.fpsMax != r.fpsMax) - changedRoles.append(MaximumFps); - if (currentReport.fpsJitter != r.fpsJitter) - changedRoles.append(FpsJitter); - } - - // ring buffer handling - // optimization: instead of sending a dataChanged for every item, we always move the - // last item to the front and change its data only - int last = reports.size() - 1; - q->beginMoveRows(QModelIndex(), last, last, QModelIndex(), 0); - reports[reportPos++] = r; - if (reportPos > last) - reportPos = 0; - q->endMoveRows(); - q->dataChanged(q->index(0), q->index(0), changedRoles); - - if (reportMem) - emit q->memoryReportingChanged(r.memoryUsed); - if (reportCpu) - emit q->cpuLoadReportingChanged(r.cpuLoad); - if (reportGpu) - emit q->gpuLoadReportingChanged(r.gpuLoad); -} - -void SystemMonitorPrivate::timerEvent(QTimerEvent *te) -{ - Q_Q(SystemMonitor); - - if (te && te->timerId() == reportingTimerId) { - makeNewReport(); - } else if (te && te->timerId() == idleTimerId) { - qreal idleVal = idleCpu->readLoadValue(); - bool nowIdle = (idleVal <= idleThreshold); - if (nowIdle != isIdle) { - isIdle = nowIdle; - emit q->idleChanged(nowIdle); - } - } -} - -auto SystemMonitorPrivate::reportForRow(int row) const -> const Report & -{ - // convert a visual row position to an index into the internal ringbuffer - int pos = reportPos - row - 1; - if (pos < 0) - pos += reports.size(); - if (pos < 0 || pos >= reports.size()) - return reports.first(); - return reports.at(pos); -} - -void SystemMonitorPrivate::updateModel(bool clear) -{ - Q_Q(SystemMonitor); - - q->beginResetModel(); - - if (clear) { - reports.clear(); - reports.resize(count); - reportPos = 0; - } else { - int oldCount = reports.size(); - int diff = count - oldCount; - if (diff > 0) - reports.insert(reportPos, diff, Report()); - else { - if (reportPos <= count) { - reports.remove(reportPos, -diff); - if (reportPos == count) - reportPos = 0; - } else { - reports.remove(reportPos, oldCount - reportPos); - reports.remove(0, reportPos - count); - reportPos = 0; - } - } - } - q->endResetModel(); -} - -SystemMonitor::SystemMonitor() - : d_ptr(new SystemMonitorPrivate(this)) -{ - Q_D(SystemMonitor); - - d->idleCpu = new CpuReader; - d->cpu = new CpuReader; - d->gpu = new GpuReader; - -#if defined(Q_OS_LINUX) - QMap<QByteArray, QByteArray> cgroupInfo = fetchCGroupProcessInfo(QCoreApplication::applicationPid()); - d->memory = new MemoryReader(QString::fromLatin1(cgroupInfo["memory"])); -#else - d->memory = new MemoryReader; -#endif - - d->idleTimerId = d->startTimer(1000); - - d->roleNames.insert(CpuLoad, "cpuLoad"); - d->roleNames.insert(GpuLoad, "gpuLoad"); - d->roleNames.insert(MemoryUsed, "memoryUsed"); - d->roleNames.insert(IoLoad, "ioLoad"); - d->roleNames.insert(AverageFps, "averageFps"); - d->roleNames.insert(MinimumFps, "minimumFps"); - d->roleNames.insert(MaximumFps, "maximumFps"); - d->roleNames.insert(FpsJitter, "fpsJitter"); - - d->updateModel(true); -} - -SystemMonitor::~SystemMonitor() -{ - Q_D(SystemMonitor); - - delete d->idleCpu; - delete d->memory; - delete d->cpu; - delete d->gpu; - qDeleteAll(d->ioHash); - delete d; -} - -int SystemMonitor::rowCount(const QModelIndex &parent) const -{ - Q_D(const SystemMonitor); - - if (parent.isValid()) - return 0; - return d->reports.count(); -} - -QVariant SystemMonitor::data(const QModelIndex &index, int role) const -{ - Q_D(const SystemMonitor); - - if (index.parent().isValid() || !index.isValid() || index.row() < 0 || index.row() >= d->reports.size()) - return QVariant(); - - const SystemMonitorPrivate::Report &r = d->reportForRow(index.row()); - - switch (role) { - case CpuLoad: - return r.cpuLoad; - case GpuLoad: - return r.gpuLoad; - case MemoryUsed: - return r.memoryUsed; - case IoLoad: - return r.ioLoad; - case AverageFps: - return r.fpsAvg; - case MinimumFps: - return r.fpsMin; - case MaximumFps: - return r.fpsMax; - case FpsJitter: - return r.fpsJitter; - } - return QVariant(); -} - -QHash<int, QByteArray> SystemMonitor::roleNames() const -{ - Q_D(const SystemMonitor); - - return d->roleNames; -} - -/*! - \qmlmethod object SystemMonitor::get(int index) - - Returns the model data for the reading point identified by \a index as a JavaScript object. - See the \l {role names} for the expected object elements. The \a index must be in the range - [0, \l count), returns an empty object if it is invalid. -*/ -QVariantMap SystemMonitor::get(int row) const -{ - if (row < 0 || row >= count()) { - qCWarning(LogSystem) << "invalid row:" << row; - return QVariantMap(); - } - - QVariantMap map; - QHash<int, QByteArray> roles = roleNames(); - for (auto it = roles.cbegin(); it != roles.cend(); ++it) - map.insert(qL1S(it.value()), data(index(row), it.key())); - return map; -} - - -quint64 SystemMonitor::totalMemory() const -{ - Q_D(const SystemMonitor); - -#if defined(Q_OS_LINUX) - auto limit = d->memory->groupLimit(); - if (limit > 0 && limit < d->memory->totalValue()) - return limit; - else - return d->memory->totalValue(); -#else - return d->memory->totalValue(); -#endif -} - -quint64 SystemMonitor::memoryUsed() const -{ - Q_D(const SystemMonitor); - return d->reports[d->latestReportPos()].memoryUsed; -} - -int SystemMonitor::cpuCores() const -{ - return QThread::idealThreadCount(); -} - -qreal SystemMonitor::cpuLoad() const -{ - Q_D(const SystemMonitor); - return d->reports[d->latestReportPos()].cpuLoad; -} - -qreal SystemMonitor::gpuLoad() const -{ - Q_D(const SystemMonitor); - return d->reports[d->latestReportPos()].gpuLoad; -} - -/*! - \qmlmethod bool SystemMonitor::setMemoryWarningThresholds(real lowWarning, real criticalWarning); - - Activates monitoring of available system memory. The arguments must define percent values (in - the range [0, 100]) of the \l totalMemory available. The \a lowWarning argument defines the - threshold in percent, when applications get the \l ApplicationInterface::memoryLowWarning() - signal. The \a criticalWarning argument defines the threshold, when applications get the - \l ApplicationInterface::memoryCriticalWarning() signal. - - Returns true, if monitoring could be started, otherwise false (e.g. if arguments are out of - range). - - \note This is only supported on Linux with the cgroups memory subsystem enabled. - - \sa totalMemory - \sa ApplicationInterface::memoryLowWarning() - \sa ApplicationInterface::memoryCriticalWarning() -*/ -bool SystemMonitor::setMemoryWarningThresholds(qreal lowWarning, qreal criticalWarning) -{ - Q_D(SystemMonitor); - - if (!qFuzzyCompare(lowWarning, d->memoryLowWarning) || !qFuzzyCompare(criticalWarning, d->memoryCriticalWarning)) { - d->memoryLowWarning = lowWarning; - d->memoryCriticalWarning = criticalWarning; - if (!d->memoryWatcher) { - d->memoryWatcher = new MemoryWatcher(this); - connect(d->memoryWatcher, &MemoryWatcher::memoryLow, - ApplicationManager::instance(), &ApplicationManager::memoryLowWarning); - connect(d->memoryWatcher, &MemoryWatcher::memoryCritical, - ApplicationManager::instance(), &ApplicationManager::memoryCriticalWarning); - } - d->memoryWatcher->setThresholds(lowWarning, criticalWarning); - return d->memoryWatcher->startWatching(); - } - return true; -} - -/*! - \qmlmethod real SystemMonitor::memoryLowWarningThreshold() - - Returns the current threshold in percent, when a low memory warning will be sent to - applications. - - \sa setMemoryWarningThresholds() -*/ -qreal SystemMonitor::memoryLowWarningThreshold() const -{ - Q_D(const SystemMonitor); - - return d->memoryLowWarning; -} - -/*! - \qmlmethod real SystemMonitor::memoryCriticalWarningThreshold() - - Returns the current threshold in percent, when a critical memory warning will be sent to - applications. - - \sa setMemoryWarningThresholds() -*/ -qreal SystemMonitor::memoryCriticalWarningThreshold() const -{ - Q_D(const SystemMonitor); - - return d->memoryCriticalWarning; -} - -void SystemMonitor::setIdleLoadThreshold(qreal loadThreshold) -{ - Q_D(SystemMonitor); - - if (!qFuzzyCompare(loadThreshold, d->idleThreshold)) { - d->idleThreshold = loadThreshold; - emit idleLoadThresholdChanged(loadThreshold); - } -} - -qreal SystemMonitor::idleLoadThreshold() const -{ - Q_D(const SystemMonitor); - - return d->idleThreshold; -} - -bool SystemMonitor::isIdle() const -{ - Q_D(const SystemMonitor); - - return d->isIdle; -} - -void SystemMonitor::setMemoryReportingEnabled(bool enabled) -{ - Q_D(SystemMonitor); - - if (enabled != d->reportMem) { - d->reportMem = enabled; - d->setupTimer(); - emit memoryReportingEnabledChanged(); - } -} - -bool SystemMonitor::isMemoryReportingEnabled() const -{ - Q_D(const SystemMonitor); - - return d->reportMem; -} - -void SystemMonitor::setCpuLoadReportingEnabled(bool enabled) -{ - Q_D(SystemMonitor); - - if (enabled != d->reportCpu) { - d->reportCpu = enabled; - d->setupTimer(); - emit cpuLoadReportingEnabledChanged(); - } -} - -bool SystemMonitor::isCpuLoadReportingEnabled() const -{ - Q_D(const SystemMonitor); - - return d->reportCpu; -} - -void SystemMonitor::setGpuLoadReportingEnabled(bool enabled) -{ - Q_D(SystemMonitor); - - if (enabled != d->reportGpu) { - d->reportGpu = enabled; - d->setupTimer(); - d->gpu->setActive(enabled); - emit gpuLoadReportingEnabledChanged(); - } -} - -bool SystemMonitor::isGpuLoadReportingEnabled() const -{ - Q_D(const SystemMonitor); - - return d->reportGpu; -} - -/*! - \qmlmethod bool SystemMonitor::addIoLoadReporting(string deviceName); - - Registers the device with the name \a deviceName for periodically reporting its I/O load. - - \note Currently this is only supported on Linux: the \a deviceName has to match one of the - devices in the \c /sys/block directory. - - Returns true, if the device could be added, otherwise false (e.g. if it does not exist). -*/ -bool SystemMonitor::addIoLoadReporting(const QString &deviceName) -{ - Q_D(SystemMonitor); - - if (!QFile::exists(qSL("/dev/") + deviceName)) - return false; - if (d->ioHash.contains(deviceName)) - return false; - - IoReader *ior = new IoReader(deviceName.toLocal8Bit().constData()); - d->ioHash.insert(deviceName, ior); - if (d->reportingInterval >= 0) - ior->readLoadValue(); // for initialization only - d->setupTimer(); - return true; -} - -/*! - \qmlmethod SystemMonitor::removeIoLoadReporting(string deviceName); - - Remove the device with the name \a deviceName from the list of monitored devices. -*/ -void SystemMonitor::removeIoLoadReporting(const QString &deviceName) -{ - Q_D(SystemMonitor); - - delete d->ioHash.take(deviceName); -} - -/*! - \qmlmethod list<string> SystemMonitor::ioLoadReportingDevices() - - Returns a list of registered device names, that will report their I/O load. -*/ -QStringList SystemMonitor::ioLoadReportingDevices() const -{ - Q_D(const SystemMonitor); - - return d->ioHash.keys(); -} - -void SystemMonitor::setFpsReportingEnabled(bool enabled) -{ - Q_D(SystemMonitor); - - if (enabled != d->reportFps) { - d->reportFps = enabled; - d->setupTimer(); - d->setupFpsReporting(); - emit fpsReportingEnabledChanged(); - } -} - -bool SystemMonitor::isFpsReportingEnabled() const -{ - Q_D(const SystemMonitor); - - return d->reportFps; -} - -void SystemMonitor::setReportingInterval(int intervalInMSec) -{ - Q_D(SystemMonitor); - d->setReportingInterval(intervalInMSec); -} - -void SystemMonitorPrivate::setReportingInterval(int intervalInMSec) -{ - Q_Q(SystemMonitor); - - if (reportingInterval == intervalInMSec) - return; // NOOP - - if (intervalInMSec <= 0) { - qmlWarning(q) << "Cannot set a reportingInterval <= 0"; - return; - } - - for (auto it = ioHash.cbegin(); it != ioHash.cend(); ++it) - it.value()->readLoadValue(); // for initialization only - updateModel(true); - reportingInterval = intervalInMSec; - if (reportingTimerId) { - killTimer(reportingTimerId); - reportingTimerId = 0; - } - setupTimer(); - emit q->reportingIntervalChanged(intervalInMSec); -} - -int SystemMonitor::reportingInterval() const -{ - Q_D(const SystemMonitor); - - return d->reportingInterval; -} - -void SystemMonitor::setCount(int count) -{ - Q_D(SystemMonitor); - - if (count != d->count) { - d->count = count > 2 ? count : 2; - d->updateModel(false); - emit countChanged(); - } -} - -int SystemMonitor::count() const -{ - Q_D(const SystemMonitor); - - return d->count; -} - -/*! \internal - report a frame swap for any window. \a item is \c 0 for the System-UI -*/ -void SystemMonitor::reportFrameSwap() -{ - Q_D(SystemMonitor); - - if (!d->reportFps) - return; - - FrameTimer *frameTimer = d->frameTimer.value(nullptr); - if (!frameTimer) { - frameTimer = new FrameTimer(); - d->frameTimer.insert(nullptr, frameTimer); - } - - frameTimer->newFrame(); -} - -QT_END_NAMESPACE_AM diff --git a/src/monitor-lib/systemmonitor.h b/src/monitor-lib/systemmonitor.h deleted file mode 100644 index 67c5c7f4..00000000 --- a/src/monitor-lib/systemmonitor.h +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:LGPL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: LGPL-3.0 -** -****************************************************************************/ - -#pragma once - -#include <QAbstractListModel> -#include <QtAppManCommon/global.h> - -QT_FORWARD_DECLARE_CLASS(QQmlEngine) -QT_FORWARD_DECLARE_CLASS(QJSEngine) - -QT_BEGIN_NAMESPACE_AM - -class SystemMonitorPrivate; -class XProcessMonitor; - -class SystemMonitor : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) - Q_PROPERTY(int reportingInterval READ reportingInterval WRITE setReportingInterval NOTIFY reportingIntervalChanged) - Q_PROPERTY(qreal idleLoadThreshold READ idleLoadThreshold WRITE setIdleLoadThreshold NOTIFY idleLoadThresholdChanged) - Q_PROPERTY(quint64 totalMemory READ totalMemory CONSTANT) - Q_PROPERTY(quint64 memoryUsed READ memoryUsed NOTIFY memoryReportingChanged) - Q_PROPERTY(int cpuCores READ cpuCores CONSTANT) - Q_PROPERTY(qreal cpuLoad READ cpuLoad NOTIFY cpuLoadReportingChanged) - Q_PROPERTY(qreal gpuLoad READ gpuLoad NOTIFY gpuLoadReportingChanged) - Q_PROPERTY(bool memoryReportingEnabled READ isMemoryReportingEnabled WRITE setMemoryReportingEnabled NOTIFY memoryReportingEnabledChanged) - Q_PROPERTY(bool cpuLoadReportingEnabled READ isCpuLoadReportingEnabled WRITE setCpuLoadReportingEnabled NOTIFY cpuLoadReportingEnabledChanged) - Q_PROPERTY(bool gpuLoadReportingEnabled READ isGpuLoadReportingEnabled WRITE setGpuLoadReportingEnabled NOTIFY gpuLoadReportingEnabledChanged) - Q_PROPERTY(bool fpsReportingEnabled READ isFpsReportingEnabled WRITE setFpsReportingEnabled NOTIFY fpsReportingEnabledChanged) - Q_PROPERTY(bool idle READ isIdle NOTIFY idleChanged) - -public: - SystemMonitor(); - ~SystemMonitor() override; - - // the item model part - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role) const override; - QHash<int, QByteArray> roleNames() const override; - - void setCount(int count); - int count() const; - - Q_INVOKABLE QVariantMap get(int index) const; - - quint64 totalMemory() const; - quint64 memoryUsed() const; - int cpuCores() const; - qreal cpuLoad() const; - qreal gpuLoad() const; - - void setIdleLoadThreshold(qreal loadThreshold); - qreal idleLoadThreshold() const; - - bool isIdle() const; - - Q_INVOKABLE bool setMemoryWarningThresholds(qreal lowWarning, qreal criticalWarning); - Q_INVOKABLE qreal memoryLowWarningThreshold() const; - Q_INVOKABLE qreal memoryCriticalWarningThreshold() const; - - void setMemoryReportingEnabled(bool enabled); - bool isMemoryReportingEnabled() const; - - void setCpuLoadReportingEnabled(bool enabled); - bool isCpuLoadReportingEnabled() const; - - void setGpuLoadReportingEnabled(bool enabled); - bool isGpuLoadReportingEnabled() const; - - Q_INVOKABLE bool addIoLoadReporting(const QString &deviceName); - Q_INVOKABLE void removeIoLoadReporting(const QString &deviceName); - Q_INVOKABLE QStringList ioLoadReportingDevices() const; - - void setFpsReportingEnabled(bool enabled); - bool isFpsReportingEnabled() const; - - void setReportingInterval(int intervalInMSec); - int reportingInterval() const; - - // semi-public API: used for the WindowManager to report FPS - void reportFrameSwap(); - -signals: - void countChanged(); - void idleChanged(bool idle); - void reportingIntervalChanged(int reportingInterval); - void idleLoadThresholdChanged(qreal idleLoadThreshold); - - void memoryReportingChanged(quint64 used); - void cpuLoadReportingChanged(qreal load); - void gpuLoadReportingChanged(qreal load); - void ioLoadReportingChanged(const QString &device, qreal load); - void fpsReportingChanged(qreal average, qreal minimum, qreal maximum, qreal jitter); - - void memoryReportingEnabledChanged(); - void cpuLoadReportingEnabledChanged(); - void gpuLoadReportingEnabledChanged(); - void fpsReportingEnabledChanged(); - -private: - SystemMonitorPrivate *d_ptr; - Q_DECLARE_PRIVATE(SystemMonitor) -}; - -QT_END_NAMESPACE_AM diff --git a/src/src.pro b/src/src.pro index 6c82b8e0..480a19ab 100644 --- a/src/src.pro +++ b/src/src.pro @@ -54,7 +54,7 @@ main_lib.depends = shared_main_lib manager_lib installer_lib window_lib monitor_ } tools_launcher_qml.subdir = tools/launcher-qml -tools_launcher_qml.depends = launcher_lib plugin_interfaces +tools_launcher_qml.depends = launcher_lib plugin_interfaces monitor_lib tools_appman.subdir = tools/appman tools_appman.depends = main_lib diff --git a/src/tools/launcher-qml/launcher-qml.cpp b/src/tools/launcher-qml/launcher-qml.cpp index f71fe9c8..7fc88ac2 100644 --- a/src/tools/launcher-qml/launcher-qml.cpp +++ b/src/tools/launcher-qml/launcher-qml.cpp @@ -91,6 +91,14 @@ #include "qml-utilities.h" #include "launcher-qml_p.h" +// monitor-lib +#include "cpustatus.h" +#include "frametimer.h" +#include "gpustatus.h" +#include "iostatus.h" +#include "memorystatus.h" +#include "monitormodel.h" + QT_USE_NAMESPACE_AM @@ -174,6 +182,14 @@ Controller::Controller(LauncherMain *a, bool quickLaunched, const QString &direc qmlRegisterType<QmlNotification>("QtApplicationManager", 1, 0, "Notification"); qmlRegisterType<QmlApplicationInterfaceExtension>("QtApplicationManager.Application", 1, 0, "ApplicationInterfaceExtension"); + // monitor-lib + qmlRegisterType<CpuStatus>("QtApplicationManager", 1, 0, "CpuStatus"); + qmlRegisterType<FrameTimer>("QtApplicationManager", 1, 0, "FrameTimer"); + qmlRegisterType<GpuStatus>("QtApplicationManager", 1, 0, "GpuStatus"); + qmlRegisterType<IoStatus>("QtApplicationManager", 1, 0, "IoStatus"); + qmlRegisterType<MemoryStatus>("QtApplicationManager", 1, 0, "MemoryStatus"); + qmlRegisterType<MonitorModel>("QtApplicationManager", 1, 0, "MonitorModel"); + m_configuration = a->runtimeConfiguration(); QString absolutePath; diff --git a/src/tools/launcher-qml/launcher-qml.pro b/src/tools/launcher-qml/launcher-qml.pro index 6053c5d8..dc246df2 100644 --- a/src/tools/launcher-qml/launcher-qml.pro +++ b/src/tools/launcher-qml/launcher-qml.pro @@ -7,6 +7,7 @@ QT = qml dbus core-private !headless:QT += quick gui gui-private quick-private QT *= \ appman_common-private \ + appman_monitor-private \ appman_notification-private \ appman_application-private \ appman_plugininterfaces-private \ diff --git a/tests/manual/systemmonitor/PropertyField.qml b/tests/manual/monitormodel/PropertyField.qml index cf1511f6..cf1511f6 100644 --- a/tests/manual/systemmonitor/PropertyField.qml +++ b/tests/manual/monitormodel/PropertyField.qml diff --git a/tests/manual/systemmonitor/README b/tests/manual/monitormodel/README index 869b9538..8bc137f7 100644 --- a/tests/manual/systemmonitor/README +++ b/tests/manual/monitormodel/README @@ -1,4 +1,4 @@ -This is a playground for manually experimenting with the SystemMonitor component and seeing how it behaves. +This is a playground for manually experimenting with the MonitorModel component and seeing how it behaves. A good way to find bugs and doing some sanity checking. TODO: It could probably later be used by qmlauto tests as a way to ensure it's always kept in a healthy state diff --git a/tests/manual/systemmonitor/SystemMonitorChart.qml b/tests/manual/monitormodel/SystemMonitorChart.qml index 59f7da5a..59f7da5a 100644 --- a/tests/manual/systemmonitor/SystemMonitorChart.qml +++ b/tests/manual/monitormodel/SystemMonitorChart.qml diff --git a/tests/manual/systemmonitor/main.qml b/tests/manual/monitormodel/main.qml index 59af386d..50fedbb3 100644 --- a/tests/manual/systemmonitor/main.qml +++ b/tests/manual/monitormodel/main.qml @@ -53,12 +53,60 @@ Window { Pane { anchors.fill: parent - SystemMonitor { - id: systemMonitor - cpuLoadReportingEnabled: cpuLoadReportingSwitch.checked - memoryReportingEnabled: memoryReportingSwitch.checked - gpuLoadReportingEnabled: gpuLoadReportingSwitch.checked - fpsReportingEnabled: fpsReportingSwitch.checked + MonitorModel { + id: monitorModel + + running: runningSwitch.checked + + // Data sources from QtApplicationManager + CpuStatus { id: cpuStatus } + MemoryStatus { id: memoryStatus } + IoStatus { + id: ioStatus + deviceNames: "sda" + } + + // A custom data source, with two roles + QtObject { + id: fooBarStatus + property var roleNames: ["foo", "bar"] + + function update() { + foo += 1; + + if (_up) { + bar += 1; + if (bar == 10) + _up = false; + } else { + bar -= 1; + if (bar == 0) + _up = true; + } + } + property int foo: 0 + property int bar: 10 + property bool _up: false + } + + // Yet another custom data source + QtObject { + id: alphaStatus + property var roleNames: ["alpha"] + function update() { + if (_up) { + alpha *= 2; + if (alpha == 1024) + _up = false; + } else { + alpha /= 2; + if (alpha == 2) + _up = true; + } + } + property real alpha: 2 + property bool _up: true + } } ScrollView { @@ -69,17 +117,14 @@ Window { anchors.left: parent.left ColumnLayout { - PropertyField { name: "count"; object: systemMonitor } - PropertyField { name: "reportingInterval"; object: systemMonitor } - PropertyField { name: "idleLoadThreshold"; object: systemMonitor } + PropertyField { name: "maximumCount"; object: monitorModel } + PropertyField { name: "interval"; object: monitorModel } RowLayout { ComboBox { id: rolesCombo - // TODO: ioLoad Layout.fillWidth: true - model: [ "cpuLoad", "gpuLoad", "memoryUsed", "averageFps", "minimumFps", - "maximumFps", "fpsJitter" ] + model: [ "cpuLoad", "memoryUsed", "foo", "bar", "alpha" ] } Button { @@ -91,36 +136,20 @@ Window { } Switch { - id: cpuLoadReportingSwitch - text: "cpuLoadReportingEnabled" + id: runningSwitch + text: "running" checked: false } - Switch { - id: memoryReportingSwitch - text: "memoryReportingEnabled" - checked: false - } - - Switch { - id: gpuLoadReportingSwitch - text: "gpuLoadReportingEnabled" - checked: false - } - - Switch { - id: fpsReportingSwitch - text: "fpsReportingEnabled" - checked: false - } + Button { text: "Clear"; onClicked: monitorModel.clear() } - Label { text: "cpuLoad: " + systemMonitor.cpuLoad } - Label { text: "totalMemory: " + systemMonitor.totalMemory } - Label { text: "memoryUsed: " + systemMonitor.memoryUsed } - Label { text: "cpuCores: " + systemMonitor.cpuCores } - Label { text: "cpuLoad: " + systemMonitor.cpuLoad } - Label { text: "gpuLoad: " + systemMonitor.gpuLoad } - Label { text: "idle: " + systemMonitor.idle } + Label { text: "cpuLoad: " + cpuStatus.cpuLoad } + Label { text: "totalMemory: " + memoryStatus.totalMemory } + Label { text: "memoryUsed: " + memoryStatus.memoryUsed } + Label { text: "ioLoad.sda: " + ioStatus.ioLoad.sda } + Label { text: "foo: " + fooBarStatus.foo } + Label { text: "bar: " + fooBarStatus.bar } + Label { text: "alpha: " + alphaStatus.alpha } } } @@ -135,7 +164,7 @@ Window { delegate: SystemMonitorChart { Layout.fillWidth: true Layout.fillHeight: true - sourceModel: systemMonitor + sourceModel: monitorModel role: model.role onRemoveClicked: chartsModel.remove(model.index) } diff --git a/tests/processmonitor/processmonitor.pro b/tests/processmonitor/processmonitor.pro deleted file mode 100644 index b7425e06..00000000 --- a/tests/processmonitor/processmonitor.pro +++ /dev/null @@ -1,11 +0,0 @@ -TARGET = tst_processmonitor - -include($$PWD/../tests.pri) - -QT *= appman_monitor-private \ - appman_manager-private \ - appman_window-private \ - appman_application-private \ - appman_common-private - -SOURCES += tst_processmonitor.cpp diff --git a/tests/processmonitor/tst_processmonitor.cpp b/tests/processmonitor/tst_processmonitor.cpp deleted file mode 100644 index 7696aeeb..00000000 --- a/tests/processmonitor/tst_processmonitor.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore> -#include <QtTest> -#include "private/processmonitor_p.h" - -QT_USE_NAMESPACE_AM - -class ReadingTaskTester : public ReadingTask -{ -public: - using ReadingTask::readMemory; -}; - -class tst_ProcessMonitor : public QObject -{ - Q_OBJECT - -public: - tst_ProcessMonitor(); - -private slots: - void memInvalid_data(); - void memInvalid(); - void memTestProcess(); - void memBasic(); - void memAdvanced(); - -private: - void printMem(const ReadingTask::Results::Memory &memres); - - ReadingTask task; - ReadingTask::Results readResults; - QMutex mutex; -}; - -tst_ProcessMonitor::tst_ProcessMonitor() : task(mutex, readResults) -{} - -void tst_ProcessMonitor::memInvalid_data() -{ - QTest::addColumn<QString>("file"); - - QTest::newRow("arbitrary") << QFINDTESTDATA("tst_processmonitor.cpp"); - QTest::newRow("binary") << QFINDTESTDATA("tst_processmonitor"); - QTest::newRow("missingvalue") << QFINDTESTDATA("invalid.smaps"); -} - -void tst_ProcessMonitor::memInvalid() -{ - QFETCH(QString, file); - - ReadingTask::Results::Memory memres; - QVERIFY(!static_cast<ReadingTaskTester*>(&task)->readMemory(file.toLocal8Bit(), memres)); - QCOMPARE(memres.totalVm, 0u); - QCOMPARE(memres.totalRss, 0u); - QCOMPARE(memres.totalPss, 0u); - QCOMPARE(memres.textVm, 0u); - QCOMPARE(memres.textRss, 0u); - QCOMPARE(memres.textPss, 0u); - QCOMPARE(memres.heapVm, 0u); - QCOMPARE(memres.heapRss, 0u); - QCOMPARE(memres.heapPss, 0u); -} - -void tst_ProcessMonitor::memTestProcess() -{ - ReadingTask::Results::Memory memres; - const QByteArray file = "/proc/" + QByteArray::number(QCoreApplication::applicationPid()) + "/smaps"; - QVERIFY(static_cast<ReadingTaskTester*>(&task)->readMemory(file, memres)); - //printMem(memres); - QVERIFY(memres.totalVm >= memres.totalRss); - QVERIFY(memres.totalRss >= memres.totalPss); - QVERIFY(memres.textVm >= memres.textRss); - QVERIFY(memres.textRss >= memres.textPss); - QVERIFY(memres.heapVm >= memres.heapRss); - QVERIFY(memres.heapRss >= memres.heapPss); -} - -void tst_ProcessMonitor::memBasic() -{ - ReadingTask::Results::Memory memres; - QVERIFY(static_cast<ReadingTaskTester*>(&task)->readMemory(QFINDTESTDATA("basic.smaps").toLocal8Bit(), memres)); - //printMem(memres); - QCOMPARE(memres.totalVm, 107384u); - QCOMPARE(memres.totalRss, 20352u); - QCOMPARE(memres.totalPss, 13814u); - QCOMPARE(memres.textVm, 7800u); - QCOMPARE(memres.textRss, 5884u); - QCOMPARE(memres.textPss, 2318u); - QCOMPARE(memres.heapVm, 24376u); - QCOMPARE(memres.heapRss, 7556u); - QCOMPARE(memres.heapPss, 7556u); -} - -void tst_ProcessMonitor::memAdvanced() -{ - ReadingTask::Results::Memory memres; - QVERIFY(static_cast<ReadingTaskTester*>(&task)->readMemory(QFINDTESTDATA("advanced.smaps").toLocal8Bit(), memres)); - //printMem(memres); - QCOMPARE(memres.totalVm, 77728u); - QCOMPARE(memres.totalRss, 17612u); - QCOMPARE(memres.totalPss, 17547u); - QCOMPARE(memres.textVm, 2104u); - QCOMPARE(memres.textRss, 1772u); - QCOMPARE(memres.textPss, 1707u); - QCOMPARE(memres.heapVm, 16032u); - QCOMPARE(memres.heapRss, 15740u); - QCOMPARE(memres.heapPss, 15740u); -} - -void tst_ProcessMonitor::printMem(const ReadingTask::Results::Memory &memres) -{ - qDebug() << "totalVm:" << memres.totalVm; - qDebug() << "totalRss:" << memres.totalRss; - qDebug() << "totalPss:" << memres.totalPss; - qDebug() << "textVm:" << memres.textVm; - qDebug() << "textRss:" << memres.textRss; - qDebug() << "textPss:" << memres.textPss; - qDebug() << "heapVm:" << memres.heapVm; - qDebug() << "heapRss:" << memres.heapRss; - qDebug() << "heapPss:" << memres.heapPss; -} - -QTEST_APPLESS_MAIN(tst_ProcessMonitor) - -#include "tst_processmonitor.moc" diff --git a/tests/processmonitor/advanced.smaps b/tests/processreader/advanced.smaps index 61212565..61212565 100644 --- a/tests/processmonitor/advanced.smaps +++ b/tests/processreader/advanced.smaps diff --git a/tests/processmonitor/basic.smaps b/tests/processreader/basic.smaps index 00640230..00640230 100644 --- a/tests/processmonitor/basic.smaps +++ b/tests/processreader/basic.smaps diff --git a/tests/processmonitor/invalid.smaps b/tests/processreader/invalid.smaps index 146a42b8..146a42b8 100644 --- a/tests/processmonitor/invalid.smaps +++ b/tests/processreader/invalid.smaps diff --git a/tests/systemmonitor/systemmonitor.pro b/tests/processreader/processreader.pro index c3d6a914..c5caa674 100644 --- a/tests/systemmonitor/systemmonitor.pro +++ b/tests/processreader/processreader.pro @@ -1,4 +1,4 @@ -TARGET = tst_systemmonitor +TARGET = tst_processreader include($$PWD/../tests.pri) @@ -8,4 +8,4 @@ QT *= appman_monitor-private \ appman_application-private \ appman_common-private -SOURCES += tst_systemmonitor.cpp +SOURCES += tst_processreader.cpp diff --git a/tests/processreader/tst_processreader.cpp b/tests/processreader/tst_processreader.cpp new file mode 100644 index 00000000..9cfa7df1 --- /dev/null +++ b/tests/processreader/tst_processreader.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore> +#include <QtTest> +#include <QtAppManMonitor/processreader.h> + +QT_USE_NAMESPACE_AM + +class tst_ProcessReader : public QObject +{ + Q_OBJECT + +public: + tst_ProcessReader(); + +private slots: + void memInvalid_data(); + void memInvalid(); + void memTestProcess(); + void memBasic(); + void memAdvanced(); + +private: + void printMem(const ProcessReader &reader); + ProcessReader reader; +}; + +tst_ProcessReader::tst_ProcessReader() +{} + +void tst_ProcessReader::memInvalid_data() +{ + QTest::addColumn<QString>("file"); + + QTest::newRow("arbitrary") << QFINDTESTDATA("tst_processreader.cpp"); + QTest::newRow("binary") << QFINDTESTDATA("tst_processreader"); + QTest::newRow("missingvalue") << QFINDTESTDATA("invalid.smaps"); +} + +void tst_ProcessReader::memInvalid() +{ + QFETCH(QString, file); + + reader.readSmaps(file.toLocal8Bit()); + + QCOMPARE(reader.totalVm.load(), 0u); + QCOMPARE(reader.totalRss.load(), 0u); + QCOMPARE(reader.totalPss.load(), 0u); + QCOMPARE(reader.textVm.load(), 0u); + QCOMPARE(reader.textRss.load(), 0u); + QCOMPARE(reader.textPss.load(), 0u); + QCOMPARE(reader.heapVm.load(), 0u); + QCOMPARE(reader.heapRss.load(), 0u); + QCOMPARE(reader.heapPss.load(), 0u); +} + +void tst_ProcessReader::memTestProcess() +{ + const QByteArray file = "/proc/" + QByteArray::number(QCoreApplication::applicationPid()) + "/smaps"; + + QVERIFY(reader.readSmaps(file)); + //printMem(reader); + QVERIFY(reader.totalVm.load() >= reader.totalRss.load()); + QVERIFY(reader.totalRss.load() >= reader.totalPss.load()); + QVERIFY(reader.textVm.load() >= reader.textRss.load()); + QVERIFY(reader.textRss.load() >= reader.textPss.load()); + QVERIFY(reader.heapVm.load() >= reader.heapRss.load()); + QVERIFY(reader.heapRss.load() >= reader.heapPss.load()); +} + +void tst_ProcessReader::memBasic() +{ + QVERIFY(reader.readSmaps(QFINDTESTDATA("basic.smaps").toLocal8Bit())); + //printMem(reader); + QCOMPARE(reader.totalVm.load(), 107384u); + QCOMPARE(reader.totalRss.load(), 20352u); + QCOMPARE(reader.totalPss.load(), 13814u); + QCOMPARE(reader.textVm.load(), 7800u); + QCOMPARE(reader.textRss.load(), 5884u); + QCOMPARE(reader.textPss.load(), 2318u); + QCOMPARE(reader.heapVm.load(), 24376u); + QCOMPARE(reader.heapRss.load(), 7556u); + QCOMPARE(reader.heapPss.load(), 7556u); +} + +void tst_ProcessReader::memAdvanced() +{ + QVERIFY(reader.readSmaps(QFINDTESTDATA("advanced.smaps").toLocal8Bit())); + //printMem(reader); + QCOMPARE(reader.totalVm.load(), 77728u); + QCOMPARE(reader.totalRss.load(), 17612u); + QCOMPARE(reader.totalPss.load(), 17547u); + QCOMPARE(reader.textVm.load(), 2104u); + QCOMPARE(reader.textRss.load(), 1772u); + QCOMPARE(reader.textPss.load(), 1707u); + QCOMPARE(reader.heapVm.load(), 16032u); + QCOMPARE(reader.heapRss.load(), 15740u); + QCOMPARE(reader.heapPss.load(), 15740u); +} + +void tst_ProcessReader::printMem(const ProcessReader &reader) +{ + qDebug() << "totalVm:" << reader.totalVm.load(); + qDebug() << "totalRss:" << reader.totalRss.load(); + qDebug() << "totalPss:" << reader.totalPss.load(); + qDebug() << "textVm:" << reader.textVm.load(); + qDebug() << "textRss:" << reader.textRss.load(); + qDebug() << "textPss:" << reader.textPss.load(); + qDebug() << "heapVm:" << reader.heapVm.load(); + qDebug() << "heapRss:" << reader.heapRss.load(); + qDebug() << "heapPss:" << reader.heapPss.load(); +} + +QTEST_APPLESS_MAIN(tst_ProcessReader) + +#include "tst_processreader.moc" diff --git a/tests/systemmonitor/tst_systemmonitor.cpp b/tests/systemmonitor/tst_systemmonitor.cpp deleted file mode 100644 index 48918bfb..00000000 --- a/tests/systemmonitor/tst_systemmonitor.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Pelagicore AG -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Pelagicore Application Manager. -** -** $QT_BEGIN_LICENSE:LGPL-QTAS$ -** Commercial License Usage -** Licensees holding valid commercial Qt Automotive Suite licenses may use -** this file in accordance with the commercial license agreement provided -** with the Software or, alternatively, in accordance with the terms -** contained in a written agreement between you and The Qt Company. For -** licensing terms and conditions see https://www.qt.io/terms-conditions. -** For further information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -** SPDX-License-Identifier: LGPL-3.0 -** -****************************************************************************/ - -#include <QQuickView> -#include <QtCore> -#include <QtTest> - -#include "windowmanager.h" -#include "systemmonitor.h" -#include <QtAppManMonitor/private/systemmonitor_p.h> - -QT_USE_NAMESPACE_AM - -class tst_SystemMonitor : public QObject -{ - Q_OBJECT - -public: - tst_SystemMonitor(); - virtual ~tst_SystemMonitor(); - -private slots: - void reportingTimer_data(); - void reportingTimer(); -private: - static bool getBooleanProperty(QObject *obj, QString propName); - static QMetaProperty getProperty(QObject *obj, QString propName); - static void setBooleanProperty(QObject *obj, QString propName, bool value); - QQuickView *view; -}; - -tst_SystemMonitor::tst_SystemMonitor() -{ - view = new QQuickView; - WindowManager::createInstance(view->engine(), QString("foo")); -} - -tst_SystemMonitor::~tst_SystemMonitor() -{ - delete view; -} - -QMetaProperty tst_SystemMonitor::getProperty(QObject *obj, QString propName) -{ - const QMetaObject *metaObj = obj->metaObject(); - int idx = metaObj->indexOfProperty(propName.toLocal8Bit()); - Q_ASSERT(idx != -1); - - return metaObj->property(idx); -} - -bool tst_SystemMonitor::getBooleanProperty(QObject *obj, QString propName) -{ - QMetaProperty property = getProperty(obj, propName); - QVariant result = property.read(obj); - Q_ASSERT(result.type() == QVariant::Bool); - return result.toBool(); -} - -void tst_SystemMonitor::setBooleanProperty(QObject *obj, QString propName, bool value) -{ - QMetaProperty property = getProperty(obj, propName); - bool ok = property.write(obj, QVariant(value)); - Q_ASSERT(ok); -} - -void tst_SystemMonitor::reportingTimer_data() -{ - QTest::addColumn<QString>("reportingEnabled"); - - QTest::newRow("memory") << QString("memoryReportingEnabled"); - QTest::newRow("cpuLoad") << QString("cpuLoadReportingEnabled"); - QTest::newRow("gpuLoad") << QString("gpuLoadReportingEnabled"); - QTest::newRow("fps") << QString("fpsReportingEnabled"); -} - -/* - Tests that the reportingTimer is only active while there's at least - one reporting category enabled. - */ -void tst_SystemMonitor::reportingTimer() -{ - QFETCH(QString, reportingEnabled); - - SystemMonitor sysMon; - auto sysMonPriv = SystemMonitorPrivate::get(&sysMon); - - sysMon.setReportingInterval(50); - - QCOMPARE(getBooleanProperty(&sysMon, reportingEnabled), false); - QCOMPARE(sysMonPriv->reportingTimerId, 0); - - setBooleanProperty(&sysMon, reportingEnabled, true); - - QVERIFY(sysMonPriv->reportingTimerId > 0); - - setBooleanProperty(&sysMon, reportingEnabled, false); - - QCOMPARE(sysMonPriv->reportingTimerId, 0); -} - -QTEST_MAIN(tst_SystemMonitor) - -#include "tst_systemmonitor.moc" diff --git a/tests/tests.pro b/tests/tests.pro index c8e93636..75b7039e 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -18,8 +18,7 @@ SUBDIRS = \ linux*:SUBDIRS += \ sudo \ - processmonitor \ - systemmonitor \ + processreader \ systemreader \ OTHER_FILES += \ |