DiskIOMeter.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. /*
  2. htop - DiskIOMeter.c
  3. (C) 2020 htop dev team
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #include "DiskIOMeter.h"
  9. #include <stdbool.h>
  10. #include "CRT.h"
  11. #include "Machine.h"
  12. #include "Macros.h"
  13. #include "Object.h"
  14. #include "Platform.h"
  15. #include "RichString.h"
  16. #include "Row.h"
  17. #include "XUtils.h"
  18. static const int DiskIOMeter_attributes[] = {
  19. METER_VALUE_NOTICE,
  20. METER_VALUE_IOREAD,
  21. METER_VALUE_IOWRITE,
  22. };
  23. static MeterRateStatus status = RATESTATUS_INIT;
  24. static char cached_read_diff_str[6];
  25. static char cached_write_diff_str[6];
  26. static double cached_utilisation_diff;
  27. static double cached_utilisation_norm;
  28. static void DiskIOMeter_updateValues(Meter* this) {
  29. const Machine* host = this->host;
  30. static uint64_t cached_last_update;
  31. uint64_t passedTimeInMs = host->realtimeMs - cached_last_update;
  32. bool hasNewData = false;
  33. DiskIOData data;
  34. /* update only every 500ms to have a sane span for rate calculation */
  35. if (passedTimeInMs > 500) {
  36. hasNewData = Platform_getDiskIO(&data);
  37. if (!hasNewData) {
  38. status = RATESTATUS_NODATA;
  39. } else if (cached_last_update == 0) {
  40. status = RATESTATUS_INIT;
  41. } else if (passedTimeInMs > 30000) {
  42. status = RATESTATUS_STALE;
  43. } else {
  44. status = RATESTATUS_DATA;
  45. }
  46. cached_last_update = host->realtimeMs;
  47. }
  48. if (hasNewData) {
  49. static uint64_t cached_read_total;
  50. static uint64_t cached_write_total;
  51. static uint64_t cached_msTimeSpend_total;
  52. if (status != RATESTATUS_INIT) {
  53. uint64_t diff;
  54. if (data.totalBytesRead > cached_read_total) {
  55. diff = data.totalBytesRead - cached_read_total;
  56. diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */
  57. diff /= ONE_K; /* convert to KiB/s */
  58. } else {
  59. diff = 0;
  60. }
  61. Meter_humanUnit(cached_read_diff_str, diff, sizeof(cached_read_diff_str));
  62. if (data.totalBytesWritten > cached_write_total) {
  63. diff = data.totalBytesWritten - cached_write_total;
  64. diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */
  65. diff /= ONE_K; /* convert to KiB/s */
  66. } else {
  67. diff = 0;
  68. }
  69. Meter_humanUnit(cached_write_diff_str, diff, sizeof(cached_write_diff_str));
  70. cached_utilisation_diff = 0.0;
  71. cached_utilisation_norm = 0.0;
  72. if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
  73. diff = data.totalMsTimeSpend - cached_msTimeSpend_total;
  74. cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs;
  75. if (data.numDisks > 0) {
  76. cached_utilisation_norm = (double)diff / (passedTimeInMs * data.numDisks);
  77. cached_utilisation_norm = MINIMUM(cached_utilisation_norm, 1.0);
  78. }
  79. }
  80. }
  81. cached_read_total = data.totalBytesRead;
  82. cached_write_total = data.totalBytesWritten;
  83. cached_msTimeSpend_total = data.totalMsTimeSpend;
  84. }
  85. this->values[0] = cached_utilisation_norm;
  86. if (status == RATESTATUS_NODATA) {
  87. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
  88. return;
  89. }
  90. if (status == RATESTATUS_INIT) {
  91. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init");
  92. return;
  93. }
  94. if (status == RATESTATUS_STALE) {
  95. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale");
  96. return;
  97. }
  98. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", cached_read_diff_str, cached_write_diff_str, cached_utilisation_diff);
  99. }
  100. static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
  101. switch (status) {
  102. case RATESTATUS_NODATA:
  103. RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
  104. return;
  105. case RATESTATUS_INIT:
  106. RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
  107. return;
  108. case RATESTATUS_STALE:
  109. RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
  110. return;
  111. case RATESTATUS_DATA:
  112. break;
  113. }
  114. char buffer[16];
  115. int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
  116. int len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
  117. RichString_appendnAscii(out, CRT_colors[color], buffer, len);
  118. RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
  119. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_read_diff_str);
  120. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
  121. RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
  122. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str);
  123. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
  124. }
  125. const MeterClass DiskIOMeter_class = {
  126. .super = {
  127. .extends = Class(Meter),
  128. .delete = Meter_delete,
  129. .display = DiskIOMeter_display
  130. },
  131. .updateValues = DiskIOMeter_updateValues,
  132. .defaultMode = TEXT_METERMODE,
  133. .supportedModes = METERMODE_DEFAULT_SUPPORTED,
  134. .maxItems = 1,
  135. .total = 1.0,
  136. .attributes = DiskIOMeter_attributes,
  137. .name = "DiskIO",
  138. .uiName = "Disk IO",
  139. .caption = "Disk IO: "
  140. };