[procps] PSS and USS support for ps

  • From: Petr Malat <oss@xxxxxxxxx>
  • To: procps@xxxxxxxxxxxxx
  • Date: Tue, 21 Apr 2015 14:32:29 +0200

Hi!
I've added support to gather information from /proc/PID/smaps, which
allows ps to display PSS and USS:

pss PSS proportional set size, the non-swapped physical
memory, with shared memory proportionally accounted
to all tasks mapping it (in kilobytes). For example
if 4 tasks share 1 MiB, their PSS increases by 256 KiB.

uss USS unique set size, the non-swapped physical memory, which
is not shared with an another task (in kilobytes).

Another fields (e.g. swap, dirty...) can be easilly added, if you think
it makes sense.

Cheers,
Petr
diff -Naurp procps-ng-3.3.10/proc/Makefile.am
procps-ng-3.3.10-new/proc/Makefile.am
--- procps-ng-3.3.10/proc/Makefile.am 2014-09-23 13:40:36.000000000 +0200
+++ procps-ng-3.3.10-new/proc/Makefile.am 2015-04-17 11:48:49.508640976
+0200
@@ -52,6 +52,8 @@ libprocps_la_SOURCES = \
sig.h \
slab.c \
slab.h \
+ smaps.c \
+ smaps.h \
sysinfo.c \
sysinfo.h \
version.c \
@@ -70,6 +72,7 @@ libprocps_la_include_HEADERS = \
readproc.h \
sig.h \
slab.h \
+ smaps.h \
sysinfo.h \
version.h \
wchan.h \
diff -Naurp procps-ng-3.3.10/proc/Makefile.in
procps-ng-3.3.10-new/proc/Makefile.in
--- procps-ng-3.3.10/proc/Makefile.in 2014-09-23 13:52:51.709224022 +0200
+++ procps-ng-3.3.10-new/proc/Makefile.in 2015-04-17 15:04:22.699196391
+0200
@@ -133,8 +133,8 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libprocps_la_DEPENDENCIES =
am_libprocps_la_OBJECTS = alloc.lo devname.lo escape.lo ksym.lo \
- pwcache.lo readproc.lo sig.lo slab.lo sysinfo.lo version.lo \
- whattime.lo
+ pwcache.lo readproc.lo sig.lo slab.lo smaps.lo sysinfo.lo \
+ version.lo whattime.lo
libprocps_la_OBJECTS = $(am_libprocps_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -402,6 +402,8 @@ libprocps_la_SOURCES = \
sig.h \
slab.c \
slab.h \
+ smaps.c \
+ smaps.h \
sysinfo.c \
sysinfo.h \
version.c \
@@ -420,6 +422,7 @@ libprocps_la_include_HEADERS = \
readproc.h \
sig.h \
slab.h \
+ smaps.h \
sysinfo.h \
version.h \
wchan.h \
@@ -514,6 +517,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readproc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sig.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slab.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smaps.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysinfo.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/whattime.Plo@am__quote@
diff -Naurp procps-ng-3.3.10/proc/readproc.c
procps-ng-3.3.10-new/proc/readproc.c
--- procps-ng-3.3.10/proc/readproc.c 2014-09-23 13:40:36.000000000 +0200
+++ procps-ng-3.3.10-new/proc/readproc.c 2015-04-17 17:28:31.366723276
+0200
@@ -26,6 +26,7 @@
#include "pwcache.h"
#include "devname.h"
#include "procps.h"
+#include "smaps.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
@@ -542,6 +543,18 @@ static void sd2proc(proc_t *restrict p)
p->sd_uunit = strdup("-");
}
#endif
+
+static void smaps2proc(proc_t *restrict p) {
+ struct smap_stat statbuf;
+
+ if (get_smap_stat(p->tgid, &statbuf)) {
+ p->pss = -1;
+ p->uss = -1;
+ } else {
+ p->pss = statbuf.pss;
+ p->uss = statbuf.private_clean + statbuf.private_dirty;
+ }
+}
///////////////////////////////////////////////////////////////////////


