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"));