DiskIOMeter.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 void DiskIOMeter_updateValues(Meter* this) {
  28. const Machine* host = this->host;
  29. static uint64_t cached_last_update;
  30. uint64_t passedTimeInMs = host->realtimeMs - cached_last_update;
  31. bool hasNewData = false;
  32. DiskIOData data;
  33. /* update only every 500ms to have a sane span for rate calculation */
  34. if (passedTimeInMs > 500) {
  35. hasNewData = Platform_getDiskIO(&data);
  36. if (!hasNewData) {
  37. status = RATESTATUS_NODATA;
  38. } else if (cached_last_update == 0) {
  39. status = RATESTATUS_INIT;
  40. } else if (passedTimeInMs > 30000) {
  41. status = RATESTATUS_STALE;
  42. } else {
  43. status = RATESTATUS_DATA;
  44. }
  45. cached_last_update = host->realtimeMs;
  46. }
  47. if (hasNewData) {
  48. static uint64_t cached_read_total;
  49. static uint64_t cached_write_total;
  50. static uint64_t cached_msTimeSpend_total;
  51. if (status != RATESTATUS_INIT) {
  52. uint64_t diff;
  53. if (data.totalBytesRead > cached_read_total) {
  54. diff = data.totalBytesRead - cached_read_total;
  55. diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */
  56. diff /= ONE_K; /* convert to KiB/s */
  57. } else {
  58. diff = 0;
  59. }
  60. Meter_humanUnit(cached_read_diff_str, diff, sizeof(cached_read_diff_str));
  61. if (data.totalBytesWritten > cached_write_total) {
  62. diff = data.totalBytesWritten - cached_write_total;
  63. diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */
  64. diff /= ONE_K; /* convert to KiB/s */
  65. } else {
  66. diff = 0;
  67. }
  68. Meter_humanUnit(cached_write_diff_str, diff, sizeof(cached_write_diff_str));
  69. if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
  70. diff = data.totalMsTimeSpend - cached_msTimeSpend_total;
  71. cached_utilisation_diff = 100.0 * (double)diff / passedTimeInMs;
  72. cached_utilisation_diff = MINIMUM(cached_utilisation_diff, 100.0);
  73. } else {
  74. cached_utilisation_diff = 0.0;
  75. }
  76. }
  77. cached_read_total = data.totalBytesRead;
  78. cached_write_total = data.totalBytesWritten;
  79. cached_msTimeSpend_total = data.totalMsTimeSpend;
  80. }
  81. this->values[0] = cached_utilisation_diff;
  82. if (status == RATESTATUS_NODATA) {
  83. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "no data");
  84. return;
  85. }
  86. if (status == RATESTATUS_INIT) {
  87. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "init");
  88. return;
  89. }
  90. if (status == RATESTATUS_STALE) {
  91. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "stale");
  92. return;
  93. }
  94. xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", cached_read_diff_str, cached_write_diff_str, cached_utilisation_diff);
  95. }
  96. static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
  97. switch (status) {
  98. case RATESTATUS_NODATA:
  99. RichString_writeAscii(out, CRT_colors[METER_VALUE_ERROR], "no data");
  100. return;
  101. case RATESTATUS_INIT:
  102. RichString_writeAscii(out, CRT_colors[METER_VALUE], "initializing...");
  103. return;
  104. case RATESTATUS_STALE:
  105. RichString_writeAscii(out, CRT_colors[METER_VALUE_WARN], "stale data");
  106. return;
  107. case RATESTATUS_DATA:
  108. break;
  109. }
  110. char buffer[16];
  111. int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
  112. int len = xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
  113. RichString_appendnAscii(out, CRT_colors[color], buffer, len);
  114. RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: ");
  115. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], cached_read_diff_str);
  116. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
  117. RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: ");
  118. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], cached_write_diff_str);
  119. RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
  120. }
  121. const MeterClass DiskIOMeter_class = {
  122. .super = {
  123. .extends = Class(Meter),
  124. .delete = Meter_delete,
  125. .display = DiskIOMeter_display
  126. },
  127. .updateValues = DiskIOMeter_updateValues,
  128. .defaultMode = TEXT_METERMODE,
  129. .supportedModes = METERMODE_DEFAULT_SUPPORTED,
  130. .maxItems = 1,
  131. .total = 100.0,
  132. .attributes = DiskIOMeter_attributes,
  133. .name = "DiskIO",
  134. .uiName = "Disk IO",
  135. .caption = "Disk IO: "
  136. };