diff --git a/CRT.c b/CRT.c index c0faf886..69372298 100644 --- a/CRT.c +++ b/CRT.c @@ -145,6 +145,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta,Black), [ZFS_COMPRESSED] = ColorPair(Blue,Black), [ZFS_RATIO] = ColorPair(Magenta,Black), + [DISKIO_UTIL_HIGH] = A_BOLD | ColorPair(White,Black), + [DISKIO_READ] = ColorPair(Green,Black), + [DISKIO_WRITE] = ColorPair(Blue,Black), }, [COLORSCHEME_MONOCHROME] = { [RESET_COLOR] = A_NORMAL, @@ -215,6 +218,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = A_DIM, [ZFS_COMPRESSED] = A_BOLD, [ZFS_RATIO] = A_BOLD, + [DISKIO_UTIL_HIGH] = A_BOLD, + [DISKIO_READ] = A_NORMAL, + [DISKIO_WRITE] = A_NORMAL, }, [COLORSCHEME_BLACKONWHITE] = { [RESET_COLOR] = ColorPair(Black,White), @@ -285,6 +291,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta,White), [ZFS_COMPRESSED] = ColorPair(Cyan,White), [ZFS_RATIO] = ColorPair(Magenta,White), + [DISKIO_UTIL_HIGH] = A_BOLD | ColorPair(Yellow,White), + [DISKIO_READ] = ColorPair(Green,White), + [DISKIO_WRITE] = ColorPair(Blue,White), }, [COLORSCHEME_LIGHTTERMINAL] = { [RESET_COLOR] = ColorPair(Blue,Black), @@ -355,6 +364,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Black), [ZFS_COMPRESSED] = ColorPair(Cyan,Black), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Black), + [DISKIO_UTIL_HIGH] = A_BOLD | ColorPair(Yellow,Black), + [DISKIO_READ] = ColorPair(Green,Black), + [DISKIO_WRITE] = ColorPair(Blue,Black), }, [COLORSCHEME_MIDNIGHT] = { [RESET_COLOR] = ColorPair(White,Blue), @@ -425,6 +437,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Blue), [ZFS_COMPRESSED] = A_BOLD | ColorPair(White,Blue), [ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Blue), + [DISKIO_UTIL_HIGH] = A_BOLD | ColorPair(White,Blue), + [DISKIO_READ] = ColorPair(Green,Blue), + [DISKIO_WRITE] = ColorPair(Black,Blue), }, [COLORSCHEME_BLACKNIGHT] = { [RESET_COLOR] = ColorPair(Cyan,Black), @@ -495,6 +510,9 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [ZFS_OTHER] = ColorPair(Magenta,Black), [ZFS_COMPRESSED] = ColorPair(Blue,Black), [ZFS_RATIO] = ColorPair(Magenta,Black), + [DISKIO_UTIL_HIGH] = A_BOLD | ColorPair(Green,Black), + [DISKIO_READ] = ColorPair(Green,Black), + [DISKIO_WRITE] = ColorPair(Blue,Black), }, [COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated. }; diff --git a/CRT.h b/CRT.h index f21488fe..4eeb9a46 100644 --- a/CRT.h +++ b/CRT.h @@ -106,6 +106,9 @@ typedef enum ColorElements_ { ZFS_OTHER, ZFS_COMPRESSED, ZFS_RATIO, + DISKIO_UTIL_HIGH, + DISKIO_READ, + DISKIO_WRITE, LAST_COLORELEMENT } ColorElements; diff --git a/DiskIOMeter.c b/DiskIOMeter.c new file mode 100644 index 00000000..8c3f1455 --- /dev/null +++ b/DiskIOMeter.c @@ -0,0 +1,92 @@ +/* +htop - DiskIOMeter.c +(C) 2020 Christian Göttsche +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "DiskIOMeter.h" + +#include + +#include "CRT.h" +#include "Platform.h" + + +static const int DiskIOMeter_attributes[] = { + DISKIO_UTIL_HIGH, + DISKIO_READ, + DISKIO_WRITE, +}; + +static unsigned long int cached_read_diff = 0; +static unsigned long int cached_write_diff = 0; +static double cached_utilisation_diff = 0.0; + +static void DiskIOMeter_updateValues(Meter* this, char* buffer, int len) { + static unsigned long int cached_read_total = 0; + static unsigned long int cached_write_total = 0; + static unsigned long int cached_msTimeSpend_total = 0; + static unsigned long long int cached_last_update = 0; + + struct timeval tv; + gettimeofday(&tv, NULL); + unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000; + + /* update only every 500ms */ + if (timeInMilliSeconds - cached_last_update > 500) { + unsigned long int bytesRead, bytesWrite, msTimeSpend; + + Platform_getDiskIO(&bytesRead, &bytesWrite, &msTimeSpend); + + cached_read_diff = (bytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ + cached_read_total = bytesRead; + + cached_write_diff = (bytesWrite - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */ + cached_write_total = bytesWrite; + + cached_utilisation_diff = 100 * (double)(msTimeSpend - cached_msTimeSpend_total) / (timeInMilliSeconds - cached_last_update); + cached_last_update = timeInMilliSeconds; + cached_msTimeSpend_total = msTimeSpend; + } + + this->values[0] = cached_utilisation_diff; + this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */ + + char bufferRead[12], bufferWrite[12]; + Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead)); + Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite)); + snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); +} + +static void DIskIOMeter_display(ATTR_UNUSED Object* cast, RichString* out) { + char buffer[16]; + + int color = cached_utilisation_diff > 40.0 ? DISKIO_UTIL_HIGH : METER_VALUE; + xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); + RichString_write(out, CRT_colors[color], buffer); + + RichString_append(out, CRT_colors[METER_TEXT], " read: "); + Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); + RichString_append(out, CRT_colors[DISKIO_READ], buffer); + + RichString_append(out, CRT_colors[METER_TEXT], " write: "); + Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); + RichString_append(out, CRT_colors[DISKIO_WRITE], buffer); +} + +MeterClass DiskIOMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = DIskIOMeter_display + }, + .updateValues = DiskIOMeter_updateValues, + .defaultMode = TEXT_METERMODE, + .maxItems = 1, + .total = 100.0, + .attributes = DiskIOMeter_attributes, + .name = "DiskIO", + .uiName = "Disk IO", + .caption = "Disk IO: " +}; diff --git a/DiskIOMeter.h b/DiskIOMeter.h new file mode 100644 index 00000000..6aca5c2d --- /dev/null +++ b/DiskIOMeter.h @@ -0,0 +1,14 @@ +#ifndef HEADER_DiskIOMeter +#define HEADER_DiskIOMeter +/* + h top - DiskIOMeter*.h +(C) 2020 Christian Göttsche +Released under the GNU GPL, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Meter.h" + +extern MeterClass DiskIOMeter_class; + +#endif /* HEADER_DiskIOMeter */ diff --git a/Makefile.am b/Makefile.am index 441ea80e..ea9a2e42 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,8 +15,8 @@ AM_LDFLAGS = AM_CPPFLAGS = -DNDEBUG myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \ -ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \ -DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ +ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c DiskIOMeter.c DiskIOMeter.h \ +MainPanel.c DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \ LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \ BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \ SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \ diff --git a/darwin/Platform.c b/darwin/Platform.c index 747ffefb..5aa50a65 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -305,3 +305,8 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + // TODO + *bytesRead = *bytesWrite = *msTimeSpend = 0; +} diff --git a/darwin/Platform.h b/darwin/Platform.h index 6c038587..3a6f119f 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -48,4 +48,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index 7d16eda1..36ab2c21 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -205,3 +205,8 @@ char* Platform_getProcessEnv(pid_t pid) { (void)pid; // prevent unused warning return NULL; } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + // TODO + *bytesRead = *bytesWrite = *msTimeSpend = 0; +} diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index 14a249e1..c0a60233 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -42,4 +42,6 @@ void Platform_setTasksValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 318b3e12..36256f9a 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -220,3 +220,8 @@ char* Platform_getProcessEnv(pid_t pid) { // TODO return NULL; } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + // TODO + *bytesRead = *bytesWrite = *msTimeSpend = 0; +} diff --git a/freebsd/Platform.h b/freebsd/Platform.h index e58e99d0..780d23e6 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -45,4 +45,6 @@ void Platform_setTasksValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif diff --git a/linux/Platform.c b/linux/Platform.c index 58bc3bea..78313b2d 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -14,6 +14,7 @@ in the source distribution for its full text. #include "Meter.h" #include "CPUMeter.h" +#include "DiskIOMeter.h" #include "MemoryMeter.h" #include "SwapMeter.h" #include "TasksMeter.h" @@ -25,12 +26,14 @@ in the source distribution for its full text. #include "zfs/ZfsArcMeter.h" #include "zfs/ZfsCompressedArcMeter.h" #include "LinuxProcess.h" +#include "StringUtils.h" #include #include #include #include #include +#include ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 }; @@ -131,6 +134,7 @@ MeterClass* Platform_meterTypes[] = { &PressureStallMemoryFullMeter_class, &ZfsArcMeter_class, &ZfsCompressedArcMeter_class, + &DiskIOMeter_class, NULL }; @@ -281,3 +285,50 @@ void Platform_getPressureStall(const char *file, bool some, double* ten, double* assert(total == 3); fclose(fd); } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + FILE *fd = fopen(PROCDIR "/diskstats", "r"); + if (!fd) { + *bytesRead = 0; + *bytesWrite = 0; + *msTimeSpend = 0; + return; + } + unsigned long int read_sum = 0, write_sum = 0, timeSpend_sum = 0; + char lineBuffer[256]; + while (fgets(lineBuffer, sizeof(lineBuffer), fd)) { + char diskname[32]; + unsigned long int read_tmp, write_tmp, timeSpend_tmp; + if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %lu %*u %*u %*u %lu %*u %*u %lu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) { + if (String_startsWith(diskname, "dm-")) + continue; + + /* only count root disks, e.g. do not count IO from sda and sda1 twice */ + if ((diskname[0] == 's' || diskname[0] == 'h') + && diskname[1] == 'd' + && isalpha((unsigned char)diskname[2]) + && isdigit((unsigned char)diskname[3])) + continue; + + /* only count root disks, e.g. do not count IO from mmcblk0 and mmcblk0p1 twice */ + if (diskname[0] == 'm' + && diskname[1] == 'm' + && diskname[2] == 'c' + && diskname[3] == 'b' + && diskname[4] == 'l' + && diskname[5] == 'k' + && isdigit((unsigned char)diskname[6]) + && diskname[7] == 'p') + continue; + + read_sum += read_tmp; + write_sum += write_tmp; + timeSpend_sum += timeSpend_tmp; + } + } + fclose(fd); + /* multiply with sector size */ + *bytesRead = 512 * read_sum; + *bytesWrite = 512 * write_sum; + *msTimeSpend = timeSpend_sum; +} diff --git a/linux/Platform.h b/linux/Platform.h index 5060ea24..68b18f28 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -44,4 +44,6 @@ char* Platform_getProcessEnv(pid_t pid); void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index 2c8bc7ae..ce370994 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -286,3 +286,8 @@ char* Platform_getProcessEnv(pid_t pid) { (void) kvm_close(kt); return env; } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + // TODO + *bytesRead = *bytesWrite = *msTimeSpend = 0; +} diff --git a/openbsd/Platform.h b/openbsd/Platform.h index 9e742bf3..e5e09ce0 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -43,4 +43,6 @@ void Platform_setTasksValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif diff --git a/solaris/Platform.c b/solaris/Platform.c index 05b44462..2114215e 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -258,3 +258,8 @@ char* Platform_getProcessEnv(pid_t pid) { strncpy( envBuilder.env + envBuilder.size, "\0", 1); return envBuilder.env; } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + // TODO + *bytesRead = *bytesWrite = *msTimeSpend = 0; +} diff --git a/solaris/Platform.h b/solaris/Platform.h index 29cc7964..d3dddb72 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -63,4 +63,6 @@ void Platform_setZfsCompressedArcValues(Meter* this); char* Platform_getProcessEnv(pid_t pid); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif diff --git a/unsupported/Platform.c b/unsupported/Platform.c index b095e126..6a6b0131 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -135,3 +135,7 @@ char* Platform_getProcessEnv(pid_t pid) { (void) pid; return NULL; } + +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend) { + *bytesRead = *bytesWrite = *msTimeSpend = 0; +} diff --git a/unsupported/Platform.h b/unsupported/Platform.h index fca7fc47..cdce6f38 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -47,4 +47,6 @@ bool Process_isThread(Process* this); char* Platform_getProcessEnv(pid_t pid); +void Platform_getDiskIO(unsigned long int *bytesRead, unsigned long int *bytesWrite, unsigned long int *msTimeSpend); + #endif