Commit 66321cea authored by David Redondo's avatar David Redondo 🏎

Add memory plugin

Uses mostly the same methods to get the information as the old code.
On FreeBSD the method to get the swap values is much simpler with just one
function call.
parent 08e9967c
......@@ -5,7 +5,8 @@ add_subdirectory(network)
add_subdirectory(power)
add_subdirectory(disks)
add_subdirectory(cpu)
if(UDev_FOUND)
add_subdirectory(gpu)
endif()
add_subdirectory(memory)
......@@ -39,7 +39,6 @@ KSGRDIface::KSGRDIface(QObject *parent, const QVariantList &args)
};
registerSubsystem("acpi");
registerSubsystem("lmsensors");
registerSubsystem("mem");
registerSubsystem("uptime");
registerSubsystem("system");
......@@ -63,7 +62,6 @@ KSGRDIface::KSGRDIface(QObject *parent, const QVariantList &args)
}
});
e.exec();
addAggregateSensors();
}
KSGRDIface::~KSGRDIface()
......@@ -299,50 +297,6 @@ void KSGRDIface::answerReceived(int id, const QList<QByteArray> &answer)
onSensorUpdated(id, answer);
}
void KSGRDIface::addAggregateSensors()
{
auto memPhysical = m_subsystems["mem"]->object("physical");
Q_ASSERT(memPhysical);
if (!memPhysical) {
return;
}
PercentageSensor *appLevel = new PercentageSensor(memPhysical, "applicationlevel", i18nc("@title", "Application Memory Percentage"));
appLevel->setShortName(i18nc("@title Application Memory Percentage", "Application"));
appLevel->setBaseSensor(memPhysical->sensor("application"));
appLevel->setDescription(i18nc("@info", "Percentage of memory taken by applications."));
PercentageSensor *bufLevel = new PercentageSensor(memPhysical, "buflevel", i18nc("@title", "Buffer Memory Percentage"));
bufLevel->setShortName(i18nc("@title Buffer Memory Percentage", "Buffer"));
bufLevel->setBaseSensor(memPhysical->sensor("buf"));
bufLevel->setDescription(i18nc("@info", "Percentage of memory taken by the buffer."));
PercentageSensor *cacheLevel = new PercentageSensor(memPhysical, "cachelevel", i18nc("@title", "Cache Memory Percentage"));
cacheLevel->setShortName(i18nc("@title Cache Memory Percentage", "Cache"));
cacheLevel->setBaseSensor(memPhysical->sensor("cached"));
cacheLevel->setDescription(i18nc("@info", "Percentage of memory taken by the cache."));
PercentageSensor *freeLevel = new PercentageSensor(memPhysical, "freelevel", i18nc("@title", "Free Memory Percentage"));
freeLevel->setShortName(i18nc("@title Free Memory Percentage", "Free"));
freeLevel->setBaseSensor(memPhysical->sensor("free"));
freeLevel->setDescription(i18nc("@info", "Percentage of free memory."));
PercentageSensor *usedLevel = new PercentageSensor(memPhysical, "usedlevel", i18nc("@title", "Used Memory Percentage"));
usedLevel->setShortName(i18nc("@title Used Memory Percentage", "Used"));
usedLevel->setBaseSensor(memPhysical->sensor("used"));
usedLevel->setDescription(i18nc("@info", "Percentage of used memory."));
PercentageSensor *availableLevel = new PercentageSensor(memPhysical, "availablelevel", i18nc("@title", "Available Memory Percentage"));
availableLevel->setShortName(i18nc("@title Available Memory Percentage", "Available"));
availableLevel->setBaseSensor(memPhysical->sensor("available"));
availableLevel->setDescription(i18nc("@info", "Percentage of available memory."));
PercentageSensor *allocatedLevel = new PercentageSensor(memPhysical, "allocatedlevel", i18nc("@title", "Allocated Memory Percentage"));
allocatedLevel->setShortName(i18nc("@title Allocated Memory Percentage", "Allocated"));
allocatedLevel->setBaseSensor(memPhysical->sensor("allocated"));
allocatedLevel->setDescription(i18nc("@info", "Percentage of allocated memory."));
}
QString KSGRDIface::shortNameFor(const QString &key)
{
// TODO: This is pretty ugly, but it is really hard to add this information to ksysguardd.
......
......@@ -60,7 +60,6 @@ private:
void unsubscribe(const QString &sensorPath);
KSysGuard::Unit unitFromString(const QString &unitString) const;
void addAggregateSensors();
QString shortNameFor(const QString &key);
//This qlist is just to have an index mapping because of KSGRD's old API
......
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
add_library(ksysguard_plugin_memory MODULE memory.cpp backend.cpp)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_sources(ksysguard_plugin_memory PRIVATE linuxbackend.cpp)
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
target_sources(ksysguard_plugin_memory PRIVATE freebsdbackend.cpp)
target_link_libraries(ksysguard_plugin_memory kvm)
endif()
target_link_libraries(ksysguard_plugin_memory Qt5::Core KSysGuard::StatsBackend KF5::CoreAddons KF5::I18n)
install(TARGETS ksysguard_plugin_memory DESTINATION ${KDE_INSTALL_PLUGINDIR}/ksysguard)
endif()
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "backend.h"
#include <AggregateSensor.h>
#include <SensorObject.h>
#include <SensorProperty.h>
#include <KLocalizedString>
MemoryBackend::MemoryBackend(SensorContainer *container)
{
m_physicalObject = new SensorObject(QStringLiteral("physical"), i18nc("@title", "Physical Memory"), container);
m_swapObject = new SensorObject(QStringLiteral("swap"), i18nc("@title", "Swap Memory"), container);
}
void MemoryBackend::makeSensors()
{
m_total = new SensorProperty(QStringLiteral("total"), m_physicalObject);
m_used = new SensorProperty(QStringLiteral("used"), m_physicalObject);
m_free = new SensorProperty(QStringLiteral("free"), m_physicalObject);
m_application = new SensorProperty(QStringLiteral("application"), m_physicalObject);
m_cache = new SensorProperty(QStringLiteral("cache"), m_physicalObject);
m_buffer = new SensorProperty(QStringLiteral("buffer"), m_physicalObject);
m_swapTotal = new SensorProperty(QStringLiteral("total"), m_swapObject);
m_swapUsed = new SensorProperty(QStringLiteral("used"), m_swapObject);
m_swapFree = new SensorProperty(QStringLiteral("free"), m_swapObject);
}
void MemoryBackend::initSensors()
{
makeSensors();
m_total->setName(i18nc("@title", "Total Physical Memory"));
m_total->setShortName(i18nc("@title, Short for 'Total Physical Memory'", "Total"));
m_total->setUnit(KSysGuard::UnitByte);
m_total->setVariantType(QVariant::ULongLong);
m_used->setName(i18nc("@title", "Used Physical Memory"));
m_used->setShortName(i18nc("@title, Short for 'Used Physical Memory'", "Used"));
m_used->setUnit(KSysGuard::UnitByte);
m_used->setVariantType(QVariant::ULongLong);
m_used->setMax(m_total);
auto usedPercentage = new PercentageSensor(m_physicalObject, QStringLiteral("usedPercent"), i18nc("@title", "Used Physical Memory Percentage"));
usedPercentage->setBaseSensor(m_used);
m_free->setName(i18nc("@title", "Free Physical Memory"));
m_free->setShortName(i18nc("@title, Short for 'Free Physical Memory'", "Free"));
m_free->setUnit(KSysGuard::UnitByte);
m_free->setVariantType(QVariant::ULongLong);
m_free->setMax(m_total);
auto freePercentage = new PercentageSensor(m_physicalObject, QStringLiteral("freePercent"), i18nc("@title", "Free Physical Memory Percentage"));
freePercentage->setBaseSensor(m_free);
m_application->setName(i18nc("@title", "Application Memory"));
m_application->setShortName(i18nc("@title, Short for 'Application Memory'", "Application"));
m_application->setUnit(KSysGuard::UnitByte);
m_application->setVariantType(QVariant::ULongLong);
m_application->setMax(m_total);
auto applicationPercentage = new PercentageSensor(m_physicalObject, QStringLiteral("applicationPercent"), i18nc("@title", "Application Memory Percentage"));
applicationPercentage->setBaseSensor(m_application);
m_cache->setName(i18nc("@title", "Cache Memory"));
m_cache->setShortName(i18nc("@title, Short for 'Cache Memory'", "Cache"));
m_cache->setUnit(KSysGuard::UnitByte);
m_cache->setVariantType(QVariant::ULongLong);
m_cache->setMax(m_total);
auto cachePercentage = new PercentageSensor(m_physicalObject, QStringLiteral("cachePercent"), i18nc("@title", "Cache Memory Percentage"));
cachePercentage->setBaseSensor(m_cache);
m_buffer->setName(i18nc("@title", "Buffer Memory"));
m_buffer->setShortName(i18nc("@title, Short for 'Buffer Memory'", "Buffer"));
m_buffer->setDescription(i18n("Amount of memory used for caching disk blocks"));
m_buffer->setUnit(KSysGuard::UnitByte);
m_buffer->setVariantType(QVariant::ULongLong);
m_buffer->setMax(m_total);
auto bufferPercentage = new PercentageSensor(m_physicalObject, QStringLiteral("bufferPercent"), i18nc("@title", "Buffer Memory Percentage"));
bufferPercentage->setBaseSensor(m_buffer);
m_swapTotal->setName(i18nc("@title", "Total Swap Memory"));
m_swapTotal->setShortName(i18nc("@title, Short for 'Total Swap Memory'", "Total"));
m_swapTotal->setUnit(KSysGuard::UnitByte);
m_swapTotal->setVariantType(QVariant::ULongLong);
m_swapUsed->setName(i18nc("@title", "Used Swap Memory"));
m_swapUsed->setShortName(i18nc("@title, Short for 'Used Swap Memory'", "Used"));
m_swapUsed->setUnit(KSysGuard::UnitByte);
m_swapUsed->setVariantType(QVariant::ULongLong);
m_swapUsed->setMax(m_swapTotal);
auto usedSwapPercentage = new PercentageSensor(m_swapObject, QStringLiteral("usedPercent"), i18nc("@title", "Used Swap Memory Percentage"));
usedSwapPercentage->setBaseSensor(m_swapUsed);
m_swapFree->setName(i18nc("@title", "Free Swap Memory"));
m_swapFree->setShortName(i18nc("@title, Short for 'Free Swap Memory'", "Free"));
m_swapFree->setUnit(KSysGuard::UnitByte);
m_swapFree->setVariantType(QVariant::ULongLong);
m_swapFree->setMax(m_swapTotal);
auto freeSwapPercentage = new PercentageSensor(m_swapObject, QStringLiteral("freePercent"), i18nc("@title", "Free Swap Memory Percentage"));
freeSwapPercentage->setBaseSensor(m_swapFree);
}
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef BACKEND_H
#define BACKEND_H
class SensorContainer;
class SensorObject;
class SensorProperty;
class MemoryBackend {
public:
MemoryBackend(SensorContainer *container);
virtual ~MemoryBackend() = default;
void initSensors();
virtual void update() = 0;
protected:
virtual void makeSensors();
SensorProperty *m_total;
SensorProperty *m_used;
SensorProperty *m_free;
SensorProperty *m_application;
SensorProperty *m_cache;
SensorProperty *m_buffer;
SensorProperty *m_swapTotal;
SensorProperty *m_swapUsed;
SensorProperty *m_swapFree;
SensorObject *m_physicalObject;
SensorObject *m_swapObject;
};
#endif
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "freebsdbackend.h"
#include <SensorObject.h>
#include <SensorProperty.h>
#include <SysctlSensor.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>
template <typename T>
bool readSysctl(const char *name, T *buffer, size_t size = sizeof(T))
{
return sysctlbyname(name, buffer, &size, nullptr, 0) != -1;
}
FreeBsdMemoryBackend::FreeBsdMemoryBackend(SensorContainer* container)
: MemoryBackend(container)
, m_pageSize(getpagesize())
, m_kd(nullptr)
{
char errorBuffer[_POSIX2_LINE_MAX];
// Get a descriptor for accesing kernel memory which we need for querying swap info. /dev/null
// indicates that we do not want to directly access kernel memory.
m_kd = kvm_openfiles(nullptr, "/dev/null", nullptr, O_RDONLY, errorBuffer);
if (!m_kd) {
qWarning() << errorBuffer;
}
}
void FreeBsdMemoryBackend::makeSensors()
{
auto totalSensor = new SysctlSensor<unsigned long long>(QStringLiteral("total"), "hw.physmem", m_physicalObject);
m_total = totalSensor;
m_sysctlSensors.append(totalSensor);
m_used = new SensorProperty(QStringLiteral("used"), m_physicalObject);
m_application = new SensorProperty(QStringLiteral("application"), m_physicalObject);
auto capturedPagesToBytes = [this] (auto pages) {return pagesToBytes(pages);};
auto freeSensor = new SysctlSensor<uint32_t>(QStringLiteral("free"), "vm.stats.vm.v_free_count", m_physicalObject);
freeSensor->setConversionFunction(capturedPagesToBytes);
m_free = freeSensor;
m_sysctlSensors.push_back(freeSensor);
auto cacheSensor = new SysctlSensor<uint32_t>(QStringLiteral("cache"),"vm.v_cache_count", m_physicalObject);
cacheSensor->setConversionFunction(capturedPagesToBytes);
m_cache = cacheSensor;
m_sysctlSensors.push_back(cacheSensor);
auto bufferSensor = new SysctlSensor<uint64_t>(QStringLiteral("buffer"), "vfs.bufspace", m_physicalObject);
m_buffer = bufferSensor;
m_sysctlSensors.push_back(bufferSensor);
m_swapTotal = new SensorProperty(QStringLiteral("total"), m_swapObject);
m_swapUsed = new SensorProperty(QStringLiteral("used"), m_swapObject);
m_swapFree = new SensorProperty(QStringLiteral("free"), m_swapObject);
}
unsigned long long FreeBsdMemoryBackend::pagesToBytes(uint32_t pages)
{
return m_pageSize * pages;
}
void FreeBsdMemoryBackend::update()
{
kvm_swap swapInfo;
// Calling it with just one swapInfo gets us the totals, passing 0 as the last argument is mandatory
if (m_swapObject->isSubscribed() && m_kd && (kvm_getswapinfo(m_kd, &swapInfo, 1, 0) != -1)) {
m_swapTotal->setValue(pagesToBytes(swapInfo.ksw_total));
m_swapUsed->setValue(pagesToBytes(swapInfo.ksw_used));
m_swapFree->setValue(pagesToBytes(swapInfo.ksw_total - swapInfo.ksw_used));
}
for (const auto sysctlSensor : m_sysctlSensors) {
sysctlSensor->update();
}
m_used->setValue(m_total->value().toULongLong() - m_free->value().toULongLong());
uint32_t activePages = 0;
uint32_t inactivePages = 0;
if (readSysctl("vm.stats.vm.v_active_count", &activePages) && readSysctl("vm.stats.vm.v_inactive_count", &inactivePages)) {
m_application->setValue(pagesToBytes(activePages + inactivePages));
}
}
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef FREEBSDBACKEND_H
#define FREEBSDBACKEND_H
#include "backend.h"
#include <QVector>
#include <kvm.h> // can't forward declare typedefed kvm_t
class SensorProperty;
class FreeBsdMemoryBackend : public MemoryBackend {
public:
FreeBsdMemoryBackend(SensorContainer *container);
void update() override;
private:
void makeSensors() override;
unsigned long long pagesToBytes(uint32_t pages);
unsigned int m_pageSize;
kvm_t *m_kd;
QVector<SensorProperty*> m_sysctlSensors;
};
#endif
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "linuxbackend.h"
#include <SensorObject.h>
#include <SensorProperty.h>
#include <QFile>
LinuxMemoryBackend::LinuxMemoryBackend(SensorContainer *container)
: MemoryBackend(container)
{
}
void LinuxMemoryBackend::update()
{
if (!m_physicalObject->isSubscribed() || ! m_swapObject->isSubscribed()) {
return;
}
QFile meminfo(QStringLiteral("/proc/meminfo"));
meminfo.open(QIODevice::ReadOnly);
// The format of the file is as follows:
// Fieldname:[whitespace]value kB
// A description of the fields can be found at
// https://www.kernel.org/doc/html/latest/filesystems/proc.html#meminfo
unsigned long long total, free, available, buffer, cache, slab, swapTotal, swapFree;
for (QByteArray line = meminfo.readLine(); !line.isEmpty(); line = meminfo.readLine()) {
int colonIndex = line.indexOf(':');
const auto fields = line.simplified().split(' ');
const QByteArray name = line.left(colonIndex);
const unsigned long long value = std::strtoull(line.mid(colonIndex + 1), nullptr, 10) * 1024;
if (name == "MemTotal") {
total = value;
} else if (name == "MemFree") {
free = value;
} else if (name == "MemAvailable") {
available = value;
} else if (name == "Buffers") {
buffer = value;
} else if (name == "Cached") {
cache = value;
} else if (name == "Slab") {
slab = value;
} else if (name == "SwapTotal") {
swapTotal = value;
} else if (name == "SwapFree") {
swapFree = value;
}
}
m_total->setValue(total);
m_used->setValue(total - available);
m_free->setValue(available);
m_application->setValue(total - free - cache - buffer - slab);
m_cache->setValue(cache + slab);
m_buffer->setValue(buffer);
m_swapTotal->setValue(swapTotal);
m_swapUsed->setValue(swapTotal - swapFree);
m_swapFree->setValue(swapFree);
}
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef LINUXBACKEND_H
#define LINUXBACKEND_H
#include "backend.h"
class LinuxMemoryBackend : public MemoryBackend {
public:
LinuxMemoryBackend(SensorContainer *container);
void update() override;
};
#endif
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "memory.h"
#include "backend.h"
#if defined Q_OS_LINUX
#include "linuxbackend.h"
#elif defined Q_OS_FREEBSD
#include "freebsdbackend.h"
#endif
#include <SensorContainer.h>
#include <KLocalizedString>
#include <KPluginFactory>
#include <QtGlobal>
MemoryPlugin::MemoryPlugin(QObject *parent, const QVariantList &args)
: SensorPlugin(parent, args)
{
auto container = new SensorContainer(QStringLiteral("memory"), i18nc("@title", "Memory"), this);
#if defined Q_OS_LINUX
m_backend = std::make_unique<LinuxMemoryBackend>(container);
#elif defined Q_OS_FREEBSD
m_backend = std::make_unique<FreeBsdMemoryBackend>(container);
#endif
m_backend->initSensors();
}
MemoryPlugin::~MemoryPlugin() = default;
void MemoryPlugin::update()
{
m_backend->update();
}
K_PLUGIN_CLASS_WITH_JSON(MemoryPlugin, "metadata.json")
#include "memory.moc"
/*
Copyright (c) 2020 David Redondo <kde@david-redondo.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3 of
the license or (at your option) at any later version that is
accepted by the membership of KDE e.V. (or its successor
approved by the membership of KDE e.V.), which shall act as a
proxy as defined in Section 14 of version 3 of the license.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/