library: add support for smaps_rollup file, <pids> api
A couple of people have suggested that smaps_rollup be
added to the ps program and/or top program. This patch
is intended to set the stage for just such extensions.
There are currently 20 displayable items in the rollup
file. And newlib sometimes uses sscanf when populating
the target, sometimes hsearch and one customized gperf
approach. None of these fit well with the smaps items.
Thus, an approach using a simple table lookup was used
and, by disabling 1 code line, it could be made immune
from changes to the items order (unlike a sscanf call)
and doesn't carry the greater cost of a hsearch/gperf.
Note: The next patch will allow top to display some of
these new fields. Then, it'll be possible to determine
the colossal costs of accessing the smaps_rollup file.
Here is a small preview of just what you will discover
when using the command 'time top/top -d0 -n1000' while
configured with just two fields: PID + 1 memory field.
------------------------------------ as a regular user
with only PID + RES (statm)
real 0m2.605s
user 0m1.060s
sys 0m1.377s
with only PID + RSS (smaps)
real 0m26.397s 10x more costly
user 0m1.253s
sys 0m24.915s
----------------- as a root (thus smaps for all tasks)
with only PID + RES (statm)
real 0m2.651s
user 0m1.177s
sys 0m1.286s
with only PID + RSS (smaps)
real 0m33.040s 12x more costly
user 0m1.256s
sys 0m31.533s
Reference(s):
. ps: expose shared/private memory separately
https://gitlab.com/procps-ng/procps/-/issues/201
. top/ps: add support for PSS reporting
https://gitlab.com/procps-ng/procps/-/issues/112
Signed-off-by: Jim Warner <james.warner@comcast.net>
This commit is contained in:
42
proc/pids.c
42
proc/pids.c
@@ -240,6 +240,26 @@ DUP_set(SIGCATCH, sigcatch)
|
||||
DUP_set(SIGIGNORE, sigignore)
|
||||
DUP_set(SIGNALS, signal)
|
||||
DUP_set(SIGPENDING, _sigpnd)
|
||||
REG_set(SMAP_ANONYMOUS, ul_int, smap_Anonymous)
|
||||
REG_set(SMAP_HUGETBL_PRV, ul_int, smap_Private_Hugetlb)
|
||||
REG_set(SMAP_HUGETBL_SHR, ul_int, smap_Shared_Hugetlb)
|
||||
REG_set(SMAP_HUGE_ANON, ul_int, smap_AnonHugePages)
|
||||
REG_set(SMAP_HUGE_FILE, ul_int, smap_FilePmdMapped)
|
||||
REG_set(SMAP_HUGE_SHMEM, ul_int, smap_ShmemPmdMapped)
|
||||
REG_set(SMAP_LAZY_FREE, ul_int, smap_LazyFree)
|
||||
REG_set(SMAP_LOCKED, ul_int, smap_Locked)
|
||||
REG_set(SMAP_PRV_CLEAN, ul_int, smap_Private_Clean)
|
||||
REG_set(SMAP_PRV_DIRTY, ul_int, smap_Private_Dirty)
|
||||
REG_set(SMAP_PSS, ul_int, smap_Pss)
|
||||
REG_set(SMAP_PSS_ANON, ul_int, smap_Pss_Anon)
|
||||
REG_set(SMAP_PSS_FILE, ul_int, smap_Pss_File)
|
||||
REG_set(SMAP_PSS_SHMEM, ul_int, smap_Pss_Shmem)
|
||||
REG_set(SMAP_REFERENCED, ul_int, smap_Referenced)
|
||||
REG_set(SMAP_RSS, ul_int, smap_Rss)
|
||||
REG_set(SMAP_SHR_CLEAN, ul_int, smap_Shared_Clean)
|
||||
REG_set(SMAP_SHR_DIRTY, ul_int, smap_Shared_Dirty)
|
||||
REG_set(SMAP_SWAP, ul_int, smap_Swap)
|
||||
REG_set(SMAP_SWAP_PSS, ul_int, smap_SwapPss)
|
||||
REG_set(STATE, s_ch, state)
|
||||
STR_set(SUPGIDS, supgid)
|
||||
STR_set(SUPGROUPS, supgrp)
|
||||
@@ -351,6 +371,7 @@ srtDECL(noop) {
|
||||
#define f_lxc PROC_FILL_LXC
|
||||
#define f_ns PROC_FILLNS
|
||||
#define f_oom PROC_FILLOOM
|
||||
#define f_smaps PROC_FILLSMAPS
|
||||
#define f_stat PROC_FILLSTAT
|
||||
#define f_statm PROC_FILLMEM
|
||||
#define f_status PROC_FILLSTATUS
|
||||
@@ -497,6 +518,26 @@ static struct {
|
||||
{ RS(SIGIGNORE), f_status, FF(str), QS(str), 0, TS(str) },
|
||||
{ RS(SIGNALS), f_status, FF(str), QS(str), 0, TS(str) },
|
||||
{ RS(SIGPENDING), f_status, FF(str), QS(str), 0, TS(str) },
|
||||
{ RS(SMAP_ANONYMOUS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_HUGETBL_PRV), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_HUGETBL_SHR), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_HUGE_ANON), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_HUGE_FILE), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_HUGE_SHMEM), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_LAZY_FREE), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_LOCKED), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_PRV_CLEAN), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_PRV_DIRTY), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_PSS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_PSS_ANON), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_PSS_FILE), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_PSS_SHMEM), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_REFERENCED), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_RSS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_SHR_CLEAN), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_SHR_DIRTY), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_SWAP), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(SMAP_SWAP_PSS), f_smaps, NULL, QS(ul_int), 0, TS(ul_int) },
|
||||
{ RS(STATE), f_either, NULL, QS(s_ch), 0, TS(s_ch) },
|
||||
{ RS(SUPGIDS), f_status, FF(str), QS(str), 0, TS(str) },
|
||||
{ RS(SUPGROUPS), x_supgrp, FF(str), QS(str), 0, TS(str) },
|
||||
@@ -551,6 +592,7 @@ enum pids_item PIDS_logical_end = MAXTABLE(Item_table);
|
||||
#undef f_lxc
|
||||
#undef f_ns
|
||||
#undef f_oom
|
||||
#undef f_smaps
|
||||
//#undef f_stat // needed later
|
||||
#undef f_statm
|
||||
//#undef f_status // needed later
|
||||
|
||||
20
proc/pids.h
20
proc/pids.h
@@ -125,6 +125,26 @@ enum pids_item {
|
||||
PIDS_SIGIGNORE, // str status: SigIgn
|
||||
PIDS_SIGNALS, // str status: ShdPnd
|
||||
PIDS_SIGPENDING, // str status: SigPnd
|
||||
PIDS_SMAP_ANONYMOUS, // ul_int smaps_rollup: Anonymous
|
||||
PIDS_SMAP_HUGETBL_PRV, // ul_int smaps_rollup: Private_Hugetlb
|
||||
PIDS_SMAP_HUGETBL_SHR, // ul_int smaps_rollup: Shared_Hugetlb
|
||||
PIDS_SMAP_HUGE_ANON, // ul_int smaps_rollup: AnonHugePages
|
||||
PIDS_SMAP_HUGE_FILE, // ul_int smaps_rollup: FilePmdMapped
|
||||
PIDS_SMAP_HUGE_SHMEM, // ul_int smaps_rollup: ShmemPmdMapped
|
||||
PIDS_SMAP_LAZY_FREE, // ul_int smaps_rollup: LazyFree
|
||||
PIDS_SMAP_LOCKED, // ul_int smaps_rollup: Locked
|
||||
PIDS_SMAP_PRV_CLEAN, // ul_int smaps_rollup: Private_Clean
|
||||
PIDS_SMAP_PRV_DIRTY, // ul_int smaps_rollup: Private_Dirty
|
||||
PIDS_SMAP_PSS, // ul_int smaps_rollup: Pss
|
||||
PIDS_SMAP_PSS_ANON, // ul_int smaps_rollup: Pss_Anon
|
||||
PIDS_SMAP_PSS_FILE, // ul_int smaps_rollup: Pss_File
|
||||
PIDS_SMAP_PSS_SHMEM, // ul_int smaps_rollup: Pss_Shmem
|
||||
PIDS_SMAP_REFERENCED, // ul_int smaps_rollup: Referenced
|
||||
PIDS_SMAP_RSS, // ul_int smaps_rollup: Rss
|
||||
PIDS_SMAP_SHR_CLEAN, // ul_int smaps_rollup: Shared_Clean
|
||||
PIDS_SMAP_SHR_DIRTY, // ul_int smaps_rollup: Shared_Dirty
|
||||
PIDS_SMAP_SWAP, // ul_int smaps_rollup: Swap
|
||||
PIDS_SMAP_SWAP_PSS, // ul_int smaps_rollup: SwapPss
|
||||
PIDS_STATE, // s_ch stat: state or status: State
|
||||
PIDS_SUPGIDS, // str status: Groups
|
||||
PIDS_SUPGROUPS, // str derived from SUPGIDS, see getgrgid(3)
|
||||
|
||||
@@ -648,6 +648,72 @@ static void io2proc(const char* s, proc_t *restrict P) {
|
||||
&P->syscw, &P->read_bytes, &P->write_bytes, &P->cancelled_write_bytes);
|
||||
}
|
||||
|
||||
// Assuming permissions have allowed the read of smaps_rollup, this
|
||||
// guy will extract some %lu data. Considering the number of items,
|
||||
// we are between small enough to use a sscanf and large enough for
|
||||
// a search.h approach. Thus we roll (get it?) our own custom code.
|
||||
static void smaps2proc (const char* s, proc_t *restrict P) {
|
||||
#define enMAX (int)((sizeof(smaptab) / sizeof(smaptab[0])))
|
||||
// 1st proc_t data field
|
||||
#define fZERO tid
|
||||
// a smaptab entry generator
|
||||
#define mkENT(F) { #F ":", -1, (int)((void*)&q->smap_ ## F - (void*)&q->fZERO) }
|
||||
// make a target field
|
||||
#define mkOBJ(e) ( (unsigned long *)((void *)&P->fZERO + smaptab[e].offs) )
|
||||
static const proc_t *q;
|
||||
static struct {
|
||||
const char *item;
|
||||
int slen;
|
||||
int offs;
|
||||
} smaptab[] = {
|
||||
/* Size smaps only, not rollup */
|
||||
/* KernelPageSize " */
|
||||
/* MMUPageSize " */
|
||||
mkENT(Rss),
|
||||
mkENT(Pss),
|
||||
mkENT(Pss_Anon), /* rollup only, not smaps */
|
||||
mkENT(Pss_File), /* " */
|
||||
mkENT(Pss_Shmem), /* " */
|
||||
mkENT(Shared_Clean),
|
||||
mkENT(Shared_Dirty),
|
||||
mkENT(Private_Clean),
|
||||
mkENT(Private_Dirty),
|
||||
mkENT(Referenced),
|
||||
mkENT(Anonymous),
|
||||
mkENT(LazyFree),
|
||||
mkENT(AnonHugePages),
|
||||
mkENT(ShmemPmdMapped),
|
||||
mkENT(FilePmdMapped),
|
||||
mkENT(Shared_Hugetlb),
|
||||
mkENT(Private_Hugetlb),
|
||||
mkENT(Swap),
|
||||
mkENT(SwapPss),
|
||||
mkENT(Locked)
|
||||
/* THPeligible smaps only, not rollup */
|
||||
/* ProtectionKey " */
|
||||
/* VmFlags " */
|
||||
};
|
||||
char *head, *tail;
|
||||
int i;
|
||||
|
||||
if (smaptab[0].slen < 0) {
|
||||
for (i = 0; i < enMAX; i++)
|
||||
smaptab[i].slen = (int)strlen(smaptab[i].item);
|
||||
}
|
||||
for (i = 0; i < enMAX; i++) {
|
||||
if (!(head = strstr(s, smaptab[i].item)))
|
||||
continue;
|
||||
head += smaptab[i].slen;
|
||||
*mkOBJ(i) = strtoul(head, &tail, 10);
|
||||
// saves some overhead BUT makes us dependent on current order
|
||||
s = tail;
|
||||
}
|
||||
#undef enMAX
|
||||
#undef fZERO
|
||||
#undef mkENT
|
||||
#undef mkOBJ
|
||||
}
|
||||
|
||||
static int file2str(const char *directory, const char *what, struct utlbuf_s *ub) {
|
||||
#define buffGRW 1024
|
||||
char path[PROCPATHLEN];
|
||||
@@ -1054,6 +1120,11 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons
|
||||
io2proc(ub.buf, p);
|
||||
}
|
||||
|
||||
if (flags & PROC_FILLSMAPS) { // read /proc/#/smaps_rollup
|
||||
if (file2str(path, "smaps_rollup", &ub) != -1)
|
||||
smaps2proc(ub.buf, p);
|
||||
}
|
||||
|
||||
if (flags & PROC_FILLMEM) { // read /proc/#/statm
|
||||
if (file2str(path, "statm", &ub) != -1)
|
||||
statm2proc(ub.buf, p);
|
||||
@@ -1169,10 +1240,16 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, proc_t *restrict cons
|
||||
io2proc(ub.buf, t);
|
||||
}
|
||||
|
||||
if (flags & PROC_FILLSMAPS) { // read /proc/#/task/#/smaps_rollup
|
||||
if (file2str(path, "smaps_rollup", &ub) != -1)
|
||||
smaps2proc(ub.buf, t);
|
||||
}
|
||||
|
||||
if (flags & PROC_FILLMEM) { // read /proc/#/task/#/statm
|
||||
if (file2str(path, "statm", &ub) != -1)
|
||||
statm2proc(ub.buf, t);
|
||||
}
|
||||
|
||||
if (flags & PROC_FILLSTATUS) { // read /proc/#/task/#/status
|
||||
if (file2str(path, "status", &ub) != -1) {
|
||||
rc += status2proc(ub.buf, t, 0);
|
||||
|
||||
@@ -104,7 +104,27 @@ typedef struct proc_t {
|
||||
syscw, // io number of write I/O operations
|
||||
read_bytes, // io number of bytes fetched from the storage layer
|
||||
write_bytes, // io number of bytes sent to the storage layer
|
||||
cancelled_write_bytes; // io number of bytes truncating pagecache
|
||||
cancelled_write_bytes, // io number of bytes truncating pagecache
|
||||
smap_Rss, // smaps_rollup mapping currently resident in RAM
|
||||
smap_Pss, // " Rss divided by total processes sharing it
|
||||
smap_Pss_Anon, // " proportional share of 'anonymous' memory
|
||||
smap_Pss_File, // " proportional share of 'file' memory
|
||||
smap_Pss_Shmem, // " proportional share of 'shmem' memory
|
||||
smap_Shared_Clean, // " unmodified shared memory
|
||||
smap_Shared_Dirty, // " altered shared memory
|
||||
smap_Private_Clean, // " unmodified private memory
|
||||
smap_Private_Dirty, // " altered private memory
|
||||
smap_Referenced, // " memory marked as referenced/accessed
|
||||
smap_Anonymous, // " memory not belonging to any file
|
||||
smap_LazyFree, // " memory marked by madvise(MADV_FREE)
|
||||
smap_AnonHugePages, // " memory backed by transparent huge pages
|
||||
smap_ShmemPmdMapped, // " shmem/tmpfs memory backed by huge pages
|
||||
smap_FilePmdMapped, // " file memory backed by huge pages
|
||||
smap_Shared_Hugetlb, // " hugetlbfs backed memory *not* counted in Rss/Pss
|
||||
smap_Private_Hugetlb, // " hugetlbfs backed memory *not* counted in Rss/Pss
|
||||
smap_Swap, // " swapped would-be-anonymous memory (includes swapped out shmem)
|
||||
smap_SwapPss, // " the proportional share of 'Swap' (excludes swapped out shmem)
|
||||
smap_Locked; // " memory amount locked to RAM
|
||||
char
|
||||
*environ, // (special) environment as string (/proc/#/environ)
|
||||
*cmdline, // (special) command line as string (/proc/#/cmdline)
|
||||
@@ -217,6 +237,7 @@ typedef struct PROCTAB {
|
||||
#define PROC_FILL_LUID 0x400000 // fill in proc_t luid (login user id)
|
||||
#define PROC_FILL_EXE 0x200000 // fill in proc_t exe path + pgm name
|
||||
#define PROC_FILLIO 0x01000000 // fill in proc_t io information
|
||||
#define PROC_FILLSMAPS 0x02000000 // fill in proc_t smaps_rollup stuff
|
||||
|
||||
// consider only processes with one of the passed:
|
||||
#define PROC_PID 0x1000 // process id numbers ( 0 terminated)
|
||||
@@ -233,10 +254,10 @@ typedef struct PROCTAB {
|
||||
#define PROC_FILL_SUPGRP ( 0x0400 | PROC_FILLSTATUS ) // obtain supplementary group names
|
||||
|
||||
// it helps to give app code a few spare bits
|
||||
#define PROC_SPARE_1 0x02000000
|
||||
#define PROC_SPARE_2 0x04000000
|
||||
#define PROC_SPARE_3 0x08000000
|
||||
#define PROC_SPARE_4 0x10000000
|
||||
#define PROC_SPARE_1 0x04000000
|
||||
#define PROC_SPARE_2 0x08000000
|
||||
#define PROC_SPARE_3 0x10000000
|
||||
#define PROC_SPARE_4 0x20000000
|
||||
|
||||
// Function definitions
|
||||
// Initialize a PROCTAB structure holding needed call-to-call persistent data
|
||||
|
||||
Reference in New Issue
Block a user