Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. QML Application slowly increases CPU usage over time to 100% and becomes unresponsive
Qt 6.11 is out! See what's new in the release blog

QML Application slowly increases CPU usage over time to 100% and becomes unresponsive

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
12 Posts 4 Posters 2.6k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • E Offline
    E Offline
    egbiomated
    wrote on last edited by
    #1

    I've got a QML based application that runs fine for about 12 hours but when I leave it to run overnight the application's UI is no longer responsive to the touchscreen. If i SSH in i can see that on one thread the CPU load is 100%. However the Application still shows updating values on the UI. I have the application polling a sensor in a separate worker thread every ~5s and that appears to be working no issue.

    When I profiled the QML app I saw that a lot of the time ~60% was spent servicing events related to the Keyboard.
    3882b66a-1d40-4fe4-83e0-dbd36b5678e6-image.png
    576e4d9a-5b97-4db4-9b41-788306f717eb-image.png

    I'm a bit new to Embedded GUI development and Qt so i was hoping to get some pointers in the right direction. I can't really post the source code unfortunately. Below are screenshots of HTOP output

    HTOP: Start of Application
    d5f79ca6-bacf-43ae-bdab-6f21bde978d5-image.png

    HTOP: End of Application
    e3b114d2-e0a2-4ea8-bed7-7c21714f64e6-image.png

    J.HilkJ 1 Reply Last reply
    0
    • E egbiomated

      I've got a QML based application that runs fine for about 12 hours but when I leave it to run overnight the application's UI is no longer responsive to the touchscreen. If i SSH in i can see that on one thread the CPU load is 100%. However the Application still shows updating values on the UI. I have the application polling a sensor in a separate worker thread every ~5s and that appears to be working no issue.

      When I profiled the QML app I saw that a lot of the time ~60% was spent servicing events related to the Keyboard.
      3882b66a-1d40-4fe4-83e0-dbd36b5678e6-image.png
      576e4d9a-5b97-4db4-9b41-788306f717eb-image.png

      I'm a bit new to Embedded GUI development and Qt so i was hoping to get some pointers in the right direction. I can't really post the source code unfortunately. Below are screenshots of HTOP output

      HTOP: Start of Application
      d5f79ca6-bacf-43ae-bdab-6f21bde978d5-image.png

      HTOP: End of Application
      e3b114d2-e0a2-4ea8-bed7-7c21714f64e6-image.png

      J.HilkJ Offline
      J.HilkJ Offline
      J.Hilk
      Moderators
      wrote on last edited by
      #2

      @egbiomated are you sure, you're not creating new objects or at the very least new/additional bindings with each poll from the worker?

      Seems like the app is super busy with property signals and updates 31.9% each


      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      1 Reply Last reply
      0
      • E Offline
        E Offline
        egbiomated
        wrote on last edited by egbiomated
        #3

        @J-Hilk said in QML Application slowly increases CPU usage over time to 100% and becomes unresponsive:

        are you sure, you're not creating new objects or at the very least new/additional bindings with each poll from the worker?
        Seems like the app is super busy with property signals and updates 31.9% each

        Sorry but I don't think i fully understand how iI would be doing that. Are you referring to the main thread where my application is running my worker passes a qobject to my ApplicationData class slot which then updates it's own private variables that are in a list [QObject]. In terms of the QML:

        Isn't all that CPU time associated with the Keyboard?

        import QtQuick 2.15
        import QtQuick.Window 2.15
        import QtQuick.Controls 2.15
        import QtQuick.Controls.Material 2.15
        import QtQuick.Layouts 1.3
        
        import "."
        import QtQuick.VirtualKeyboard 2.2
        import "./lib.js" as Lib
        
        
        ApplicationWindow {
            id: window
            width: Style.windowWidth
            height: Style.windowHeight
            visible: true
        
            contentOrientation: Qt.LandscapeOrientation
            property int headerState: 0
            Timer {
                interval: 5000;
                running: true;
                repeat: true
                onTriggered: {
                    switch(headerState)
                    {
                        case 0:
                            titleLabel.text = Lib.getAuthState()
                            headerState += 1
                            break;
                        case 1:
                            titleLabel.text = applicationData.getDateTime().toString().split(' ').slice(0,5).join(' ')
                            headerState += 1
                            break;
                        case 2:
                            headerState += 1
                            break;
                        default:
                            headerState = 0
                            titleLabel.text = stackView.currentItem.title
                    }
        
                }
            }
        
            Shortcut {
                sequences: ["Esc", "Back"]
                enabled: stackView.depth > 1
                onActivated: {
                    stackView.pop()
                    listView.currentIndex = -1
                }
            }
        
            Shortcut {
                sequence: "Menu"
                onActivated: optionsMenu.open()
            }
        
            header: ToolBar {
                contentHeight: toolButton.implicitHeight
        
                transform: Rotation {
                    id: rotation
                    angle: 90
                    origin.x: 400
                    origin.y: 400
                }
        
                RowLayout {
                    // spacing: 20
                    anchors.fill: parent
                    // width: 480
        
                    ToolButton {
                        id: toolButton
                        text: stackView.depth > 1 ? "\u25C0" : "\u2630"
                        font.pixelSize: Qt.application.font.pixelSize * 1.6
                        onClicked: {
                            if (stackView.depth > 1) {
                                stackView.pop()
                            } else {
                                drawer.open()
                            }
                        }
                    }
        
                    Label {
                        id: titleLabel
                        height: parent.height
                        Layout.alignment: horizontalCenter
                        font.pixelSize: 20
                        elide: Label.ElideRight
                        horizontalAlignment: Qt.AlignHCenter
                        verticalAlignment: Qt.AlignVCenter
                        text: Lib.getAuthState()
                    }
        
                    ToolButton {
                        id: menuButton
                        text: "\u2606"
                        font.pointSize: Style.h3
                        font.bold: true
                    }
                }
            }
        
            ListModel {
                id: menuModel
                ListElement {
                    title: "Home";
                    source: "qrc:/pages/SensorList.qml";
                }
                ListElement {
                    title: "Sensor Setup";
                    source: "qrc:/pages/SensorsSetup.qml";
                    section: "Setup";
                }
                ListElement {
                    title: "Calibration";
                    source: "qrc:/pages/Calibration.qml";
                    section: "Setup";
                }
                ListElement {
                    title: "Alarm Setup";
                    source: "qrc:/pages/AlarmSetup.qml";
                    section: "Setup";
                }
                ListElement {
                    title: "Analog Outputs";
                    source: "qrc:/pages/AnalogOutputs.qml";
                    section: "Configuration" ;
                }
                ListElement {
                    title: "Relay Outputs";
                    source: "qrc:/pages/AlarmOutputs.qml";
                    section: "Configuration";
                }
                ListElement {
                    title: "Address Setup";
                    source: "qrc:/pages/SetAddress.qml";
                    section: "Configuration";
                }
                ListElement {
                    title: "Device Setup";
                    source: "qrc:/pages/DeviceSetup.qml";
                    section: "Configuration";
                }
        
        
            }
        
        
            StackView {
                width: Style.windowWidth
                height: Style.windowHeight
                id: stackView
                initialItem: "/pages/SensorList.qml"
            }
        
            //ABOUT Dialog
            Dialog {
                id: aboutDialog
                modal: true
                focus: true
                title: "Device Information"
                x: (window.width - width) / 2
                y: window.height / 6
                width: Math.min(window.width, window.height) * .9
                contentHeight: aboutColumn.height
                font.pointSize: Style.h1
                font.bold: true
                background: Rectangle {
                    width: parent.width
                    height: parent.height
                    color: "white"
                }
        
                Column {
                    id: aboutColumn
                    spacing: 20
        
                    Text {
                        width: aboutDialog.availableWidth
                        text: "Device S/N: 123209"
                        font.pixelSize: Style.h2
                    }
        
                    Text {
                        width: aboutDialog.availableWidth
                        text: "Device Uptime: 5D 3H 33M"
                        font.pixelSize: Style.h2
                    }
                }
            }
        
        
            InputPanel {
                id: inputPanel
                width: 480
                active: true
                rotation: 90
                y: 165
                x: Qt.inputMethod.visible ? -180 : -180 - inputPanel.height
            }
        
        
        }
        

        Is there something you see here that would lead you to any conclusion.

        1 Reply Last reply
        0
        • KH-219DesignK Offline
          KH-219DesignK Offline
          KH-219Design
          wrote on last edited by KH-219Design
          #4

          I have encountered performance problems with StackView in the past.

          Full disclosure: I do think that some of my struggles with StackView stem from my neglecting to actually study it properly and understand the various ways to use it.

          For my applications, it has just become easier to avoid StackView. In your case, you will need to choose whether to avoid it or whether instead to learn about it further.

          I have been able to avoid it because I was using it simply to manage a single foregrounded full-screen item at a time.

          If all you need is to create a "conceptual stack" of equally-sized items (like "pages" in your app), such that only one of them is ever visible at a time, then you can do this without a StackView. Just create each "page" to be the same size and location, but link the visible boolean to a custom expression that ensures that it only becomes true for one of the pages at a time.

          It will turn into something like the following:

              Item {
                id: front
                anchors.fill: parent
          
                property int whichScreen: 0
          
                MainMenu {
                  anchors.fill: parent
                  visible: front.whichScreen == 0
                  onGotoPitchTrain: {
                    front.whichScreen = 1
                  }
                  onGotoKeySigTrain: {
                    front.whichScreen = 2
                  }
                }
          
                PitchTrain {
                  id: pt
                  anchors.fill: parent
                  visible: front.whichScreen == 1
                }
          
                KeySigTrain {
                  id: ks
                  anchors.fill: parent
                  visible: front.whichScreen == 2
                }
              }
          
          

          www.219design.com
          Software | Electrical | Mechanical | Product Design

          E 1 Reply Last reply
          0
          • KH-219DesignK KH-219Design

            I have encountered performance problems with StackView in the past.

            Full disclosure: I do think that some of my struggles with StackView stem from my neglecting to actually study it properly and understand the various ways to use it.

            For my applications, it has just become easier to avoid StackView. In your case, you will need to choose whether to avoid it or whether instead to learn about it further.

            I have been able to avoid it because I was using it simply to manage a single foregrounded full-screen item at a time.

            If all you need is to create a "conceptual stack" of equally-sized items (like "pages" in your app), such that only one of them is ever visible at a time, then you can do this without a StackView. Just create each "page" to be the same size and location, but link the visible boolean to a custom expression that ensures that it only becomes true for one of the pages at a time.

            It will turn into something like the following:

                Item {
                  id: front
                  anchors.fill: parent
            
                  property int whichScreen: 0
            
                  MainMenu {
                    anchors.fill: parent
                    visible: front.whichScreen == 0
                    onGotoPitchTrain: {
                      front.whichScreen = 1
                    }
                    onGotoKeySigTrain: {
                      front.whichScreen = 2
                    }
                  }
            
                  PitchTrain {
                    id: pt
                    anchors.fill: parent
                    visible: front.whichScreen == 1
                  }
            
                  KeySigTrain {
                    id: ks
                    anchors.fill: parent
                    visible: front.whichScreen == 2
                  }
                }
            
            
            E Offline
            E Offline
            egbiomated
            wrote on last edited by
            #5

            @KH-219Design Do you think that the stack view would still be an issue even when the device is idling the CPU run's high when the device IDLE's over night i'm not switching between the screens that the stack view is loading all of the time. But like you said I don't have a solid understanding of its internals.

            1 Reply Last reply
            1
            • fcarneyF Offline
              fcarneyF Offline
              fcarney
              wrote on last edited by
              #6

              What is that timer for? Does speeding it up cause the problem to happen faster? Are there C++ objects being allocated as a result of things changing?

              C++ is a perfectly valid school of magic.

              E 1 Reply Last reply
              1
              • KH-219DesignK Offline
                KH-219DesignK Offline
                KH-219Design
                wrote on last edited by
                #7

                @egbiomated said in QML Application slowly increases CPU usage over time to 100% and becomes unresponsive:

                i'm not switching between the screens that the stack view is loading

                It has been over a year (possibly more than 2) since I have used StackView, so my memory has faded regarding the exact unwanted symptoms I observed and the exact triggering scenarios.

                However, if no methods on the StackView are being called and no properties on the StackView are being mutated when you encounter the unwanted CPU usage, then it does seem improbable to blame StackView in that case.

                www.219design.com
                Software | Electrical | Mechanical | Product Design

                E 1 Reply Last reply
                1
                • fcarneyF fcarney

                  What is that timer for? Does speeding it up cause the problem to happen faster? Are there C++ objects being allocated as a result of things changing?

                  E Offline
                  E Offline
                  egbiomated
                  wrote on last edited by
                  #8

                  @fcarney
                  The timer is just switching a banner in the Header of the Page from a Text String to a Date Object. The date object is generated in C++ and passed through the context

                  1 Reply Last reply
                  0
                  • KH-219DesignK KH-219Design

                    @egbiomated said in QML Application slowly increases CPU usage over time to 100% and becomes unresponsive:

                    i'm not switching between the screens that the stack view is loading

                    It has been over a year (possibly more than 2) since I have used StackView, so my memory has faded regarding the exact unwanted symptoms I observed and the exact triggering scenarios.

                    However, if no methods on the StackView are being called and no properties on the StackView are being mutated when you encounter the unwanted CPU usage, then it does seem improbable to blame StackView in that case.

                    E Offline
                    E Offline
                    egbiomated
                    wrote on last edited by
                    #9

                    @KH-219Design Properties are being updated on the item that is displayed on the StackView but the stackview itself is not changing. The page displayed.

                    Each Page in the stack view consists of a page like this there are a fixed number of sensors that I am dealing with that can be on or off at any given time so i use a listview to render all the components so the updates do happen with a fair degree of nesting

                    import QtQuick 2.12
                    import QtQuick.Window 2.12
                    import QtQuick.Controls 2.12
                    import QtQuick.Controls.Material 2.12
                    import QtQuick.Layouts 1.3
                    import "../"
                    import "../lib.js" as Lib
                    import "../components" as Components
                    
                    Page {
                       // width: window.width
                       // height: window.height
                       width: Style.windowWidth
                       height: Style.windowHeight
                       // title: qsTr("Sensors")
                       id: sensorSetupPage
                       title: qsTr("Home")
                    
                       transform: Rotation {
                           id: pageRotation
                           angle: 90
                           origin.x: 400
                           origin.y: 352.5
                       }
                    
                       Component.onCompleted: {
                           applicationData.setDeviceState(0)
                       }
                    
                       ListView {
                           id: sensorListView
                           width: parent.width
                           height: parent.height
                           model: Lib.getAvailableSensor(applicationData.sensor_list).length
                           delegate: Components.DynamicSensor {
                               width: parent.width
                               height: Math.ceil((sensorListView.height-50)/(Lib.getAvailableSensor(applicationData.sensor_list).length))
                               sensorData: applicationData.sensor_list[Lib.getAvailableSensorIndex(index)]
                               numAvailable: Lib.getAvailableSensor(applicationData.sensor_list).length
                               MouseArea {
                                   anchors.fill: parent
                                   onClicked: {
                                       // applicationData.sInFocus = Lib.getAvailableSensorIndex(index)
                                       applicationData.setSensorInfocus(Lib.getAvailableSensorIndex(index))
                                       stackView.push("qrc:/pages/SensorDetail.qml")
                                   }
                               }
                           }
                       }
                    }
                    
                    
                    import QtQuick 2.12
                    import QtQuick.Controls 2.12
                    import QtQuick.Controls.Material 2.12
                    import Qt.labs.settings 1.0
                    import "../"
                    import "../lib.js" as Lib
                    
                    Rectangle {
                        id: dynamicSensor
                        width: parent.width
                        height: parent.height
                        property var sensorData
                    
                        function getSensorUnits() {
                            if (sensorData.sensorUnits === 0)
                            {
                               return "PPM"
                            }
                            else if (sensorData.sensorUnits === 1)
                            {
                               return "PPB"
                            }
                            else
                            {
                               return "% 0<sub>2</sub>"
                    
                            }
                        }
                    
                        function parseO2Data() {
                            var units = getSensorUnits();
                            if (sensorData.o2Level > 0)
                            {
                                if (units === "PPM")
                                {
                                    return sensorData.o2Level.toFixed(3)
                                }
                                else if (units === "PPB")
                                {
                                    return sensorData.o2Level.toFixed(1)
                                }
                                else
                                {
                                    return sensorData.o2Level.toFixed(2)
                                }
                            }
                            else
                            {
                                return 0.00
                            }
                        }
                    
                        function getColor() {
                            if (sensorData.alarmTripped && !sensorData.standby && !applicationData.alarm_list[Lib.getAvailableSensorIndex(index)].ack)
                            {
                               return "#ff5252"
                            }
                            else if (sensorData.pulseCount > 900000) {
                    
                                return "yellow"
                            }
                            else
                            {
                                return "transparent"
                            }
                        }
                    
                        property string sensorName: "Sensor 1: Condensate Pump"
                        property string sensorUnits: getSensorUnits()
                        property int numAvailable: Lib.getAvailableSensor()
                        property int sensorIndex: Lib.getAvailableSensorIndex(index)
                        property bool isLast: numAvailable === (sensorIndex) ? true : false
                        property real rowSpacers: (numAvailable < 3 ? 24 : 16)
                        // color: (sensorData.alarmTripped && !sensorData.standby && !applicationData.alarm_list[Lib.getAvailableSensorIndex(index)].ack) ? "#ff5252" : "transparent"
                        color: getColor()
                    
                        function getFontSize() {
                            var baseSize =  Style.h1 + (numAvailable < 3 ? 30 : 15)
                            // if (getSensorUnits() === "PPM") {
                            //     baseSize -= 30
                            //}
                            return baseSize
                        }
                    
                        Column {
                            width: (parent.width - (Style.gutter*2))
                            leftPadding: Style.gutter
                            // height: parent.height
                            topPadding: 10
                            // spacing: 1
                    
                            Text {
                                id: sensorNameRow
                                width: parent.width
                                height: (Style.h3 + rowSpacers)
                                horizontalAlignment: Text.AlignLeft
                                verticalAlignment: Text.AlignVCenter
                                font.bold: true
                                font.underline: true
                                font.pointSize: Style.h3
                                // text: sensorName
                                text: "Sensor " + (sensorIndex + 1) + ": " + sensorData.sensorName
                                // bottomPadding: 10
                                // toPadding: 10
                            }
                    
                            Row {
                                id: oxygenUnitRow
                                width: parent.width
                                height: (Style.h3 + rowSpacers)
                                Text {
                                    width: parent.width/2
                                    height: parent.height
                                    horizontalAlignment: Text.AlignLeft
                                    verticalAlignment: Text.AlignVCenter
                                    font.bold: true
                                    font.pointSize: Style.h3
                                    text: "Dissolved 0<sub>2</sub>"
                                }
                    
                                Text {
                                    width: parent.width/2
                                    height: parent.height
                                    rightPadding: Style.gutter
                                    horizontalAlignment: Text.AlignRight
                                    verticalAlignment: Text.AlignVCenter
                                    font.bold: true
                                    font.pointSize: Style.h3
                                    text: sensorUnits
                                }
                            }
                    
                            Text {
                                    width: parent.width
                                    height: dynamicSensor.height - (sensorNameRow.height + oxygenUnitRow.height + temperatureRow.height + 10)
                                    topPadding: 5
                                    bottomPadding: 5
                                    horizontalAlignment: Text.AlignHCenter
                                    verticalAlignment: Text.AlignVCenter
                                    font.bold: true
                                    // font.pointSize: Style.h1 + (numAvailable < 3 ? 30 : 15)
                                    font.pointSize: getFontSize()
                                    text: sensorData.standby ? "--.--" : parseO2Data()
                            }
                    
                            Row {
                                id: temperatureRow
                                width: parent.width
                                height: (Style.h3 + rowSpacers)
                    
                                Text {
                                    width: parent.width/3
                                    height: parent.height
                                    horizontalAlignment: Text.AlignLeft
                                    verticalAlignment: Text.AlignBottom
                                    font.bold: true
                                    font.pointSize: Style.h3
                                    text: !sensorData.standby ? "Online" : "Standby"
                                }
                    
                                Text {
                                    width: parent.width/3
                                    height: parent.height
                                    horizontalAlignment: Text.AlignHCenter
                                    verticalAlignment: Text.AlignBottom
                                    font.bold: true
                                    font.pointSize: Style.h3
                                    text: !applicationData.alarm_list[sensorIndex].ack ? "" : "Alarm Ack."
                                }
                    
                                Text {
                                    width: parent.width/3
                                    height: parent.height
                                    rightPadding: Style.gutter
                                    horizontalAlignment: Text.AlignRight
                                    verticalAlignment: Text.AlignVCenter
                                    font.bold: true
                                    font.pointSize: Style.h3
                                    text: sensorData.standby ? "--.--" : (sensorData.temp).toFixed(2) + (sensorData.tempUnits === 0 ? " \xB0C" : " \xB0F")
                                }
                            }
                    
                            Rectangle {
                                width: parent.width
                                height: isLast ? 0 : 2
                                border.width: isLast ? 0 : 2
                                border.color: "black"
                            }
                        }
                    }
                    
                    1 Reply Last reply
                    0
                    • fcarneyF Offline
                      fcarneyF Offline
                      fcarney
                      wrote on last edited by
                      #10

                      You are going to have to create a minimal program that no longer exhibits the problem and slowly add things until you find what is causing this. Or start taking out chunks and testing that. You will have to do process of elimination. I would start with removing the virtual keyboard since you have a bunch of stuff having to do with keyboard processing. Also, make sure there is not a cat sleeping on a wireless keyboard somewhere.

                      C++ is a perfectly valid school of magic.

                      1 Reply Last reply
                      1
                      • KH-219DesignK Offline
                        KH-219DesignK Offline
                        KH-219Design
                        wrote on last edited by
                        #11

                        You could also run dmesg to look at the kernel log, to see if there are any strange messages related to input drivers. (I have nothing in mind that I would expect to find, I'm just continuing along the lines of the keyboard and touchscreen investigation.)

                        In addition to keyboard, I'm looking at the flame diagram again and seeing filterLocaleIndices and onLocaleChanged near the middle of both the two main "mountains". Could there be some cron job or something that runs overnight and updates/adds/removes locales? or switches the user's locale?

                        @fcarney 's suggestion about process of elimination makes sense.

                        I wonder if you really have to run it overnight, or if perhaps you can force the device to "sleep" or to go to the lock screen a couple times, and maybe that is all it takes to trigger it?

                        www.219design.com
                        Software | Electrical | Mechanical | Product Design

                        1 Reply Last reply
                        1
                        • E Offline
                          E Offline
                          egbiomated
                          wrote on last edited by
                          #12

                          That flame graph is just from the first 4 minutes of operation not the entire 24 hours sorry should've said that before. dmesg doesn't turn up anything that would make me think there is an issue and cron doesn't exist on the machine it's the stock boot2qt image

                          1 Reply Last reply
                          1

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved