GPUMeter.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. htop - GPUMeter.c
  3. (C) 2023 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 "linux/GPUMeter.h"
  9. #include "CRT.h"
  10. #include "RichString.h"
  11. #include "linux/LinuxMachine.h"
  12. static size_t activeMeters;
  13. bool GPUMeter_active(void) {
  14. return activeMeters > 0;
  15. }
  16. struct EngineData {
  17. const char* key; /* owned by LinuxMachine */
  18. unsigned long long int timeDiff;
  19. };
  20. static struct EngineData GPUMeter_engineData[4];
  21. static unsigned long long int prevResidueTime, curResidueTime;
  22. static double totalUsage;
  23. static unsigned long long int totalGPUTimeDiff;
  24. static const int GPUMeter_attributes[] = {
  25. GPU_ENGINE_1,
  26. GPU_ENGINE_2,
  27. GPU_ENGINE_3,
  28. GPU_ENGINE_4,
  29. GPU_RESIDUE,
  30. };
  31. static int humanTimeUnit(char* buffer, size_t size, unsigned long long int value) {
  32. if (value < 1000)
  33. return xSnprintf(buffer, size, "%3lluns", value);
  34. if (value < 10000)
  35. return xSnprintf(buffer, size, "%1llu.%1lluus", value / 1000, (value % 1000) / 100);
  36. value /= 1000;
  37. if (value < 1000)
  38. return xSnprintf(buffer, size, "%3lluus", value);
  39. if (value < 10000)
  40. return xSnprintf(buffer, size, "%1llu.%1llums", value / 1000, (value % 1000) / 100);
  41. value /= 1000;
  42. if (value < 1000)
  43. return xSnprintf(buffer, size, "%3llums", value);
  44. if (value < 10000)
  45. return xSnprintf(buffer, size, "%1llu.%1llus", value / 1000, (value % 1000) / 100);
  46. value /= 1000;
  47. if (value < 600)
  48. return xSnprintf(buffer, size, "%3llus", value);
  49. value /= 60;
  50. if (value < 600)
  51. return xSnprintf(buffer, size, "%3llum", value);
  52. value /= 60;
  53. if (value < 96)
  54. return xSnprintf(buffer, size, "%3lluh", value);
  55. value /= 24;
  56. return xSnprintf(buffer, size, "%3llud", value);
  57. }
  58. static void GPUMeter_updateValues(Meter* this) {
  59. const Machine* host = this->host;
  60. const LinuxMachine* lhost = (const LinuxMachine*) host;
  61. const GPUEngineData* gpuEngineData;
  62. char* buffer = this->txtBuffer;
  63. size_t size = sizeof(this->txtBuffer);
  64. int written;
  65. unsigned int i;
  66. uint64_t monotonictimeDelta;
  67. assert(ARRAYSIZE(GPUMeter_engineData) + 1 == ARRAYSIZE(GPUMeter_attributes));
  68. totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime);
  69. monotonictimeDelta = host->monotonicMs - host->prevMonotonicMs;
  70. prevResidueTime = curResidueTime;
  71. curResidueTime = lhost->curGpuTime;
  72. for (gpuEngineData = lhost->gpuEngineData, i = 0; gpuEngineData && i < ARRAYSIZE(GPUMeter_engineData); gpuEngineData = gpuEngineData->next, i++) {
  73. GPUMeter_engineData[i].key = gpuEngineData->key;
  74. GPUMeter_engineData[i].timeDiff = saturatingSub(gpuEngineData->curTime, gpuEngineData->prevTime);
  75. curResidueTime = saturatingSub(curResidueTime, gpuEngineData->curTime);
  76. this->values[i] = 100.0 * GPUMeter_engineData[i].timeDiff / (1000 * 1000) / monotonictimeDelta;
  77. }
  78. this->values[ARRAYSIZE(GPUMeter_engineData)] = 100.0 * saturatingSub(curResidueTime, prevResidueTime) / (1000 * 1000) / monotonictimeDelta;
  79. totalUsage = 100.0 * totalGPUTimeDiff / (1000 * 1000) / monotonictimeDelta;
  80. written = snprintf(buffer, size, "%.1f", totalUsage);
  81. METER_BUFFER_CHECK(buffer, size, written);
  82. METER_BUFFER_APPEND_CHR(buffer, size, '%');
  83. }
  84. static void GPUMeter_display(const Object* cast, RichString* out) {
  85. char buffer[50];
  86. int written;
  87. const Meter* this = (const Meter*)cast;
  88. unsigned int i;
  89. RichString_writeAscii(out, CRT_colors[METER_TEXT], ":");
  90. written = xSnprintf(buffer, sizeof(buffer), "%4.1f", totalUsage);
  91. RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written);
  92. RichString_appendnAscii(out, CRT_colors[METER_TEXT], "%(", 2);
  93. written = humanTimeUnit(buffer, sizeof(buffer), totalGPUTimeDiff);
  94. RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written);
  95. RichString_appendnAscii(out, CRT_colors[METER_TEXT], ")", 1);
  96. for (i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) {
  97. if (!GPUMeter_engineData[i].key)
  98. break;
  99. RichString_appendnAscii(out, CRT_colors[METER_TEXT], " ", 1);
  100. RichString_appendAscii(out, CRT_colors[METER_TEXT], GPUMeter_engineData[i].key);
  101. RichString_appendnAscii(out, CRT_colors[METER_TEXT], ":", 1);
  102. if (isNonnegative(this->values[i]))
  103. written = xSnprintf(buffer, sizeof(buffer), "%4.1f", this->values[i]);
  104. else
  105. written = xSnprintf(buffer, sizeof(buffer), " N/A");
  106. RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written);
  107. RichString_appendnAscii(out, CRT_colors[METER_TEXT], "%(", 2);
  108. written = humanTimeUnit(buffer, sizeof(buffer), GPUMeter_engineData[i].timeDiff);
  109. RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written);
  110. RichString_appendnAscii(out, CRT_colors[METER_TEXT], ")", 1);
  111. }
  112. }
  113. static void GPUMeter_init(Meter* this ATTR_UNUSED) {
  114. activeMeters++;
  115. }
  116. static void GPUMeter_done(Meter* this ATTR_UNUSED) {
  117. assert(activeMeters > 0);
  118. activeMeters--;
  119. }
  120. const MeterClass GPUMeter_class = {
  121. .super = {
  122. .extends = Class(Meter),
  123. .delete = Meter_delete,
  124. .display = GPUMeter_display,
  125. },
  126. .init = GPUMeter_init,
  127. .done = GPUMeter_done,
  128. .updateValues = GPUMeter_updateValues,
  129. .defaultMode = BAR_METERMODE,
  130. .supportedModes = METERMODE_DEFAULT_SUPPORTED,
  131. .maxItems = ARRAYSIZE(GPUMeter_engineData) + 1,
  132. .total = 100.0,
  133. .attributes = GPUMeter_attributes,
  134. .name = "GPU",
  135. .uiName = "GPU usage",
  136. .caption = "GPU"
  137. };