Commit 23852597 authored by Arjen Hiemstra's avatar Arjen Hiemstra
Browse files

Move reading of /proc/pid/smaps_rollup to a separate thread

As it turns out, smaps/smaps_rollup are fairly slow to read because the
kernel iterates all the memory of a process when this file is accessed.
This caused the update time of all processes to go up from ~30 ms to
more than 200ms.

This changes things to do the reading of smaps_rollup in a separate
thread that doesn't block the main update. This reduces update time to
~50ms, which is noticably better. To accomodate the asynchronous nature,
some additional functionality needed to be added so we can notify
Processes of the asynchronous update.
parent 40cb8d6a
......@@ -18,6 +18,7 @@ set(ksysguard_LIB_SRCS
process_attribute_model.cpp
process_data_model.cpp
process_data_provider.cpp
read_procsmaps_runnable.cpp
)
ecm_qt_declare_logging_category(ksysguard_LIB_SRCS HEADER processcore_debug.h IDENTIFIER LIBKSYSGUARD_PROCESSCORE CATEGORY_NAME org.kde.libksysguard.processcore)
......@@ -78,7 +79,9 @@ set(ksysguardprocesslist_helper_srcs
helper.cpp
process.cpp
processes_local_p.cpp
processes_base_p.cpp)
processes_base_p.cpp
read_procsmaps_runnable.cpp
)
add_executable(ksysguardprocesslist_helper ${ksysguardprocesslist_helper_srcs})
target_link_libraries(ksysguardprocesslist_helper Qt5::Core KF5::AuthCore KF5::I18n)
......
......@@ -31,6 +31,7 @@
#include <QMutableSetIterator>
#include <QByteArray>
#include <QElapsedTimer>
#include <QVariantMap>
//for sysconf
#include <unistd.h>
......@@ -93,6 +94,8 @@ Processes::Private::~Private() {
Processes::Processes(const QString &host, QObject *parent) : QObject(parent), d(new Private(this))
{
qRegisterMetaType<KSysGuard::Process::Updates>();
if(host.isEmpty()) {
d->mAbstractProcesses = new ProcessesLocal();
} else {
......@@ -102,6 +105,7 @@ Processes::Processes(const QString &host, QObject *parent) : QObject(parent), d(
}
d->mIsLocalHost = host.isEmpty();
connect( d->mAbstractProcesses, &AbstractProcesses::processesUpdated, this, &Processes::processesUpdated);
connect( d->mAbstractProcesses, &AbstractProcesses::processUpdated, this, &Processes::processUpdated);
}
Processes::~Processes()
{
......@@ -358,6 +362,26 @@ void Processes::processesUpdated() {
emit updated();
}
void Processes::processUpdated(long pid, const Process::Updates &changes)
{
auto process = d->mProcesses.value(pid);
if (!process) {
return;
}
for (auto entry : changes) {
switch (entry.first) {
case Process::VmPSS:
process->setVmPSS(entry.second.toLongLong());
break;
default:
break;
}
}
Q_EMIT processChanged(process, false);
}
void Processes::Private::markProcessesAsEnded(long pid)
{
Q_ASSERT(pid >= 0);
......
......@@ -25,6 +25,7 @@
#include "process.h"
#include <QObject>
#include <QHash>
#include <QVariant>
namespace KSysGuard
{
......@@ -203,6 +204,7 @@ namespace KSysGuard
public Q_SLOTS:
/** The abstract processes has updated its list of processes */
void processesUpdated();
void processUpdated(long pid, const Process::Updates &changes);
Q_SIGNALS:
/** The data for a process has changed.
......@@ -267,4 +269,5 @@ namespace KSysGuard
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Processes::UpdateFlags)
}
#endif
......@@ -144,6 +144,8 @@ Q_SIGNALS:
/** \brief This is emitted when the processes have been updated, and the view should be refreshed.
*/
void processesUpdated();
void processUpdated(long pid, const KSysGuard::Process::Updates &changes);
};
}
......
......@@ -21,6 +21,7 @@
#include "processes_local_p.h"
#include "process.h"
#include "read_procsmaps_runnable.h"
#include <klocalizedstring.h>
......@@ -29,6 +30,7 @@
#include <QSet>
#include <QByteArray>
#include <QTextStream>
#include <QThreadPool>
//for sysconf
#include <unistd.h>
......@@ -97,9 +99,6 @@ static int ioprio_get(int which, int who)
}
#endif
namespace KSysGuard
{
......@@ -114,7 +113,6 @@ namespace KSysGuard
inline bool readProcCmdline(const QString &dir, Process *process);
inline bool readProcCGroup(const QString &dir, Process *process);
inline bool readProcAttr(const QString &dir, Process *process);
inline bool readProcSmaps(const QString &dir, Process *process);
inline bool getNiceness(long pid, Process *process);
inline bool getIOStatistics(const QString &dir, Process *process);
QFile mFile;
......@@ -456,27 +454,6 @@ bool ProcessesLocal::Private::readProcCmdline(const QString &dir, Process *proce
return true;
}
bool ProcessesLocal::Private::readProcSmaps(const QString &dir, Process *process)
{
mFile.setFileName(dir + QStringLiteral("smaps_rollup"));
if (!mFile.open(QIODevice::ReadOnly)) {
return false;
}
auto totalPss = -1LL;
while (mFile.readLine(mBuffer, sizeof(mBuffer)) > 0) {
if (qstrncmp(mBuffer, "Pss:", strlen("Pss:")) == 0) {
totalPss += atoll(mBuffer + sizeof("Pss:") - 1);
}
}
mFile.close();
process->setVmPSS(totalPss);
return true;
}
bool ProcessesLocal::Private::getNiceness(long pid, Process *process) {
int sched = sched_getscheduler(pid);
switch(sched) {
......@@ -569,17 +546,25 @@ bool ProcessesLocal::Private::getIOStatistics(const QString &dir, Process *proce
}
return true;
}
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
{
bool success = true;
QString dir = QLatin1String("/proc/") + QString::number(pid) + QLatin1Char('/');
auto runnable = new ReadProcSmapsRunnable{dir};
connect(runnable, &ReadProcSmapsRunnable::finished, this, [this, pid, runnable]() {
Q_EMIT processUpdated(pid, { { Process::VmPSS, runnable->pss() } });
runnable->deleteLater();
}, Qt::DirectConnection);
QThreadPool::globalInstance()->start(runnable);
if(!d->readProcStat(dir, process)) success = false;
if(!d->readProcStatus(dir, process)) success = false;
if(!d->readProcStatm(dir, process)) success = false;
if(!d->readProcCmdline(dir, process)) success = false;
if(!d->readProcCGroup(dir, process)) success = false;
if(!d->readProcAttr(dir, process)) success = false;
if(!d->readProcSmaps(dir, process)) success = false;
if(!d->getNiceness(pid, process)) success = false;
if(mUpdateFlags.testFlag(Processes::IOStatistics) && !d->getIOStatistics(dir, process)) success = false;
......
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "read_procsmaps_runnable.h"
#include <QFile>
using namespace KSysGuard;
ReadProcSmapsRunnable::ReadProcSmapsRunnable(const QString &dir)
: QObject()
, m_dir(dir)
{
setAutoDelete(false);
}
void ReadProcSmapsRunnable::run()
{
QFile file{m_dir + QStringLiteral("smaps_rollup")};
if (!file.open(QIODevice::ReadOnly)) {
return;
}
m_pss = 0LL;
auto buffer = QByteArray{1024, '\0'};
while (file.readLine(buffer.data(), buffer.size()) > 0) {
if (buffer.startsWith("Pss:")) {
m_pss += std::stoll(buffer.mid(sizeof("Pss:")).toStdString());
}
}
file.close();
Q_EMIT finished();
}
qlonglong ReadProcSmapsRunnable::pss() const
{
return m_pss;
}
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#pragma once
#include <QObject>
#include <QRunnable>
namespace KSysGuard {
class ReadProcSmapsRunnable : public QObject, public QRunnable
{
Q_OBJECT
public:
ReadProcSmapsRunnable(const QString &dir);
void run() override;
qlonglong pss() const;
Q_SIGNAL void finished();
private:
QString m_dir;
qlonglong m_pss = 0;
};
} // namespace KSysGuard
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment