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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user