Linux: overhaul io process fields

- avoid UBSAN conversions
- print N/A on no data (i.e. as unprivileged user)
- fix rate calculation to show bytes (instead of a thousandth)
- print bytes as human number (i.e. 8MB) instead of 8388608
- stabilize sorting by adjusting NAN values to very tiny negative number
This commit is contained in:
Christian Göttsche 2021-01-27 15:11:48 +01:00 committed by BenBE
parent fee744abd2
commit fdaa15bd8d
5 changed files with 70 additions and 47 deletions

View File

@ -53,17 +53,16 @@ void Process_humanNumber(RichString* str, unsigned long long number, bool colori
char buffer[10]; char buffer[10];
int len; int len;
int largeNumberColor = CRT_colors[LARGE_NUMBER]; int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES]; int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
int processGigabytesColor = CRT_colors[PROCESS_GIGABYTES]; int processGigabytesColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS];
int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];
int processColor = CRT_colors[PROCESS]; int processColor = CRT_colors[PROCESS];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
processGigabytesColor = CRT_colors[PROCESS];
}
if (number < 1000) { if (number == ULLONG_MAX) {
//Invalid number
RichString_appendAscii(str, shadowColor, " N/A ");
} else if (number < 1000) {
//Plain number, no markings //Plain number, no markings
len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number); len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
RichString_appendnAscii(str, processColor, buffer, len); RichString_appendnAscii(str, processColor, buffer, len);

View File

@ -253,14 +253,18 @@ static inline bool Process_isChildOf(const Process* this, pid_t pid) {
void Process_setupColumnWidths(void); void Process_setupColumnWidths(void);
/* Takes number in kilo units (base 1024) */
void Process_humanNumber(RichString* str, unsigned long long number, bool coloring); void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
/* Takes number in bare units (base 1000) */
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring); void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
/* Takes number in hundredths of a seconds */
void Process_printTime(RichString* str, unsigned long long totalHundredths); void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_fillStarttimeBuffer(Process* this); void Process_fillStarttimeBuffer(Process* this);
/* Takes number in bare units (base 1024) */
void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring); void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width); void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width);

View File

@ -74,13 +74,13 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
#ifdef HAVE_VSERVER #ifdef HAVE_VSERVER
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, }, [VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
#endif #endif
[RCHAR] = { .name = "RCHAR", .title = " RD_CHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [RCHAR] = { .name = "RCHAR", .title = "RCHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[WCHAR] = { .name = "WCHAR", .title = " WR_CHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [WCHAR] = { .name = "WCHAR", .title = "WCHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[SYSCR] = { .name = "SYSCR", .title = " RD_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [SYSCR] = { .name = "SYSCR", .title = " READ_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[SYSCW] = { .name = "SYSCW", .title = " WR_SYSC ", .description = "Number of write(2) syscalls for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [SYSCW] = { .name = "SYSCW", .title = " WRITE_SYSC ", .description = "Number of write(2) syscalls for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[RBYTES] = { .name = "RBYTES", .title = " IO_RBYTES ", .description = "Bytes of read(2) I/O for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [RBYTES] = { .name = "RBYTES", .title = " IO_R ", .description = "Bytes of read(2) I/O for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[WBYTES] = { .name = "WBYTES", .title = " IO_WBYTES ", .description = "Bytes of write(2) I/O for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [WBYTES] = { .name = "WBYTES", .title = " IO_W ", .description = "Bytes of write(2) I/O for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[CNCLWB] = { .name = "CNCLWB", .title = " IO_CANCEL ", .description = "Bytes of cancelled write(2) I/O", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [CNCLWB] = { .name = "CNCLWB", .title = " IO_C ", .description = "Bytes of cancelled write(2) I/O", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
[IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, },
@ -637,13 +637,13 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
case STIME: Process_printTime(str, lp->stime); return; case STIME: Process_printTime(str, lp->stime); return;
case CUTIME: Process_printTime(str, lp->cutime); return; case CUTIME: Process_printTime(str, lp->cutime); return;
case CSTIME: Process_printTime(str, lp->cstime); return; case CSTIME: Process_printTime(str, lp->cstime); return;
case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return; case RCHAR: Process_humanNumber(str, lp->io_rchar, coloring); return;
case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return; case WCHAR: Process_humanNumber(str, lp->io_wchar, coloring); return;
case SYSCR: Process_colorNumber(str, lp->io_syscr, coloring); return; case SYSCR: Process_colorNumber(str, lp->io_syscr, coloring); return;
case SYSCW: Process_colorNumber(str, lp->io_syscw, coloring); return; case SYSCW: Process_colorNumber(str, lp->io_syscw, coloring); return;
case RBYTES: Process_colorNumber(str, lp->io_read_bytes, coloring); return; case RBYTES: Process_humanNumber(str, lp->io_read_bytes, coloring); return;
case WBYTES: Process_colorNumber(str, lp->io_write_bytes, coloring); return; case WBYTES: Process_humanNumber(str, lp->io_write_bytes, coloring); return;
case CNCLWB: Process_colorNumber(str, lp->io_cancelled_write_bytes, coloring); return; case CNCLWB: Process_humanNumber(str, lp->io_cancelled_write_bytes, coloring); return;
case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return; case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return;
case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return; case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return;
case IO_RATE: { case IO_RATE: {
@ -753,6 +753,13 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
RichString_appendWide(str, attr, buffer); RichString_appendWide(str, attr, buffer);
} }
static double adjustNaN(double num) {
if (isnan(num))
return -0.0005;
return num;
}
static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) { static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, ProcessField key) {
const LinuxProcess* p1 = (const LinuxProcess*)v1; const LinuxProcess* p1 = (const LinuxProcess*)v1;
const LinuxProcess* p2 = (const LinuxProcess*)v2; const LinuxProcess* p2 = (const LinuxProcess*)v2;
@ -797,11 +804,11 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
case CNCLWB: case CNCLWB:
return SPACESHIP_NUMBER(p1->io_cancelled_write_bytes, p2->io_cancelled_write_bytes); return SPACESHIP_NUMBER(p1->io_cancelled_write_bytes, p2->io_cancelled_write_bytes);
case IO_READ_RATE: case IO_READ_RATE:
return SPACESHIP_NUMBER(p1->io_rate_read_bps, p2->io_rate_read_bps); return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps), adjustNaN(p2->io_rate_read_bps));
case IO_WRITE_RATE: case IO_WRITE_RATE:
return SPACESHIP_NUMBER(p1->io_rate_write_bps, p2->io_rate_write_bps); return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_write_bps));
case IO_RATE: case IO_RATE:
return SPACESHIP_NUMBER(p1->io_rate_read_bps + p1->io_rate_write_bps, p2->io_rate_read_bps + p2->io_rate_write_bps); return SPACESHIP_NUMBER(adjustNaN(p1->io_rate_read_bps) + adjustNaN(p1->io_rate_write_bps), adjustNaN(p2->io_rate_read_bps) + adjustNaN(p2->io_rate_write_bps));
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ
case CTID: case CTID:
return SPACESHIP_NULLSTR(p1->ctid, p2->ctid); return SPACESHIP_NULLSTR(p1->ctid, p2->ctid);

View File

@ -81,15 +81,31 @@ typedef struct LinuxProcess_ {
long m_drs; long m_drs;
long m_lrs; long m_lrs;
long m_dt; long m_dt;
/* Data read (in kilobytes) */
unsigned long long io_rchar; unsigned long long io_rchar;
/* Data written (in kilobytes) */
unsigned long long io_wchar; unsigned long long io_wchar;
/* Number of read(2) syscalls */
unsigned long long io_syscr; unsigned long long io_syscr;
/* Number of write(2) syscalls */
unsigned long long io_syscw; unsigned long long io_syscw;
/* Storage data read (in kilobytes) */
unsigned long long io_read_bytes; unsigned long long io_read_bytes;
/* Storage data written (in kilobytes) */
unsigned long long io_write_bytes; unsigned long long io_write_bytes;
/* Storgae data cancelled (in kilobytes) */
unsigned long long io_cancelled_write_bytes; unsigned long long io_cancelled_write_bytes;
unsigned long long io_rate_read_time;
unsigned long long io_rate_write_time; /* Point in time of last io scan (in seconds elapsed since the Epoch) */
unsigned long long io_last_scan_time;
double io_rate_read_bps; double io_rate_read_bps;
double io_rate_write_bps; double io_rate_write_bps;
#ifdef HAVE_OPENVZ #ifdef HAVE_OPENVZ

View File

@ -385,15 +385,14 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
if (r < 0) { if (r < 0) {
process->io_rate_read_bps = NAN; process->io_rate_read_bps = NAN;
process->io_rate_write_bps = NAN; process->io_rate_write_bps = NAN;
process->io_rchar = -1LL; process->io_rchar = ULLONG_MAX;
process->io_wchar = -1LL; process->io_wchar = ULLONG_MAX;
process->io_syscr = -1LL; process->io_syscr = ULLONG_MAX;
process->io_syscw = -1LL; process->io_syscw = ULLONG_MAX;
process->io_read_bytes = -1LL; process->io_read_bytes = ULLONG_MAX;
process->io_write_bytes = -1LL; process->io_write_bytes = ULLONG_MAX;
process->io_cancelled_write_bytes = -1LL; process->io_cancelled_write_bytes = ULLONG_MAX;
process->io_rate_read_time = -1LL; process->io_last_scan_time = now;
process->io_rate_write_time = -1LL;
return; return;
} }
@ -405,22 +404,18 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
switch (line[0]) { switch (line[0]) {
case 'r': case 'r':
if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) { if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
process->io_rchar = strtoull(line + 7, NULL, 10); process->io_rchar = strtoull(line + 7, NULL, 10) / ONE_K;
} else if (String_startsWith(line + 1, "ead_bytes: ")) { } else if (String_startsWith(line + 1, "ead_bytes: ")) {
process->io_read_bytes = strtoull(line + 12, NULL, 10); process->io_read_bytes = strtoull(line + 12, NULL, 10) / ONE_K;
process->io_rate_read_bps = process->io_rate_read_bps = ONE_K * (process->io_read_bytes - last_read) / (now - process->io_last_scan_time);
((double)(process->io_read_bytes - last_read)) / (((double)(now - process->io_rate_read_time)) / 1000);
process->io_rate_read_time = now;
} }
break; break;
case 'w': case 'w':
if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) { if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
process->io_wchar = strtoull(line + 7, NULL, 10); process->io_wchar = strtoull(line + 7, NULL, 10) / ONE_K;
} else if (String_startsWith(line + 1, "rite_bytes: ")) { } else if (String_startsWith(line + 1, "rite_bytes: ")) {
process->io_write_bytes = strtoull(line + 13, NULL, 10); process->io_write_bytes = strtoull(line + 13, NULL, 10) / ONE_K;
process->io_rate_write_bps = process->io_rate_write_bps = ONE_K * (process->io_write_bytes - last_write) / (now - process->io_last_scan_time);
((double)(process->io_write_bytes - last_write)) / (((double)(now - process->io_rate_write_time)) / 1000);
process->io_rate_write_time = now;
} }
break; break;
case 's': case 's':
@ -432,10 +427,12 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t proc
break; break;
case 'c': case 'c':
if (String_startsWith(line + 1, "ancelled_write_bytes: ")) { if (String_startsWith(line + 1, "ancelled_write_bytes: ")) {
process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10); process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10) / ONE_K;
} }
} }
} }
process->io_last_scan_time = now;
} }
typedef struct LibraryData_ { typedef struct LibraryData_ {