diff options
Diffstat (limited to 'DiskIOMeter.c')
-rw-r--r-- | DiskIOMeter.c | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/DiskIOMeter.c b/DiskIOMeter.c new file mode 100644 index 0000000..8d658de --- /dev/null +++ b/DiskIOMeter.c @@ -0,0 +1,161 @@ +/* +htop - DiskIOMeter.c +(C) 2020 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "config.h" // IWYU pragma: keep + +#include "DiskIOMeter.h" + +#include <stdbool.h> + +#include "CRT.h" +#include "Machine.h" +#include "Macros.h" +#include "Object.h" +#include "Platform.h" +#include "RichString.h" +#include "Row.h" +#include "XUtils.h" + + +static const int DiskIOMeter_attributes[] = { + METER_VALUE_NOTICE, + METER_VALUE_IOREAD, + METER_VALUE_IOWRITE, +}; + +static MeterRateStatus status = RATESTATUS_INIT; +static char cached_read_diff_str[6]; +static char cached_write_diff_str[6]; +static double cached_utilisation_diff; + +static void DiskIOMeter_updateValues(Meter* this) { + const Machine* host = this->host; + + static uint64_t cached_last_update; + uint64_t passedTimeInMs = host->realtimeMs - cached_last_update; + bool hasNewData = false; + DiskIOData data; + + /* update only every 500ms to have a sane span for rate calculation */ + if (passedTimeInMs > 500) { + hasNewData = Platform_getDiskIO(&data); + if (!hasNewData) { + status = RATESTATUS_NODATA; + } else if (cached_last_update == 0) { + status = RATESTATUS_INIT; + } else if (passedTimeInMs > 30000) { + status = RATESTATUS_STALE; + } else { + status = RATESTATUS_DATA; + } + + cached_last_update = host->realtimeMs; + } + + if (hasNewData) { + static uint64_t cached_read_total; + static uint64_t cached_write_total; + static uint64_t cached_msTimeSpend_total; + + if (status != RATESTATUS_INIT) { + uint64_t diff; + + if (data.totalBytesRead > cached_read_total) { + diff = data.totalBytesRead - cached_read_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ + } else { + diff = 0; + } + Meter_humanUnit(cached_read_diff_str, diff, sizeof(cached_read_diff_str)); + + if (data.totalBytesWritten > cached_write_total) { + diff = data.totalBytesWritten - cached_write_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ + } else { + diff = 0; + } + Meter_humanUnit(cached_write_diff_str, diff, sizeof(cached_write_diff_str)); + + if (data.totalMsTimeSpend > cached_msTimeSpend_total) { + diff = data.totalMsTimeSpend - cached_msTimeSpend_total; + cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs; + cached_utilisation_diff = MINIMUM(cached_utilisation_diff, 100.0); + } else { + cached_utilisation_diff = 0.0; + } + } + + cached_read_total = data.totalBytesRead; + cached_write_total = data.totalBytesWritten; + cached_msTimeSpend_total = data.totalMsTimeSpend; + } + + this->values[0] = cached_utilisation_diff; + + if (status == RATESTATUS_NODATA) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); + return; + } + if (status == RATESTATUS_INIT) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init"); + return; + } + if (status == RATESTATUS_STALE) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale"); + return; + } + + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", cached_read_diff_str, cached_write_diff_str, cached_utilisation_diff); +} + +static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { + switch (status) { + case RATESTATUS_NODATA: + RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data"); + return; + case RATESTATUS_INIT: + RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing..."); + return; + case RATESTATUS_STALE: + RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data"); + return; + case RATESTATUS_DATA: + break; + } + + char buffer[16]; + + int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE; + int len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff); + RichString_appendnAscii(out, CRT_colors[color], buffer, len); + + RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_read_diff_str); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); + + RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: "); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); +} + +const 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: " +}; |