/* SPDX-FileCopyrightText: 2023 Adrian Edwards SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ #include "pressure.h" #include "pressure_logging.h" #include #include #include #include #include #include /** * A Structure holding the data for a single line of output from /proc/pressure */ struct PressureDatapoint { double avg10; double avg60; double avg300; qulonglong total; }; /** * A Structure holding the data for a single files worth of output from /proc/pressure */ struct PressureData { PressureDatapoint some; PressureDatapoint full; }; /** * Read a file from /proc and process it into an instance of PressureData. * * Filename is specified as the name of the virtual file to read that will be appended to /proc/pressure/, for example "memory", "cpu", or "io" * * File contents returned looks like this: * some avg10=0.00 avg60=0.00 avg300=0.00 total=16304493 * full avg10=0.00 avg60=0.00 avg300=0.00 total=15066192 * * Returns a PressureData structure containing the parsed data */ PressureData parseDataFromFile(const QString fileSelector) { qCDebug(KSYSTEMSTATS_PRESSURE) << "Parsing " << "/proc/pressure/" << fileSelector; QFile pressureInfoFile(QStringLiteral("/proc/pressure/") + fileSelector); bool open = pressureInfoFile.open(QIODevice::ReadOnly); static const QByteArray avg10DataLabel = QByteArrayLiteral("avg10"); static const QByteArray avg60DataLabel = QByteArrayLiteral("avg60"); static const QByteArray avg300DataLabel = QByteArrayLiteral("avg300"); static const QByteArray totalDataLabel = QByteArrayLiteral("total"); static const QByteArray someDataLabel = QByteArrayLiteral("some"); static const QByteArray fullDataLabel = QByteArrayLiteral("full"); if (!open) { // You can choose how to handle the error case, such as throwing an exception // or returning a default-initialized Data structure. return PressureData{}; } qCDebug(KSYSTEMSTATS_PRESSURE) << "File open successfully"; PressureData pressureData; for (QByteArray line = pressureInfoFile.readLine(); !line.isEmpty(); line = pressureInfoFile.readLine()) { qCDebug(KSYSTEMSTATS_PRESSURE) << "Reading line:" << line; QList fields = line.simplified().split(' '); //This assumes fields is not empty, which is mostly gauranteed because //we already checked if line isEmpty() in the loop QByteArray type = fields.takeFirst(); qCDebug(KSYSTEMSTATS_PRESSURE) << "type:" << type; PressureDatapoint data = {0,0,0,0}; // For each statistic from the fields parsed, set data based on it for (QByteArray statistic : fields) { const int delim = statistic.indexOf("="); const QString name = statistic.left(delim).trimmed(); const QString value = statistic.mid(delim + 1).trimmed(); qCDebug(KSYSTEMSTATS_PRESSURE) << "statistic:" << statistic << "name:" << name << "value:" << value << "processed:" << value.toDouble(); if (name == avg10DataLabel) { data.avg10 = value.toDouble(); } else if (name == avg60DataLabel) { data.avg60 = value.toDouble(); } else if (name == avg300DataLabel) { data.avg300 = value.toDouble(); } else if (name == totalDataLabel) { data.total = value.toULongLong(); } } if (type == someDataLabel) { pressureData.some = data; } else if (type == fullDataLabel) { pressureData.full = data; } else { pressureData.some = data; } } pressureInfoFile.close(); qCDebug(KSYSTEMSTATS_PRESSURE) << fileSelector << "some" << pressureData.some.avg10 << pressureData.some.avg60 << pressureData.some.avg300 << pressureData.some.total; qCDebug(KSYSTEMSTATS_PRESSURE) << fileSelector << "full" << pressureData.full.avg10 << pressureData.full.avg60 << pressureData.full.avg300 << pressureData.full.total; return pressureData; } PressurePlugin::~PressurePlugin() = default; /// Top level class for this plugin that implements SensorPlugin from libksysguard PressurePlugin::PressurePlugin(QObject *parent, const QVariantList &args) : SensorPlugin(parent, args) { qCDebug(KSYSTEMSTATS_PRESSURE) << "Initializing"; container = new KSysGuard::SensorContainer(QStringLiteral("pressure"), i18nc("@title", "Pressure"), this); memoryObject = new KSysGuard::SensorObject(QStringLiteral("memory"), i18nc("@title", "Memory Pressure"), container); memorySome10SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some10Sec"), i18nc("@title", "Some (10 second average)"), memoryObject); memorySome10SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available memory resources, averaged over 10 seconds")); memorySome60SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some60Sec"), i18nc("@title", "Some (1 minute average)"), memoryObject); memorySome60SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available memory resources, averaged over 1 minutes")); memorySome300SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some300Sec"), i18nc("@title", "Some (5 minute average)"), memoryObject); memorySome300SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available memory resources, averaged over 5 minutes")); memorySomeTotalProperty = new KSysGuard::SensorProperty(QStringLiteral("someTotal"), i18nc("@title", "Total Some Stall Time"), memoryObject); memorySomeTotalProperty->setDescription(i18nc("@info", "Amount of time that at least one task is stalled waiting for available memory resources, measured in microseconds")); memoryFull10SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full10Sec"), i18nc("@title", "Full (10 second average)"), memoryObject); memoryFull10SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available CPU resources, averaged over 10 seconds")); memoryFull60SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full60Sec"), i18nc("@title", "Full (1 minute average)"), memoryObject); memoryFull60SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available CPU resources, averaged over 1 minutes")); memoryFull300SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full300Sec"), i18nc("@title", "Full (5 minute average)"), memoryObject); memoryFull300SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available CPU resources, averaged over 5 minutes")); memoryFullTotalProperty = new KSysGuard::SensorProperty(QStringLiteral("fullTotal"), i18nc("@title", "Total Full Stall Time"), memoryObject); memoryFullTotalProperty->setDescription(i18nc("@info", "Amount of time that at least one task is stalled waiting for available CPU resources, measured in microseconds")); cpuObject = new KSysGuard::SensorObject(QStringLiteral("cpu"), i18nc("@title", "CPU Pressure"), container); cpuSome10SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some10Sec"), i18nc("@title", "Some (10 second average)"), cpuObject); cpuSome10SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available IO resources, averaged over 10 seconds")); cpuSome60SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some60Sec"), i18nc("@title", "Some (1 minute average)"), cpuObject); cpuSome60SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available IO resources, averaged over 1 minutes")); cpuSome300SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some300Sec"), i18nc("@title", "Some (5 minute average)"), cpuObject); cpuSome300SecProperty->setDescription(i18nc("@info", "Percentage of time that at least one task is stalled waiting for available IO resources, averaged over 5 minutes")); cpuSomeTotalProperty = new KSysGuard::SensorProperty(QStringLiteral("someTotal"), i18nc("@title", "Total Some Stall Time"), cpuObject); cpuSomeTotalProperty->setDescription(i18nc("@info", "Amount of time that at least one task is stalled waiting for available IO resources, measured in microseconds")); cpuFull10SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full10Sec"), i18nc("@title", "Full (10 second average)"), cpuObject); cpuFull10SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available memory resources, averaged over 10 seconds")); cpuFull60SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full60Sec"), i18nc("@title", "Full (1 minute average)"), cpuObject); cpuFull60SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available memory resources, averaged over 1 minutes")); cpuFull300SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full300Sec"), i18nc("@title", "Full (5 minute average)"), cpuObject); cpuFull300SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available memory resources, averaged over 5 minutes")); cpuFullTotalProperty = new KSysGuard::SensorProperty(QStringLiteral("fullTotal"), i18nc("@title", "Total Full Stall Time"), cpuObject); cpuFullTotalProperty->setDescription(i18nc("@info", "Amount of time that all tasks are stalled waiting for available memory resources, measured in microseconds")); ioObject = new KSysGuard::SensorObject(QStringLiteral("io"), i18nc("@title", "IO Pressure"), container); ioSome10SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some10Sec"), i18nc("@title", "Some (10 second average)"), ioObject); ioSome10SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available CPU resources, averaged over 10 seconds")); ioSome60SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some60Sec"), i18nc("@title", "Some (1 minute average)"), ioObject); ioSome60SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available CPU resources, averaged over 1 minutes")); ioSome300SecProperty = new KSysGuard::SensorProperty(QStringLiteral("some300Sec"), i18nc("@title", "Some (5 minute average)"), ioObject); ioSome300SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available CPU resources, averaged over 5 minutes")); ioSomeTotalProperty = new KSysGuard::SensorProperty(QStringLiteral("someTotal"), i18nc("@title", "Total Some Stall Time"), ioObject); ioSomeTotalProperty->setDescription(i18nc("@info", "Amount of time that all tasks are stalled waiting for available CPU resources, measured in microseconds")); ioFull10SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full10Sec"), i18nc("@title", "Full (10 second average)"), ioObject); ioFull10SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available IO resources, averaged over 10 seconds")); ioFull60SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full60Sec"), i18nc("@title", "Full (1 minute average)"), ioObject); ioFull60SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available IO resources, averaged over 1 minutes")); ioFull300SecProperty = new KSysGuard::SensorProperty(QStringLiteral("full300Sec"), i18nc("@title", "Full (5 minute average)"), ioObject); ioFull300SecProperty->setDescription(i18nc("@info", "Percentage of time that all tasks are stalled waiting for available IO resources, averaged over 5 minutes")); ioFullTotalProperty = new KSysGuard::SensorProperty(QStringLiteral("fullTotal"), i18nc("@title", "Total Full Stall Time"), ioObject); ioFullTotalProperty->setDescription(i18nc("@info", "Amount of time that all tasks are stalled waiting for available IO resources, measured in microseconds")); qCDebug(KSYSTEMSTATS_PRESSURE) << "Setting up"; auto setup_datafield = [](KSysGuard::SensorProperty *sensorProperty) { sensorProperty->setUnit(KSysGuard::UnitPercent); sensorProperty->setVariantType(QVariant::Double); sensorProperty->setMax(100); }; setup_datafield(memorySome10SecProperty); setup_datafield(memorySome60SecProperty); setup_datafield(memorySome300SecProperty); setup_datafield(memoryFull10SecProperty); setup_datafield(memoryFull60SecProperty); setup_datafield(memoryFull300SecProperty); setup_datafield(cpuSome10SecProperty); setup_datafield(cpuSome60SecProperty); setup_datafield(cpuSome300SecProperty); setup_datafield(cpuFull10SecProperty); setup_datafield(cpuFull60SecProperty); setup_datafield(cpuFull300SecProperty); setup_datafield(ioSome10SecProperty); setup_datafield(ioSome60SecProperty); setup_datafield(ioSome300SecProperty); setup_datafield(ioFull10SecProperty); setup_datafield(ioFull60SecProperty); setup_datafield(ioFull300SecProperty); auto setup_totalfield = [](KSysGuard::SensorProperty *sensorProperty) { // TODO: specify unit sensorProperty->setUnit(KSysGuard::UnitPercent); sensorProperty->setVariantType(QVariant::ULongLong); }; setup_totalfield(memorySomeTotalProperty); setup_totalfield(memoryFullTotalProperty); setup_totalfield(cpuSomeTotalProperty); setup_totalfield(cpuFullTotalProperty); setup_totalfield(ioSomeTotalProperty); setup_totalfield(ioFullTotalProperty); } void PressurePlugin::update() { qCDebug(KSYSTEMSTATS_PRESSURE) << "Updating"; PressureData data = parseDataFromFile(QStringLiteral("memory")); memorySome10SecProperty->setValue(data.some.avg10); memorySome60SecProperty->setValue(data.some.avg60); memorySome300SecProperty->setValue(data.some.avg300); memorySomeTotalProperty->setValue(data.some.total); memoryFull10SecProperty->setValue(data.full.avg10); memoryFull60SecProperty->setValue(data.full.avg60); memoryFull300SecProperty->setValue(data.full.avg300); memoryFullTotalProperty->setValue(data.full.total); data = parseDataFromFile(QStringLiteral("cpu")); cpuSome10SecProperty->setValue(data.some.avg10); cpuSome60SecProperty->setValue(data.some.avg60); cpuSome300SecProperty->setValue(data.some.avg300); cpuSomeTotalProperty->setValue(data.some.total); cpuFull10SecProperty->setValue(data.full.avg10); cpuFull60SecProperty->setValue(data.full.avg60); cpuFull300SecProperty->setValue(data.full.avg300); cpuFullTotalProperty->setValue(data.full.total); data = parseDataFromFile(QStringLiteral("io")); ioSome10SecProperty->setValue(data.some.avg10); ioSome60SecProperty->setValue(data.some.avg60); ioSome300SecProperty->setValue(data.some.avg300); ioSomeTotalProperty->setValue(data.some.total); ioFull10SecProperty->setValue(data.full.avg10); ioFull60SecProperty->setValue(data.full.avg60); ioFull300SecProperty->setValue(data.full.avg300); ioFullTotalProperty->setValue(data.full.total); } K_PLUGIN_CLASS_WITH_JSON(PressurePlugin, "metadata.json") #include "pressure.moc"