diff --git a/processcore/process.h b/processcore/process.h --- a/processcore/process.h +++ b/processcore/process.h @@ -136,6 +136,9 @@ qlonglong vmURSS() const; void setVmURSS(qlonglong vmURSS); ///< Physical memory used only by the process, and not counting the code for shared libraries. Set to -1 if unknown + qlonglong vmPSS() const; + void setVmPSS(qlonglong vmPSS); ///< Proportional set size, the amount of private physical memory used by the process + the amount of shared memory used divided over the number of processes using it. + QString name() const; void setName(const QString &name); ///< The name (e.g. "ksysguard", "konversation", "init") @@ -196,6 +199,8 @@ qlonglong& vmURSSChange() const; // REF, make non-ref later! ///< The change in vmURSS since last update, in KiB + qlonglong vmPSSChange() const; ///< The change in vmPSS since last update, in KiB. + unsigned long& pixmapBytes() const; // REF, make non-ref later! ///< The number of bytes used for pixmaps/images and not counted by vmRSS or vmURSS bool& hasManagedGuiWindow() const; // REF, make non-ref later! @@ -249,7 +254,8 @@ Status = 0x1000, Login = 0x2000, IO = 0x4000, - NumThreads = 0x8000 + NumThreads = 0x8000, + VmPSS = 0x10000, }; Q_DECLARE_FLAGS(Changes, Change) diff --git a/processcore/process.cpp b/processcore/process.cpp --- a/processcore/process.cpp +++ b/processcore/process.cpp @@ -56,9 +56,11 @@ qlonglong vmSize; qlonglong vmRSS; qlonglong vmURSS; + qlonglong vmPSS; qlonglong vmSizeChange; qlonglong vmRSSChange; qlonglong vmURSSChange; + qlonglong vmPSSChange; unsigned long pixmapBytes; bool hasManagedGuiWindow; QString name; @@ -182,9 +184,11 @@ d->vmSize=0; d->vmRSS = 0; d->vmURSS = 0; + d->vmPSS = 0; d->vmSizeChange = 0; d->vmRSSChange = 0; d->vmURSSChange = 0; + d->vmPSSChange = 0; d->pixmapBytes = 0; d->hasManagedGuiWindow = false; d->status=OtherStatus; @@ -359,6 +363,11 @@ return d->vmURSS; } +qlonglong Process::vmPSS() const +{ + return d->vmPSS; +} + qlonglong& Process::vmSizeChange() const { return d->vmSizeChange; @@ -374,6 +383,11 @@ return d->vmURSSChange; } +qlonglong Process::vmPSSChange() const +{ + return d->vmPSSChange; +} + unsigned long& Process::pixmapBytes() const { return d->pixmapBytes; @@ -678,6 +692,20 @@ d->changes |= Process::VmURSS; } +void Process::setVmPSS(qlonglong pss) +{ + if (d->vmPSSChange != 0 || d->vmPSS != 0) { + d->vmPSSChange = pss - d->vmPSS; + } + + if (d->vmPSS == pss) { + return; + } + + d->vmPSS = pss; + d->changes |= Process::VmPSS; +} + void Process::setName(const QString &_name) { if(d->name == _name) return; d->name = _name; diff --git a/processcore/processes_linux_p.cpp b/processcore/processes_linux_p.cpp --- a/processcore/processes_linux_p.cpp +++ b/processcore/processes_linux_p.cpp @@ -114,6 +114,7 @@ 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; @@ -455,6 +456,27 @@ 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) { @@ -557,6 +579,7 @@ 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; diff --git a/processui/ProcessModel.h b/processui/ProcessModel.h --- a/processui/ProcessModel.h +++ b/processui/ProcessModel.h @@ -138,7 +138,7 @@ * setup header function, and make sure you increase PROCESSHEADERVERSION. This will ensure * that old saved settings won't be used */ -#define PROCESSHEADERVERSION 9 +#define PROCESSHEADERVERSION 10 enum { HeadingName=0, HeadingUser, HeadingPid, @@ -157,7 +157,8 @@ HeadingXMemory, HeadingXTitle, HeadingCGroup, - HeadingMACContext + HeadingMACContext, + HeadingVmPSS, }; enum { UidRole = Qt::UserRole, SortingValueRole, WindowIdRole, PlainValueRole, PercentageRole, PercentageHistoryRole }; diff --git a/processui/ProcessModel.cpp b/processui/ProcessModel.cpp --- a/processui/ProcessModel.cpp +++ b/processui/ProcessModel.cpp @@ -259,6 +259,9 @@ qlonglong memoryRight = (processRight->vmURSS() != -1) ? processRight->vmURSS() : processRight->vmRSS(); return memoryLeft > memoryRight; } + case HeadingVmPSS: { + return processLeft->vmPSS() > processRight->vmPSS(); + } case HeadingStartTime: { return processLeft->startTime() > processRight->startTime(); } @@ -787,6 +790,11 @@ index = q->createIndex(row, ProcessModel::HeadingUser, process); emit q->dataChanged(index, index); } + if (process->changes() & KSysGuard::Process::VmPSS) { + totalUpdated++; + auto index = q->createIndex(row, ProcessModel::HeadingVmPSS, process); + emit q->dataChanged(index, index); + } if(process->changes() & KSysGuard::Process::Name) { totalUpdated++; QModelIndex index = q->createIndex(row, ProcessModel::HeadingName, process); @@ -981,6 +989,7 @@ case HeadingUser: case HeadingCPUUsage: case HeadingCPUTime: + case HeadingVmPSS: return QVariant(Qt::AlignCenter); } @@ -1037,6 +1046,8 @@ return i18n("The control group (cgroup) where this process belongs."); case HeadingMACContext: return i18n("Mandatory Access Control (SELinux or AppArmor) context for this process."); + case HeadingVmPSS: + return i18n("The amount of private physical memory used by a process, with the amount of shared memory divided by the amount of processes using that shared memory added."); default: return QVariant(); } @@ -1095,6 +1106,8 @@ return i18n("Technical information: This shows Linux Control Group (cgroup) membership, retrieved from /proc/[pid]/cgroup. Control groups are used by Systemd and containers for limiting process group's usage of resources and to monitor them."); case HeadingMACContext: return i18n("Technical information: This shows Mandatory Access Control (SELinux or AppArmor) context, retrieved from /proc/[pid]/attr/current."); + case HeadingVmPSS: + return i18n("Technical information: This is often referred to as \"Proportional Set Size\" and is the closest approximation of the real amount of total memory used by a process. Note that the number of applications sharing shared memory is determined per shared memory section and thus can vary per memory section."); default: return QVariant(); } @@ -1421,6 +1434,8 @@ return process->cGroup(); case HeadingMACContext: return process->macContext(); + case HeadingVmPSS: + return process->vmPSS() >= 0 ? formatMemoryInfo(process->vmPSS(), d->mUnits, true) : QVariant{}; default: return QVariant(); } @@ -1676,7 +1691,25 @@ #endif return QVariant(QVariant::String); } - + case HeadingVmPSS: { + if(process->vmPSS() == -1) { + return xi18nc("@info:tooltip", "Your system does not seem to have this information available to be read."); + } + if(d->mMemTotal > 0 ) { + return xi18nc( + "@info:tooltip", + "Total memory usage: %1 out of %2 (%3 %)", + format.formatByteSize(process->vmPSS() * 1024), + format.formatByteSize(d->mMemTotal * 1024), + qRound(process->vmPSS() * 1000.0 / d->mMemTotal) / 10.0 + ); + } else { + return xi18nc( + "@info:tooltip", + "Shared library memory usage: %1", format.formatByteSize(process->vmPSS() * 1024) + ); + } + } default: return QVariant(QVariant::String); } @@ -1697,6 +1730,7 @@ case HeadingVmSize: case HeadingIoWrite: case HeadingIoRead: + case HeadingVmPSS: return QVariant(Qt::AlignRight | Qt::AlignVCenter); default: return QVariant(Qt::AlignLeft | Qt::AlignVCenter); @@ -1804,6 +1838,8 @@ return process->cGroup(); case HeadingMACContext: return process->macContext(); + case HeadingVmPSS: + return process->vmPSS() >= 0 ? process->vmPSS() : QVariant{}; default: return QVariant(); } @@ -1845,6 +1881,11 @@ if(process->vmURSS() == -1 || d->mMemTotal <= 0) return -1; return float(process->vmRSS() - process->vmURSS())/d->mMemTotal; + case HeadingVmPSS: + if (process->vmPSS() == -1 || d->mMemTotal <= 0) { + return -1; + } + return float(process->vmPSS()) / d->mMemTotal; default: return -1; } @@ -1989,6 +2030,7 @@ #endif headings << i18nc("process heading", "CGroup"); headings << i18nc("process heading", "MAC Context"); + headings << i18nc("process heading", "Total Memory"); if(d->mHeadings.isEmpty()) { // If it's empty, this is the first time this has been called, so insert the headings beginInsertColumns(QModelIndex(), 0, headings.count()-1); diff --git a/processui/ksysguardprocesslist.cpp b/processui/ksysguardprocesslist.cpp --- a/processui/ksysguardprocesslist.cpp +++ b/processui/ksysguardprocesslist.cpp @@ -342,6 +342,7 @@ d->mUi->treeView->header()->hideSection(ProcessModel::HeadingXMemory); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingCGroup); d->mUi->treeView->header()->hideSection(ProcessModel::HeadingMACContext); + d->mUi->treeView->header()->hideSection(ProcessModel::HeadingVmPSS); // NOTE! After this is all setup, the settings for the header are restored // from the user's last run. (in restoreHeaderState) // So making changes here only affects the default settings. To @@ -764,7 +765,7 @@ d->mModel.ioInformation() == ProcessModel::SyscallsRate || d->mModel.ioInformation() == ProcessModel::ActualBytesRate; - if( index == ProcessModel::HeadingVmSize || index == ProcessModel::HeadingMemory || index == ProcessModel::HeadingXMemory || index == ProcessModel::HeadingSharedMemory || ( (index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) && d->mModel.ioInformation() != ProcessModel::Syscalls)) { + if( index == ProcessModel::HeadingVmSize || index == ProcessModel::HeadingMemory || index == ProcessModel::HeadingXMemory || index == ProcessModel::HeadingSharedMemory || index == ProcessModel::HeadingVmPSS || ( (index == ProcessModel::HeadingIoRead || index == ProcessModel::HeadingIoWrite) && d->mModel.ioInformation() != ProcessModel::Syscalls)) { //If the user right clicks on a column that contains a memory size, show a toggle option for displaying //the memory in different units. e.g. "2000 k" or "2 m" menu.addSeparator()->setText(i18n("Display Units"));