diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 4227dfa92..9740db473 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -27,8 +27,8 @@ static const int DiskIOMeter_attributes[] = { }; static MeterRateStatus status = RATESTATUS_INIT; -static uint32_t cached_read_diff; -static uint32_t cached_write_diff; +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) { @@ -36,16 +36,13 @@ static void DiskIOMeter_updateValues(Meter* this) { 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) { - static uint64_t cached_read_total; - static uint64_t cached_write_total; - static uint64_t cached_msTimeSpend_total; - uint64_t diff; - - DiskIOData data; - if (!Platform_getDiskIO(&data)) { + hasNewData = Platform_getDiskIO(&data); + if (!hasNewData) { status = RATESTATUS_NODATA; } else if (cached_last_update == 0) { status = RATESTATUS_INIT; @@ -56,41 +53,54 @@ static void DiskIOMeter_updateValues(Meter* this) { } cached_last_update = host->realtimeMs; + } - if (status == RATESTATUS_NODATA) { - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); - return; - } + if (hasNewData) { + static uint64_t cached_read_total; + static uint64_t cached_write_total; + static uint64_t cached_msTimeSpend_total; - 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 */ - cached_read_diff = (uint32_t)diff; - } else { - cached_read_diff = 0; + 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; - 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 */ - cached_write_diff = (uint32_t)diff; - } else { - cached_write_diff = 0; - } + cached_read_total = data.totalBytesRead; cached_write_total = data.totalBytesWritten; - - if (data.totalMsTimeSpend > cached_msTimeSpend_total) { - diff = data.totalMsTimeSpend - cached_msTimeSpend_total; - cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs; - } else { - cached_utilisation_diff = 0.0; - } 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; @@ -100,13 +110,7 @@ static void DiskIOMeter_updateValues(Meter* this) { return; } - 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(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); + 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) { @@ -132,13 +136,11 @@ static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) RichString_appendnAscii(out, CRT_colors[color], buffer, len); RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); - Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer); + 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: "); - Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); } diff --git a/Meter.c b/Meter.c index 12c8c9418..268de7886 100644 --- a/Meter.c +++ b/Meter.c @@ -51,32 +51,40 @@ Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type return this; } -int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) { - const char* prefix = "KMGTPEZY"; - unsigned long int powi = 1; - unsigned int powj = 1, precision = 2; - - for (;;) { - if (value / 1024 < powi) - break; - - if (prefix[1] == '\0') +/* Converts 'value' in kibibytes into a human readable string. + Example output strings: "0K", "1023K", "98.7M" and "1.23G" */ +int Meter_humanUnit(char* buffer, double value, size_t size) { + size_t i = 0; + + assert(value >= 0.0); + while (value >= ONE_K) { + if (i >= ARRAYSIZE(unitPrefixes) - 1) { + if (value > 9999.0) { + return xSnprintf(buffer, size, "inf"); + } break; + } - powi *= 1024; - ++prefix; + value /= ONE_K; + ++i; } - if (*prefix == 'K') - precision = 0; + int precision = 0; - for (; precision > 0; precision--) { - powj *= 10; - if (value / powi < powj) - break; + if (i > 0) { + // Fraction digits for mebibytes and above + precision = value <= 99.9 ? (value <= 9.99 ? 2 : 1) : 0; + + // Round up if 'value' is in range (99.9, 100) or (9.99, 10) + if (precision < 2) { + double limit = precision == 1 ? 10.0 : 100.0; + if (value < limit) { + value = limit; + } + } } - return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix); + return xSnprintf(buffer, size, "%.*f%c", precision, value, unitPrefixes[i]); } void Meter_delete(Object* cast) { diff --git a/Meter.h b/Meter.h index e0ca1f076..6b14634a9 100644 --- a/Meter.h +++ b/Meter.h @@ -146,7 +146,9 @@ extern const MeterClass Meter_class; Meter* Meter_new(const Machine* host, unsigned int param, const MeterClass* type); -int Meter_humanUnit(char* buffer, unsigned long int value, size_t size); +/* Converts 'value' in kibibytes into a human readable string. + Example output strings: "0K", "1023K", "98.7M" and "1.23G" */ +int Meter_humanUnit(char* buffer, double value, size_t size); void Meter_delete(Object* cast); diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index 6408244b3..ea7ffba20 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -27,27 +27,25 @@ static const int NetworkIOMeter_attributes[] = { }; static MeterRateStatus status = RATESTATUS_INIT; -static uint32_t cached_rxb_diff; +static double cached_rxb_diff; +static char cached_rxb_diff_str[6]; static uint32_t cached_rxp_diff; -static uint32_t cached_txb_diff; +static double cached_txb_diff; +static char cached_txb_diff_str[6]; static uint32_t cached_txp_diff; static void NetworkIOMeter_updateValues(Meter* this) { const Machine* host = this->host; - static uint64_t cached_last_update = 0; + static uint64_t cached_last_update = 0; uint64_t passedTimeInMs = host->realtimeMs - cached_last_update; + bool hasNewData = false; + NetworkIOData data; /* update only every 500ms to have a sane span for rate calculation */ if (passedTimeInMs > 500) { - static uint64_t cached_rxb_total; - static uint64_t cached_rxp_total; - static uint64_t cached_txb_total; - static uint64_t cached_txp_total; - uint64_t diff; - - NetworkIOData data; - if (!Platform_getNetworkIO(&data)) { + hasNewData = Platform_getNetworkIO(&data); + if (!hasNewData) { status = RATESTATUS_NODATA; } else if (cached_last_update == 0) { status = RATESTATUS_INIT; @@ -58,51 +56,68 @@ static void NetworkIOMeter_updateValues(Meter* this) { } cached_last_update = host->realtimeMs; + } - if (status == RATESTATUS_NODATA) { - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data"); - return; - } + if (hasNewData) { + static uint64_t cached_rxb_total; + static uint64_t cached_rxp_total; + static uint64_t cached_txb_total; + static uint64_t cached_txp_total; - if (data.bytesReceived > cached_rxb_total) { - diff = data.bytesReceived - cached_rxb_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - diff /= ONE_K; /* convert to KiB/s */ - cached_rxb_diff = (uint32_t)diff; - } else { - cached_rxb_diff = 0; + if (status != RATESTATUS_INIT) { + uint64_t diff; + + if (data.bytesReceived > cached_rxb_total) { + diff = data.bytesReceived - cached_rxb_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + cached_rxb_diff = diff; + } else { + cached_rxb_diff = 0; + } + Meter_humanUnit(cached_rxb_diff_str, cached_rxb_diff / ONE_K, sizeof(cached_rxb_diff_str)); + + if (data.packetsReceived > cached_rxp_total) { + diff = data.packetsReceived - cached_rxp_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to pkts/s */ + cached_rxp_diff = (uint32_t)diff; + } else { + cached_rxp_diff = 0; + } + + if (data.bytesTransmitted > cached_txb_total) { + diff = data.bytesTransmitted - cached_txb_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + cached_txb_diff = diff; + } else { + cached_txb_diff = 0; + } + Meter_humanUnit(cached_txb_diff_str, cached_txb_diff / ONE_K, sizeof(cached_txb_diff_str)); + + if (data.packetsTransmitted > cached_txp_total) { + diff = data.packetsTransmitted - cached_txp_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to pkts/s */ + cached_txp_diff = (uint32_t)diff; + } else { + cached_txp_diff = 0; + } } - cached_rxb_total = data.bytesReceived; - if (data.packetsReceived > cached_rxp_total) { - diff = data.packetsReceived - cached_rxp_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - cached_rxp_diff = (uint32_t)diff; - } else { - cached_rxp_diff = 0; - } + cached_rxb_total = data.bytesReceived; cached_rxp_total = data.packetsReceived; - - if (data.bytesTransmitted > cached_txb_total) { - diff = data.bytesTransmitted - cached_txb_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - diff /= ONE_K; /* convert to KiB/s */ - cached_txb_diff = (uint32_t)diff; - } else { - cached_txb_diff = 0; - } cached_txb_total = data.bytesTransmitted; - - if (data.packetsTransmitted > cached_txp_total) { - diff = data.packetsTransmitted - cached_txp_total; - diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ - cached_txp_diff = (uint32_t)diff; - } else { - cached_txp_diff = 0; - } cached_txp_total = data.packetsTransmitted; } + this->values[0] = cached_rxb_diff; + this->values[1] = cached_txb_diff; + if (cached_rxb_diff + cached_txb_diff > this->total) { + this->total = cached_rxb_diff + cached_txb_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; @@ -112,17 +127,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { return; } - this->values[0] = cached_rxb_diff; - this->values[1] = cached_txb_diff; - if (cached_rxb_diff + cached_txb_diff > this->total) { - this->total = cached_rxb_diff + cached_txb_diff; - } - - char bufferBytesReceived[12], bufferBytesTransmitted[12]; - Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived)); - Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted)); - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s %d/%dpkts/s", - bufferBytesReceived, bufferBytesTransmitted, cached_rxp_diff, cached_txp_diff); + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s %u/%upkts/s", + cached_rxb_diff_str, cached_txb_diff_str, cached_rxp_diff, cached_txp_diff); } static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { @@ -144,13 +150,11 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o int len; RichString_writeAscii(out, CRT_colors[METER_TEXT], "rx: "); - Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_rxb_diff_str); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " tx: "); - Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer)); - RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_txb_diff_str); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); len = xSnprintf(buffer, sizeof(buffer), " (%u/%u pkts/s) ", cached_rxp_diff, cached_txp_diff); diff --git a/XUtils.h b/XUtils.h index cfb375cc9..afe22112e 100644 --- a/XUtils.h +++ b/XUtils.h @@ -111,4 +111,7 @@ int compareRealNumbers(double a, double b); nonnegative. */ double sumPositiveValues(const double* array, size_t count); +/* IEC unit prefixes */ +static const char unitPrefixes[] = { 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q' }; + #endif diff --git a/pcp/PCPDynamicMeter.c b/pcp/PCPDynamicMeter.c index 241153a03..1e118351c 100644 --- a/pcp/PCPDynamicMeter.c +++ b/pcp/PCPDynamicMeter.c @@ -351,27 +351,27 @@ void PCPDynamicMeter_updateValues(PCPDynamicMeter* this, Meter* meter) { break; case PM_TYPE_32: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.l, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.l, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%d", atom.l); break; case PM_TYPE_U32: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.ul, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.ul, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%u", atom.ul); break; case PM_TYPE_64: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.ll, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.ll, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%lld", (long long) atom.ll); break; case PM_TYPE_U64: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.ull, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.ull, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%llu", (unsigned long long) atom.ull); break; case PM_TYPE_FLOAT: bytes += conv.dimSpace ? - Meter_humanUnit(buffer + bytes, atom.f, size - bytes) : + Meter_humanUnit(buffer + bytes, (double) atom.f, size - bytes) : xSnprintf(buffer + bytes, size - bytes, "%.2f", (double) atom.f); break; case PM_TYPE_DOUBLE: @@ -427,27 +427,27 @@ void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* met break; case PM_TYPE_32: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.l, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.l, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%d", atom.l); break; case PM_TYPE_U32: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.ul, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.ul, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%u", atom.ul); break; case PM_TYPE_64: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.ll, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.ll, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%lld", (long long) atom.ll); break; case PM_TYPE_U64: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.ull, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.ull, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%llu", (unsigned long long) atom.ull); break; case PM_TYPE_FLOAT: len = conv.dimSpace ? - Meter_humanUnit(buffer, atom.f, sizeof(buffer)) : + Meter_humanUnit(buffer, (double) atom.f, sizeof(buffer)) : xSnprintf(buffer, sizeof(buffer), "%.2f", (double) atom.f); break; case PM_TYPE_DOUBLE: