From a7afe06e6f1b397b7404fbee724a51f88cc8a59c Mon Sep 17 00:00:00 2001 From: Craig Small Date: Sat, 24 Apr 2021 22:38:48 +1000 Subject: [PATCH] library: adding IO accounting This is a modification of MR !122 by @renit1609 to fit the new library. Problem statement: The procps library has no PROC_FILLIO flag to fetch the proc field "/proc/[pid]/io" data process-wise. IO Accounting is not included as part of procps. Requirement: We have a requirement to fetch process wise IO utilization which can be used for monitoring. When looking through the procps library, I see that IO Accounting (/proc/[pid]/io) is not being included as part of procps. There is no such flag like PROC_FILLIO being included in readproc.h . Solution: While looking at the implementation done for other proc fields, I used the spare bits in app code. I renamed PROC_SPARE_1 as PROC_FILLIO, the spare bit from PROC_SPARE_* and used it for fetching /proc/[pid]/io data as part of the procps library similar to other fields. I moved the PROC_SPARE_* bits each by 1 bit to retain the spare bits. Meanwhile added the IO fields in proc_t structure. References: procps-ng/procps!122 procps-ng/procps#184 --- proc/pids.c | 15 +++++++++++++++ proc/pids.h | 7 +++++++ proc/readproc.c | 16 ++++++++++++++++ proc/readproc.h | 18 +++++++++++++----- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/proc/pids.c b/proc/pids.c index 62a4a83b..bacf85ca 100644 --- a/proc/pids.c +++ b/proc/pids.c @@ -193,6 +193,13 @@ REG_set(ID_SUSER, str, suser) REG_set(ID_TGID, s_int, tgid) REG_set(ID_TID, s_int, tid) REG_set(ID_TPGID, s_int, tpgid) +REG_set(IO_READ_BYTES, ul_int, read_bytes) +REG_set(IO_READ_CHARS, ul_int, rchar) +REG_set(IO_READ_OPS, ul_int, syscr) +REG_set(IO_WRITE_BYTES, ul_int, write_bytes) +REG_set(IO_WRITE_CBYTES, ul_int, cancelled_write_bytes) +REG_set(IO_WRITE_CHARS, ul_int, wchar) +REG_set(IO_WRITE_OPS, ul_int, syscw) REG_set(LXCNAME, str, lxcname) CVT_set(MEM_CODE, ul_int, trs) REG_set(MEM_CODE_PGS, ul_int, trs) @@ -341,6 +348,7 @@ srtDECL(noop) { #define f_grp PROC_FILLGRP #define f_login PROC_FILL_LUID #define f_lxc PROC_FILL_LXC +#define f_io PROC_FILLIO #define f_ns PROC_FILLNS #define f_oom PROC_FILLOOM #define f_stat PROC_FILLSTAT @@ -442,6 +450,13 @@ static struct { { RS(ID_TGID), 0, NULL, QS(s_int), 0, TS(s_int) }, // oldflags: free w/ simple_nextpid { RS(ID_TID), 0, NULL, QS(s_int), 0, TS(s_int) }, // oldflags: free w/ simple_nexttid { RS(ID_TPGID), f_stat, NULL, QS(s_int), 0, TS(s_int) }, + { RS(IO_READ_BYTES), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, + { RS(IO_READ_CHARS), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, + { RS(IO_READ_OPS), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, + { RS(IO_WRITE_BYTES), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, + { RS(IO_WRITE_CBYTES), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, + { RS(IO_WRITE_CHARS), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, + { RS(IO_WRITE_OPS), f_io, NULL, QS(ul_int), 0, TS(ul_int) }, { RS(LXCNAME), f_lxc, NULL, QS(str), 0, TS(str) }, // freefunc NULL w/ cached string { RS(MEM_CODE), f_statm, NULL, QS(ul_int), 0, TS(ul_int) }, { RS(MEM_CODE_PGS), f_statm, NULL, QS(ul_int), 0, TS(ul_int) }, diff --git a/proc/pids.h b/proc/pids.h index 60037d7b..f80a8f4a 100644 --- a/proc/pids.h +++ b/proc/pids.h @@ -78,6 +78,13 @@ enum pids_item { PIDS_ID_TGID, // s_int status: Tgid PIDS_ID_TID, // s_int from /proc//task/ PIDS_ID_TPGID, // s_int stat: tty_pgrp + PIDS_IO_READ_BYTES, // ul_int io: bytes read + PIDS_IO_READ_CHARS, // ul_int io: characters read + PIDS_IO_READ_OPS, // ul_int io: read operations + PIDS_IO_WRITE_BYTES, // ul_int io: bytes written + PIDS_IO_WRITE_CBYTES, // ul_int io: cancelled write bytes + PIDS_IO_WRITE_CHARS, // ul_int io: characters written + PIDS_IO_WRITE_OPS, // ul_int io: write operations PIDS_LXCNAME, // str derived from CGROUP 'lxc.payload' PIDS_MEM_CODE, // ul_int derived from MEM_CODE_PGS, as KiB PIDS_MEM_CODE_PGS, // ul_int statm: trs diff --git a/proc/readproc.c b/proc/readproc.c index bc92fa7b..9f96741b 100644 --- a/proc/readproc.c +++ b/proc/readproc.c @@ -641,6 +641,12 @@ static void statm2proc(const char* s, proc_t *restrict P) { &P->trs, &P->lrs, &P->drs, &P->dt); } +static void io2proc(const char* s, proc_t *restrict P) { + int num; + num = sscanf(s, "rchar: %lu wchar: %lu syscr: %lu syscw: %lu read_bytes: %lu write_bytes: %lu cancelled_write_bytes: %lu", + &P->rchar, &P->wchar, &P->syscr, + &P->syscw, &P->read_bytes, &P->write_bytes, &P->cancelled_write_bytes); +} static int file2str(const char *directory, const char *what, struct utlbuf_s *ub) { #define buffGRW 1024 @@ -1043,6 +1049,11 @@ static proc_t* simple_readproc(PROCTAB *restrict const PT, proc_t *restrict cons rc += stat2proc(ub.buf, p); } + if (flags & PROC_FILLIO) { // read /proc/#/io + if (file2str(path, "io", &ub) != -1) + io2proc(ub.buf, p); + } + if (flags & PROC_FILLMEM) { // read /proc/#/statm if (file2str(path, "statm", &ub) != -1) statm2proc(ub.buf, p); @@ -1153,6 +1164,11 @@ static proc_t* simple_readtask(PROCTAB *restrict const PT, proc_t *restrict cons rc += stat2proc(ub.buf, t); } + if (flags & PROC_FILLIO) { // read /proc/#/io + if (file2str(path, "io", &ub) != -1) + io2proc(ub.buf, t); + } + if (flags & PROC_FILLMEM) { // read /proc/#/task/#statm if (file2str(path, "statm", &ub) != -1) statm2proc(ub.buf, t); diff --git a/proc/readproc.h b/proc/readproc.h index 46cb825a..992eb53e 100644 --- a/proc/readproc.h +++ b/proc/readproc.h @@ -97,7 +97,14 @@ typedef struct proc_t { min_flt, // stat number of minor page faults since process start maj_flt, // stat number of major page faults since process start cmin_flt, // stat cumulative min_flt of process and child processes - cmaj_flt; // stat cumulative maj_flt of process and child processes + cmaj_flt, // stat cumulative maj_flt of process and child processes + rchar, // io characters read + wchar, // io characters written + syscr, // io number of read I/O operations + 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 char *environ, // (special) environment as string (/proc/#/environ) *cmdline, // (special) command line as string (/proc/#/cmdline) @@ -209,6 +216,7 @@ typedef struct PROCTAB { #define PROC_FILL_LXC 0x800000 // fill in proc_t lxcname, if possible #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 // consider only processes with one of the passed: #define PROC_PID 0x1000 // process id numbers ( 0 terminated) @@ -225,10 +233,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 0x01000000 -#define PROC_SPARE_2 0x02000000 -#define PROC_SPARE_3 0x04000000 -#define PROC_SPARE_4 0x08000000 +#define PROC_SPARE_1 0x02000000 +#define PROC_SPARE_2 0x04000000 +#define PROC_SPARE_3 0x08000000 +#define PROC_SPARE_4 0x10000000 // Function definitions // Initialize a PROCTAB structure holding needed call-to-call persistent data