@@ -939,6 +952,11 @@ static proc_t* simple_readproc(PROCTAB *
if (unlikely(flags & PROC_FILLSYSTEMD)) // get sd-login.h stuff
sd2proc(p);
#endif
+
+ if (unlikely(flags & PROC_FILLSMAPS)) {
+ smaps2proc(p);
+ }
+
return p;
next_proc:
return NULL;
diff -Naurp procps-ng-3.3.10/proc/readproc.h
procps-ng-3.3.10-new/proc/readproc.h
--- procps-ng-3.3.10/proc/readproc.h 2014-09-23 13:40:36.000000000 +0200
+++ procps-ng-3.3.10-new/proc/readproc.h 2015-04-17 15:40:29.655674897
+0200
@@ -181,6 +181,7 @@ typedef struct proc_t {
*sd_unit, // n/a systemd system unit id
*sd_uunit; // n/a systemd user unit id
#endif
+ long pss, uss;
} proc_t;

// PROCTAB: data structure holding the persistent information readproc needs
@@ -292,6 +293,7 @@ extern proc_t * get_proc_stats(pid_t pid
#define PROC_FILLOOM 0x0800 // fill in proc_t oom_score and oom_adj
#define PROC_FILLNS 0x8000 // fill in proc_t namespace information
#define PROC_FILLSYSTEMD 0x80000 // fill in proc_t systemd information
+#define PROC_FILLSMAPS 0x100000 // read smaps

#define PROC_LOOSE_TASKS 0x2000 // treat threads as if they were processes

diff -Naurp procps-ng-3.3.10/proc/smaps.c procps-ng-3.3.10-new/proc/smaps.c
--- procps-ng-3.3.10/proc/smaps.c 1970-01-01 01:00:00.000000000 +0100
+++ procps-ng-3.3.10-new/proc/smaps.c 2015-04-17 17:37:54.337898112 +0200
@@ -0,0 +1,161 @@
+/*
+ * smaps.c - smap related functions for libproc
+ *
+ * Petr Malat <oss@xxxxxxxxx>
+ *
+ * Copyright (C) 2015 Petr Malat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "smaps.h"
+
+/* Provide a generic access to smaps. It can be used by other tools in
+ * the future */
+
+#include <limits.h>
+
+struct smap_info {
+ unsigned long start;
+ int perm;
+ dev_t dev;
+ off_t offset;
+ ino_t inode;
+ char image[PATH_MAX];
+
+ unsigned long long size;
+ unsigned long long rss;
+ unsigned long long pss;
+ unsigned long long shared_clean;
+ unsigned long long shared_dirty;
+ unsigned long long private_clean;
+ unsigned long long private_dirty;
+ unsigned long long referenced;
+ unsigned long long anon;
+ unsigned long long anon_huge;
+ unsigned long long swap;
+ unsigned long long kernel_page_size;
+ unsigned long long mmu_page_size;
+};
+
+
+
+#define ML(name, member) { name, offsetof(struct smap_info, member) }
+static struct {
+ char *name;
+ int off;
+} mapping[] = {
+ ML("Size", size),
+ ML("Rss", rss),
+ ML("Pss", pss),
+ ML("Shared_Clean", shared_clean),
+ ML("Shared_Dirty", shared_dirty),
+ ML("Private_Clean", private_clean),
+ ML("Private_Dirty", private_dirty),
+ ML("Referenced", referenced),
+ ML("Anonymous", anon),
+ ML("AnonHugePages", anon_huge),
+ ML("Swap", swap),
+ ML("KernelPageSize", kernel_page_size),
+ ML("MMUPageSize", mmu_page_size),
+};
+
+static int get_smap_area(void *iter, struct smap_info *info)
+{
+ char tmp[32], tmp2[32];
+ int rtn, major, minor, c, i;
+ long long size;
+ FILE *f = iter;
+
+ rtn = fscanf(f, "%llx-%*llx %s %llx %x:%x %lu",
+ &info->start, tmp, &info->offset, &major, &minor, &info->inode);
+
+ if (rtn != 6) {
+ return -1;
+ }
+
+ while (' ' == (c = fgetc(f))) {}
+ ungetc(c, f);
+
+ for (i = 0; '\n' != (c = fgetc(f)) && i < sizeof info->image - 1; i++) {
+ info->image[i] = c;
+ }
+ info->image[i] = 0;
+
+ while (3 == fscanf(f, " %31[A-Z]%31[A-Za-z_]: %lld kB", tmp, tmp2,
&size)) {
+ strncat(tmp, tmp2, sizeof tmp);
+ for (i = 0; i < sizeof mapping/sizeof mapping[0]; i++) {
+ if (0 == strcmp(mapping[i].name, tmp)) {
+ *(unsigned long long*)((char*)info +
mapping[i].off) = size;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void *get_smap_iter(pid_t pid)
+{
+ char tmp[20];
+
+ snprintf(tmp, sizeof tmp, "/proc/%d/smaps", pid);
+ return fopen(tmp, "r");
+}
+
+static void put_smap_iter(void *iter)
+{
+ fclose((FILE *)iter);
+}
+
+
+
+
+/** Get statistics from smaps */
+int get_smap_stat(pid_t pid, struct smap_stat *statbuf)
+{
+ struct smap_info info;
+ void *iter;
+
+ memset(statbuf, 0, sizeof *statbuf);
+
+ iter = get_smap_iter(pid);
+ if (!iter) {
+ return -1;
+ }
+
+ while (!get_smap_area(iter, &info)) {
+ statbuf->size += info.size;
+ statbuf->rss += info.rss;
+ statbuf->pss += info.pss;
+ statbuf->shared_clean += info.shared_clean;
+ statbuf->shared_dirty += info.shared_dirty;
+ statbuf->private_clean += info.private_clean;
+ statbuf->private_dirty += info.private_dirty;
+ statbuf->anon += info.anon;
+ statbuf->anon_huge += info.anon_huge;
+ statbuf->swap += info.swap;
+
+ statbuf->maps++;
+ }
+
+ put_smap_iter(iter);
+
+ return statbuf->maps ? 0 : -1;
+}
diff -Naurp procps-ng-3.3.10/proc/smaps.h procps-ng-3.3.10-new/proc/smaps.h
--- procps-ng-3.3.10/proc/smaps.h 1970-01-01 01:00:00.000000000 +0100
+++ procps-ng-3.3.10-new/proc/smaps.h 2015-04-17 17:35:18.561126916 +0200
@@ -0,0 +1,22 @@
+#ifndef PROC_SMAPS_H
+#define PROC_SMAPS_H
+
+#include <sys/types.h>
+
+struct smap_stat {
+ unsigned long long size;
+ unsigned long long rss;
+ unsigned long long pss;
+ unsigned long long shared_clean;
+ unsigned long long shared_dirty;
+ unsigned long long private_clean;
+ unsigned long long private_dirty;
+ unsigned long long anon;
+ unsigned long long anon_huge;
+ unsigned long long swap;
+ unsigned long long maps;
+};
+
+int get_smap_stat(pid_t pid, struct smap_stat *stat);
+
+#endif // PROC_SMAPS_H
diff -Naurp procps-ng-3.3.10/ps/display.c procps-ng-3.3.10-new/ps/display.c
--- procps-ng-3.3.10/ps/display.c 2014-09-23 13:40:36.000000000 +0200
+++ procps-ng-3.3.10-new/ps/display.c 2015-04-17 15:46:41.318333963 +0200
@@ -239,7 +239,7 @@ static unsigned task_format_needs;

#define needs_for_format (proc_format_needs|task_format_needs)

-#define PROC_ONLY_FLAGS
(PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM|PROC_FILLCGROUP)
+#define PROC_ONLY_FLAGS
(PROC_FILLENV|PROC_FILLARG|PROC_FILLCOM|PROC_FILLMEM|PROC_FILLCGROUP|PROC_FILLSMAPS)

/***** munge lists and determine openproc() flags */
static void lists_and_needs(void){
diff -Naurp procps-ng-3.3.10/ps/output.c procps-ng-3.3.10-new/ps/output.c
--- procps-ng-3.3.10/ps/output.c 2014-09-23 13:40:36.000000000 +0200
+++ procps-ng-3.3.10-new/ps/output.c 2015-04-17 17:04:33.855111522 +0200
@@ -149,6 +149,8 @@ CMP_SMALL(priority)
CMP_SMALL(nlwp)
CMP_SMALL(nice) /* priority */
CMP_INT(rss) /* resident set size from stat file */ /* vm_rss, resident */
+CMP_INT(pss) /* proportional set size from smaps file */
+CMP_INT(uss) /* unique set size from smaps file */
CMP_INT(alarm)
CMP_INT(size) /* total pages */ /* vm_size, vsize */
CMP_INT(resident) /* resident pages */ /* vm_rss, rss */
@@ -959,6 +961,22 @@ static int pr_rss(char *restrict const o
return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
}

+static int pr_pss(char *restrict const outbuf, const proc_t *restrict const
pp){
+ if (pp->pss == -1) {
+ return snprintf(outbuf, COLWID, "?");
+ } else {
+ return snprintf(outbuf, COLWID, "%lu", pp->pss);
+ }
+}
+
+static int pr_uss(char *restrict const outbuf, const proc_t *restrict const
pp){
+ if (pp->uss == -1) {
+ return snprintf(outbuf, COLWID, "?");
+ } else {
+ return snprintf(outbuf, COLWID, "%lu", pp->uss);
+ }
+}
+
/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
static int pr_pmem(char *restrict const outbuf, const proc_t *restrict const
pp){
unsigned long pmem = 0;
@@ -1394,6 +1412,7 @@ static int pr_t_left2(char *restrict con
#ifdef WITH_SYSTEMD
#define SD PROC_FILLSYSTEMD /* retrieve systemd stuff */
#endif
+#define MAP PROC_FILLSMAPS /* read statm */
#define SGRP PROC_FILLSTATUS | PROC_FILLSUPGRP /* supgid -> supgrp (names) */
#define CGRP PROC_FILLCGROUP | PROC_EDITCGRPCVT /* read cgroup */

@@ -1560,6 +1579,7 @@ static const format_struct format_array[
{"projid", "PROJID", pr_nop, sr_nop, 5, 0, SUN, PO|RIGHT},
{"pset", "PSET", pr_nop, sr_nop, 4, 0, DEC, TO|RIGHT},
{"psr", "PSR", pr_psr, sr_nop, 3, 0, DEC, TO|RIGHT},
+{"pss", "PSS", pr_pss, sr_pss, 5, MAP, XXX, PO|RIGHT},
{"psxpri", "PPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
{"re", "RE", pr_nop, sr_nop, 3, 0, BSD, AN|RIGHT},
{"resident", "RES", pr_nop, sr_resident, 5,MEM, LNX, PO|RIGHT},
@@ -1657,6 +1677,7 @@ static const format_struct format_array[
{"userns", "USERNS", pr_userns, sr_userns, 10, NS, LNX, ET|RIGHT},
{"usertime", "USER", pr_nop, sr_nop, 4, 0, DEC, ET|RIGHT},
{"usrpri", "UPR", pr_nop, sr_nop, 3, 0, DEC, TO|RIGHT},
/*upr*/
+{"uss", "USS", pr_uss, sr_uss, 5, MAP, XXX, PO|RIGHT},
{"util", "C", pr_c, sr_pcpu, 2, 0, SGI, ET|RIGHT},
// not sure about "C"
{"utime", "UTIME", pr_nop, sr_utime, 6, 0, LNx, ET|RIGHT},
{"utsns", "UTSNS", pr_utsns, sr_utsns, 10, NS, LNX, ET|RIGHT},
diff -Naurp procps-ng-3.3.10/ps/ps.1 procps-ng-3.3.10-new/ps/ps.1
--- procps-ng-3.3.10/ps/ps.1 2014-09-23 13:40:36.000000000 +0200
+++ procps-ng-3.3.10-new/ps/ps.1 2015-04-21 14:08:50.572083561 +0200
@@ -1496,6 +1496,12 @@ psr PSR T{
processor that process is currently assigned to.
T}

+pss PSS T{
+proportional set size, the non\-swapped physical memory, with shared memory
+proportionally accounted to all tasks mapping it (in kilobytes). For example
+if 4 tasks share 1 MiB, their PSS increases by 256 KiB.
+T}
+
rgid RGID T{
real group ID.
T}
@@ -1803,6 +1809,11 @@ userns USERNS T{
Unique inode number describing the namespace the process belongs to. See
namespaces(7).
T}

+uss USS T{
+unique set size, the non\-swapped physical memory, which is not shared with an
+another task (in kilobytes).
+T}
+
utsns UTSNS T{
Unique inode number describing the namespace the process belongs to. See
namespaces(7).
T}

Other related posts: