From 0c925e4e95ff901b00d04115178cc0501a052a37 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 02:13:41 +0800 Subject: [PATCH 1/8] Adjust linux/GPUMeter.c file structure; no code changes --- linux/GPUMeter.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c index 628d4c71c..56125bf93 100644 --- a/linux/GPUMeter.c +++ b/linux/GPUMeter.c @@ -11,15 +11,10 @@ in the source distribution for its full text. #include "CRT.h" #include "RichString.h" +#include "XUtils.h" #include "linux/LinuxMachine.h" -static size_t activeMeters; - -bool GPUMeter_active(void) { - return activeMeters > 0; -} - struct EngineData { const char* key; /* owned by LinuxMachine */ unsigned long long int timeDiff; @@ -38,6 +33,12 @@ static const int GPUMeter_attributes[] = { GPU_RESIDUE, }; +static size_t activeMeters; + +bool GPUMeter_active(void) { + return activeMeters > 0; +} + static int humanTimeUnit(char* buffer, size_t size, unsigned long long int value) { if (value < 1000) From 43efe1407d20ecf9d1f59901305583998978f2ee Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 02:19:35 +0800 Subject: [PATCH 2/8] GPUMeter_updateValues() code shrink Signed-off-by: Kang-Che Sung --- linux/GPUMeter.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c index 56125bf93..12250838f 100644 --- a/linux/GPUMeter.c +++ b/linux/GPUMeter.c @@ -86,21 +86,17 @@ static int humanTimeUnit(char* buffer, size_t size, unsigned long long int value static void GPUMeter_updateValues(Meter* this) { const Machine* host = this->host; const LinuxMachine* lhost = (const LinuxMachine*) host; - const GPUEngineData* gpuEngineData; - char* buffer = this->txtBuffer; - size_t size = sizeof(this->txtBuffer); - int written; - unsigned int i; - uint64_t monotonictimeDelta; assert(ARRAYSIZE(GPUMeter_engineData) + 1 == ARRAYSIZE(GPUMeter_attributes)); totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime); - monotonictimeDelta = host->monotonicMs - host->prevMonotonicMs; + uint64_t monotonictimeDelta = host->monotonicMs - host->prevMonotonicMs; prevResidueTime = curResidueTime; curResidueTime = lhost->curGpuTime; + const GPUEngineData* gpuEngineData; + size_t i; for (gpuEngineData = lhost->gpuEngineData, i = 0; gpuEngineData && i < ARRAYSIZE(GPUMeter_engineData); gpuEngineData = gpuEngineData->next, i++) { GPUMeter_engineData[i].key = gpuEngineData->key; GPUMeter_engineData[i].timeDiff = saturatingSub(gpuEngineData->curTime, gpuEngineData->prevTime); @@ -113,10 +109,8 @@ static void GPUMeter_updateValues(Meter* this) { this->values[ARRAYSIZE(GPUMeter_engineData)] = 100.0 * saturatingSub(curResidueTime, prevResidueTime) / (1000 * 1000) / monotonictimeDelta; totalUsage = 100.0 * totalGPUTimeDiff / (1000 * 1000) / monotonictimeDelta; - written = snprintf(buffer, size, "%.1f", totalUsage); - METER_BUFFER_CHECK(buffer, size, written); - METER_BUFFER_APPEND_CHR(buffer, size, '%'); + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%", totalUsage); } static void GPUMeter_display(const Object* cast, RichString* out) { From a341f4e942c7f0644f2b5f49082d4084311ed718 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 02:29:19 +0800 Subject: [PATCH 3/8] Make GPU residue values consistent across meter instances GPUMeter_updateValues() used a locally cached "residue time" variable for computing the percentage of the "GPU residue" usage. The "residue" percentages of subsequent GPUMeter instances could be 0 due to the cached "residue time" off-sync with the time data from the LinuxMachine instance. Rewrite the logic of the function so that all computed GPU percentage values are cached, and the cache updated only when there's increment on host->monotonicMs. Signed-off-by: Kang-Che Sung --- linux/GPUMeter.c | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c index 12250838f..772abbaeb 100644 --- a/linux/GPUMeter.c +++ b/linux/GPUMeter.c @@ -18,10 +18,13 @@ in the source distribution for its full text. struct EngineData { const char* key; /* owned by LinuxMachine */ unsigned long long int timeDiff; + double percentage; }; static struct EngineData GPUMeter_engineData[4]; -static unsigned long long int prevResidueTime, curResidueTime; +static uint64_t prevMonotonicMs; +static double residuePercentage; +static unsigned long long int prevResidueTime; static double totalUsage; static unsigned long long int totalGPUTimeDiff; @@ -89,26 +92,37 @@ static void GPUMeter_updateValues(Meter* this) { assert(ARRAYSIZE(GPUMeter_engineData) + 1 == ARRAYSIZE(GPUMeter_attributes)); - totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime); - uint64_t monotonictimeDelta = host->monotonicMs - host->prevMonotonicMs; + // The results are cached so that we can update values of multiple meter + // instances. We also need a local cache of the monotonic timestamp, thus we + // don't use host->prevMonotonicMs. + if (host->monotonicMs > prevMonotonicMs) { + uint64_t monotonictimeDelta = host->monotonicMs - prevMonotonicMs; - prevResidueTime = curResidueTime; - curResidueTime = lhost->curGpuTime; + unsigned long long int curResidueTime = lhost->curGpuTime; - const GPUEngineData* gpuEngineData; - size_t i; - for (gpuEngineData = lhost->gpuEngineData, i = 0; gpuEngineData && i < ARRAYSIZE(GPUMeter_engineData); gpuEngineData = gpuEngineData->next, i++) { - GPUMeter_engineData[i].key = gpuEngineData->key; - GPUMeter_engineData[i].timeDiff = saturatingSub(gpuEngineData->curTime, gpuEngineData->prevTime); + const GPUEngineData* gpuEngineData; + size_t i; + for (gpuEngineData = lhost->gpuEngineData, i = 0; gpuEngineData && i < ARRAYSIZE(GPUMeter_engineData); gpuEngineData = gpuEngineData->next, i++) { + GPUMeter_engineData[i].key = gpuEngineData->key; + GPUMeter_engineData[i].timeDiff = saturatingSub(gpuEngineData->curTime, gpuEngineData->prevTime); + GPUMeter_engineData[i].percentage = 100.0 * GPUMeter_engineData[i].timeDiff / (1000 * 1000) / monotonictimeDelta; - curResidueTime = saturatingSub(curResidueTime, gpuEngineData->curTime); + curResidueTime = saturatingSub(curResidueTime, gpuEngineData->curTime); + } - this->values[i] = 100.0 * GPUMeter_engineData[i].timeDiff / (1000 * 1000) / monotonictimeDelta; - } + residuePercentage = 100.0 * saturatingSub(curResidueTime, prevResidueTime) / (1000 * 1000) / monotonictimeDelta; + + totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime); + totalUsage = 100.0 * totalGPUTimeDiff / (1000 * 1000) / monotonictimeDelta; - this->values[ARRAYSIZE(GPUMeter_engineData)] = 100.0 * saturatingSub(curResidueTime, prevResidueTime) / (1000 * 1000) / monotonictimeDelta; + prevResidueTime = curResidueTime; + prevMonotonicMs = host->monotonicMs; + } - totalUsage = 100.0 * totalGPUTimeDiff / (1000 * 1000) / monotonictimeDelta; + for (size_t i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) { + this->values[i] = GPUMeter_engineData[i].percentage; + } + this->values[ARRAYSIZE(GPUMeter_engineData)] = residuePercentage; xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%", totalUsage); } From 80e619c526b1a61ea29f8ddd8b3fcf491bb742eb Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 03:59:09 +0800 Subject: [PATCH 4/8] Move GPUMeter Linux-specific code to Platform.c This makes GPUMeter code portable, that is, allow other platforms to support GPU meter. Co-authored-by: Matteo Nicoli Signed-off-by: Kang-Che Sung --- linux/GPUMeter.c | 52 +++++------------------------------------------- linux/GPUMeter.h | 8 ++++++++ linux/Platform.c | 46 ++++++++++++++++++++++++++++++++++++++++++ linux/Platform.h | 2 ++ 4 files changed, 61 insertions(+), 47 deletions(-) diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c index 772abbaeb..9c9b851d6 100644 --- a/linux/GPUMeter.c +++ b/linux/GPUMeter.c @@ -10,21 +10,12 @@ in the source distribution for its full text. #include "linux/GPUMeter.h" #include "CRT.h" +#include "Platform.h" #include "RichString.h" #include "XUtils.h" -#include "linux/LinuxMachine.h" -struct EngineData { - const char* key; /* owned by LinuxMachine */ - unsigned long long int timeDiff; - double percentage; -}; - -static struct EngineData GPUMeter_engineData[4]; -static uint64_t prevMonotonicMs; -static double residuePercentage; -static unsigned long long int prevResidueTime; +struct GPUMeterEngineData GPUMeter_engineData[4]; static double totalUsage; static unsigned long long int totalGPUTimeDiff; @@ -87,42 +78,9 @@ static int humanTimeUnit(char* buffer, size_t size, unsigned long long int value } static void GPUMeter_updateValues(Meter* this) { - const Machine* host = this->host; - const LinuxMachine* lhost = (const LinuxMachine*) host; - - assert(ARRAYSIZE(GPUMeter_engineData) + 1 == ARRAYSIZE(GPUMeter_attributes)); - - // The results are cached so that we can update values of multiple meter - // instances. We also need a local cache of the monotonic timestamp, thus we - // don't use host->prevMonotonicMs. - if (host->monotonicMs > prevMonotonicMs) { - uint64_t monotonictimeDelta = host->monotonicMs - prevMonotonicMs; - - unsigned long long int curResidueTime = lhost->curGpuTime; - - const GPUEngineData* gpuEngineData; - size_t i; - for (gpuEngineData = lhost->gpuEngineData, i = 0; gpuEngineData && i < ARRAYSIZE(GPUMeter_engineData); gpuEngineData = gpuEngineData->next, i++) { - GPUMeter_engineData[i].key = gpuEngineData->key; - GPUMeter_engineData[i].timeDiff = saturatingSub(gpuEngineData->curTime, gpuEngineData->prevTime); - GPUMeter_engineData[i].percentage = 100.0 * GPUMeter_engineData[i].timeDiff / (1000 * 1000) / monotonictimeDelta; + assert(ARRAYSIZE(GPUMeter_engineData) <= ARRAYSIZE(GPUMeter_attributes) - 1); - curResidueTime = saturatingSub(curResidueTime, gpuEngineData->curTime); - } - - residuePercentage = 100.0 * saturatingSub(curResidueTime, prevResidueTime) / (1000 * 1000) / monotonictimeDelta; - - totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime); - totalUsage = 100.0 * totalGPUTimeDiff / (1000 * 1000) / monotonictimeDelta; - - prevResidueTime = curResidueTime; - prevMonotonicMs = host->monotonicMs; - } - - for (size_t i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) { - this->values[i] = GPUMeter_engineData[i].percentage; - } - this->values[ARRAYSIZE(GPUMeter_engineData)] = residuePercentage; + Platform_setGPUValues(this, &totalUsage, &totalGPUTimeDiff); xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%", totalUsage); } @@ -180,7 +138,7 @@ const MeterClass GPUMeter_class = { .updateValues = GPUMeter_updateValues, .defaultMode = BAR_METERMODE, .supportedModes = METERMODE_DEFAULT_SUPPORTED, - .maxItems = ARRAYSIZE(GPUMeter_engineData) + 1, + .maxItems = ARRAYSIZE(GPUMeter_attributes), .total = 100.0, .attributes = GPUMeter_attributes, .name = "GPU", diff --git a/linux/GPUMeter.h b/linux/GPUMeter.h index a770ec779..151968772 100644 --- a/linux/GPUMeter.h +++ b/linux/GPUMeter.h @@ -12,6 +12,14 @@ in the source distribution for its full text. #include "Meter.h" +struct GPUMeterEngineData { + const char* key; /* owned by LinuxMachine */ + unsigned long long int timeDiff; + double percentage; +}; + +extern struct GPUMeterEngineData GPUMeter_engineData[4]; + extern const MeterClass GPUMeter_class; bool GPUMeter_active(void); diff --git a/linux/Platform.c b/linux/Platform.c index eec5d1bf2..85e928d0b 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -369,6 +369,52 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { return percent; } +void Platform_setGPUValues(Meter* this, double* totalUsage, unsigned long long* totalGPUTimeDiff) { + const Machine* host = this->host; + const LinuxMachine* lhost = (const LinuxMachine*) host; + + // Must match the index as used in GPUMeter_attributes + const size_t residueIndex = 4; + assert(ARRAYSIZE(GPUMeter_engineData) == residueIndex); + + static uint64_t prevMonotonicMs; + static double residuePercentage; + static unsigned long long int prevResidueTime; + + // The results are cached so that we can update values of multiple meter + // instances. We also need a local cache of the monotonic timestamp, thus we + // don't use host->prevMonotonicMs. + if (host->monotonicMs > prevMonotonicMs) { + uint64_t monotonictimeDelta = host->monotonicMs - prevMonotonicMs; + + unsigned long long int curResidueTime = lhost->curGpuTime; + + const GPUEngineData* gpuEngineData; + size_t i; + for (gpuEngineData = lhost->gpuEngineData, i = 0; gpuEngineData && i < ARRAYSIZE(GPUMeter_engineData); gpuEngineData = gpuEngineData->next, i++) { + GPUMeter_engineData[i].key = gpuEngineData->key; + GPUMeter_engineData[i].timeDiff = saturatingSub(gpuEngineData->curTime, gpuEngineData->prevTime); + GPUMeter_engineData[i].percentage = 100.0 * GPUMeter_engineData[i].timeDiff / (1000 * 1000) / monotonictimeDelta; + + curResidueTime = saturatingSub(curResidueTime, gpuEngineData->curTime); + } + + residuePercentage = 100.0 * saturatingSub(curResidueTime, prevResidueTime) / (1000 * 1000) / monotonictimeDelta; + + *totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime); + *totalUsage = 100.0 * (*totalGPUTimeDiff) / (1000 * 1000) / monotonictimeDelta; + + prevResidueTime = curResidueTime; + prevMonotonicMs = host->monotonicMs; + } + + this->curItems = residueIndex + 1; + for (size_t i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) { + this->values[i] = GPUMeter_engineData[i].percentage; + } + this->values[residueIndex] = residuePercentage; +} + void Platform_setMemoryValues(Meter* this) { const Machine* host = this->host; const LinuxMachine* lhost = (const LinuxMachine*) host; diff --git a/linux/Platform.h b/linux/Platform.h index e99d1a226..9867bb13e 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -64,6 +64,8 @@ pid_t Platform_getMaxPid(void); double Platform_setCPUValues(Meter* this, unsigned int cpu); +void Platform_setGPUValues(Meter* this, double* totalUsage, unsigned long long* totalGPUTimeDiff); + void Platform_setMemoryValues(Meter* this); void Platform_setSwapValues(Meter* this); From affd0d437cd1488efd49790fe3c5c4bc6cd036f3 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 03:59:13 +0800 Subject: [PATCH 5/8] Replace unnecessary RichString_appendnAscii() calls with appendAscii() This slightly reduces code size. Signed-off-by: Kang-Che Sung --- linux/GPUMeter.c | 12 ++++++------ pcp/PCPDynamicMeter.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c index 9c9b851d6..dad2342d7 100644 --- a/linux/GPUMeter.c +++ b/linux/GPUMeter.c @@ -94,27 +94,27 @@ static void GPUMeter_display(const Object* cast, RichString* out) { RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); written = xSnprintf(buffer, sizeof(buffer), "%4.1f", totalUsage); RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendnAscii(out, CRT_colors[METER_TEXT], "%(", 2); + RichString_appendAscii(out, CRT_colors[METER_TEXT], "%("); written = humanTimeUnit(buffer, sizeof(buffer), totalGPUTimeDiff); RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendnAscii(out, CRT_colors[METER_TEXT], ")", 1); + RichString_appendAscii(out, CRT_colors[METER_TEXT], ")"); for (i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) { if (!GPUMeter_engineData[i].key) break; - RichString_appendnAscii(out, CRT_colors[METER_TEXT], " ", 1); + RichString_appendAscii(out, CRT_colors[METER_TEXT], " "); RichString_appendAscii(out, CRT_colors[METER_TEXT], GPUMeter_engineData[i].key); - RichString_appendnAscii(out, CRT_colors[METER_TEXT], ":", 1); + RichString_appendAscii(out, CRT_colors[METER_TEXT], ":"); if (isNonnegative(this->values[i])) written = xSnprintf(buffer, sizeof(buffer), "%4.1f", this->values[i]); else written = xSnprintf(buffer, sizeof(buffer), " N/A"); RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendnAscii(out, CRT_colors[METER_TEXT], "%(", 2); + RichString_appendAscii(out, CRT_colors[METER_TEXT], "%("); written = humanTimeUnit(buffer, sizeof(buffer), GPUMeter_engineData[i].timeDiff); RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendnAscii(out, CRT_colors[METER_TEXT], ")", 1); + RichString_appendAscii(out, CRT_colors[METER_TEXT], ")"); } } diff --git a/pcp/PCPDynamicMeter.c b/pcp/PCPDynamicMeter.c index 311f1af52..57c84dddc 100644 --- a/pcp/PCPDynamicMeter.c +++ b/pcp/PCPDynamicMeter.c @@ -415,7 +415,7 @@ void PCPDynamicMeter_display(PCPDynamicMeter* this, ATTR_UNUSED const Meter* met nodata = 0; /* we will use this metric so *some* data will be added */ if (i > 0) - RichString_appendnAscii(out, CRT_colors[metric->color], " ", 1); + RichString_appendAscii(out, CRT_colors[metric->color], " "); if (metric->label) RichString_appendAscii(out, CRT_colors[METER_TEXT], metric->label); From 882c7463e8ff29e0db6407e850d7cc96d32720af Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 04:23:17 +0800 Subject: [PATCH 6/8] GPUMeter: Support placeholder "unavailable" values GPUMeter now allows placeholder values for GPU usage and "time differences". For platforms that do not support GPU, the GPU usage percentage may display as "N/A", and for platforms that cannot retrieve precise GPU time, the "GPU time differences" may be hidden from text mode display. Co-authored-by: Matteo Nicoli Signed-off-by: Kang-Che Sung --- linux/GPUMeter.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c index dad2342d7..f63e4e342 100644 --- a/linux/GPUMeter.c +++ b/linux/GPUMeter.c @@ -9,6 +9,8 @@ in the source distribution for its full text. #include "linux/GPUMeter.h" +#include + #include "CRT.h" #include "Platform.h" #include "RichString.h" @@ -16,8 +18,8 @@ in the source distribution for its full text. struct GPUMeterEngineData GPUMeter_engineData[4]; -static double totalUsage; -static unsigned long long int totalGPUTimeDiff; +static double totalUsage = NAN; +static unsigned long long int totalGPUTimeDiff = -1ULL; static const int GPUMeter_attributes[] = { GPU_ENGINE_1, @@ -82,24 +84,36 @@ static void GPUMeter_updateValues(Meter* this) { Platform_setGPUValues(this, &totalUsage, &totalGPUTimeDiff); + if (!isNonnegative(totalUsage)) { + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A"); + return; + } xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1f%%", totalUsage); } static void GPUMeter_display(const Object* cast, RichString* out) { + const Meter* this = (const Meter*)cast; + char buffer[50]; int written; - const Meter* this = (const Meter*)cast; - unsigned int i; RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); + if (!isNonnegative(totalUsage)) { + RichString_appendAscii(out, CRT_colors[METER_VALUE], " N/A"); + return; + } + written = xSnprintf(buffer, sizeof(buffer), "%4.1f", totalUsage); RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendAscii(out, CRT_colors[METER_TEXT], "%("); - written = humanTimeUnit(buffer, sizeof(buffer), totalGPUTimeDiff); - RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendAscii(out, CRT_colors[METER_TEXT], ")"); + RichString_appendAscii(out, CRT_colors[METER_TEXT], "%"); + if (totalGPUTimeDiff != -1ULL) { + RichString_appendAscii(out, CRT_colors[METER_TEXT], "("); + written = humanTimeUnit(buffer, sizeof(buffer), totalGPUTimeDiff); + RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); + RichString_appendAscii(out, CRT_colors[METER_TEXT], ")"); + } - for (i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) { + for (size_t i = 0; i < ARRAYSIZE(GPUMeter_engineData); i++) { if (!GPUMeter_engineData[i].key) break; @@ -111,10 +125,13 @@ static void GPUMeter_display(const Object* cast, RichString* out) { else written = xSnprintf(buffer, sizeof(buffer), " N/A"); RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendAscii(out, CRT_colors[METER_TEXT], "%("); - written = humanTimeUnit(buffer, sizeof(buffer), GPUMeter_engineData[i].timeDiff); - RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); - RichString_appendAscii(out, CRT_colors[METER_TEXT], ")"); + RichString_appendAscii(out, CRT_colors[METER_TEXT], "%"); + if (GPUMeter_engineData[i].timeDiff != -1ULL) { + RichString_appendAscii(out, CRT_colors[METER_TEXT], "("); + written = humanTimeUnit(buffer, sizeof(buffer), GPUMeter_engineData[i].timeDiff); + RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); + RichString_appendAscii(out, CRT_colors[METER_TEXT], ")"); + } } } From 33fd5be060d1ad6a95b867bc7ffa7951235fe3b5 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Mon, 24 Feb 2025 04:23:24 +0800 Subject: [PATCH 7/8] Move linux/GPUMeter.* to GPUMeter.* The GPUMeter code is now platform independent. Co-authored-by: Matteo Nicoli Signed-off-by: Kang-Che Sung --- linux/GPUMeter.c => GPUMeter.c | 2 +- linux/GPUMeter.h => GPUMeter.h | 0 Makefile.am | 4 ++-- linux/LinuxProcessTable.c | 2 +- linux/Platform.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename linux/GPUMeter.c => GPUMeter.c (99%) rename linux/GPUMeter.h => GPUMeter.h (100%) diff --git a/linux/GPUMeter.c b/GPUMeter.c similarity index 99% rename from linux/GPUMeter.c rename to GPUMeter.c index f63e4e342..9e1685b21 100644 --- a/linux/GPUMeter.c +++ b/GPUMeter.c @@ -7,7 +7,7 @@ in the source distribution for its full text. #include "config.h" // IWYU pragma: keep -#include "linux/GPUMeter.h" +#include "GPUMeter.h" #include diff --git a/linux/GPUMeter.h b/GPUMeter.h similarity index 100% rename from linux/GPUMeter.h rename to GPUMeter.h diff --git a/Makefile.am b/Makefile.am index f25c0f068..cf38fef84 100644 --- a/Makefile.am +++ b/Makefile.am @@ -119,6 +119,7 @@ myhtopheaders = \ EnvScreen.h \ FileDescriptorMeter.h \ FunctionBar.h \ + GPUMeter.h \ Hashtable.h \ Header.h \ HeaderLayout.h \ @@ -174,7 +175,6 @@ linux_platform_headers = \ generic/uname.h \ linux/CGroupUtils.h \ linux/GPU.h \ - linux/GPUMeter.h \ linux/HugePageMeter.h \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ @@ -195,12 +195,12 @@ linux_platform_headers = \ zfs/ZfsCompressedArcMeter.h linux_platform_sources = \ + GPUMeter.c \ generic/gettime.c \ generic/hostname.c \ generic/uname.c \ linux/CGroupUtils.c \ linux/GPU.c \ - linux/GPUMeter.c \ linux/HugePageMeter.c \ linux/IOPriorityPanel.c \ linux/LibSensors.c \ diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index 3d8dc962e..1fb3d8f6f 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -26,6 +26,7 @@ in the source distribution for its full text. #include #include "Compat.h" +#include "GPUMeter.h" #include "Hashtable.h" #include "Machine.h" #include "Macros.h" @@ -40,7 +41,6 @@ in the source distribution for its full text. #include "XUtils.h" #include "linux/CGroupUtils.h" #include "linux/GPU.h" -#include "linux/GPUMeter.h" #include "linux/LinuxMachine.h" #include "linux/LinuxProcess.h" #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep diff --git a/linux/Platform.c b/linux/Platform.c index 85e928d0b..4de4d5940 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -31,6 +31,7 @@ in the source distribution for its full text. #include "DateTimeMeter.h" #include "DiskIOMeter.h" #include "FileDescriptorMeter.h" +#include "GPUMeter.h" #include "HostnameMeter.h" #include "HugePageMeter.h" #include "LoadAverageMeter.h" @@ -51,7 +52,6 @@ in the source distribution for its full text. #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" -#include "linux/GPUMeter.h" #include "linux/IOPriority.h" #include "linux/IOPriorityPanel.h" #include "linux/LinuxMachine.h" From d185c1353c214eb1b0727b62c6f9cb673605771d Mon Sep 17 00:00:00 2001 From: Matteo Nicoli Date: Mon, 24 Feb 2025 06:23:33 +0800 Subject: [PATCH 8/8] Add GPUMeter code for Darwin Co-authored-by: Kang-Che Sung --- Makefile.am | 1 + darwin/DarwinMachine.c | 7 ++++++ darwin/DarwinMachine.h | 3 +++ darwin/Platform.c | 54 ++++++++++++++++++++++++++++++++++++++++++ darwin/Platform.h | 2 ++ 5 files changed, 67 insertions(+) diff --git a/Makefile.am b/Makefile.am index cf38fef84..cf2ccee43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -367,6 +367,7 @@ darwin_platform_headers = \ zfs/ZfsCompressedArcMeter.h darwin_platform_sources = \ + GPUMeter.c \ darwin/Platform.c \ darwin/PlatformHelpers.c \ darwin/DarwinMachine.c \ diff --git a/darwin/DarwinMachine.c b/darwin/DarwinMachine.c index 7aba9c8f5..b02b4e45b 100644 --- a/darwin/DarwinMachine.c +++ b/darwin/DarwinMachine.c @@ -105,12 +105,19 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { openzfs_sysctl_init(&this->zfs); openzfs_sysctl_updateArcStats(&this->zfs); + this->GPUService = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("IOGPU")); + if (!this->GPUService) { + CRT_debug("Cannot initialize IOGPU service"); + } + return super; } void Machine_delete(Machine* super) { DarwinMachine* this = (DarwinMachine*) super; + IOObjectRelease(this->GPUService); + DarwinMachine_freeCPULoadInfo(&this->prev_load); Machine_done(super); diff --git a/darwin/DarwinMachine.h b/darwin/DarwinMachine.h index a4ca1a02e..13a385983 100644 --- a/darwin/DarwinMachine.h +++ b/darwin/DarwinMachine.h @@ -7,6 +7,7 @@ Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ +#include #include #include @@ -26,6 +27,8 @@ typedef struct DarwinMachine_ { processor_cpu_load_info_t prev_load; processor_cpu_load_info_t curr_load; + io_service_t GPUService; + ZfsArcStats zfs; } DarwinMachine; diff --git a/darwin/Platform.c b/darwin/Platform.c index 78caedf30..c7cdb79cc 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -10,6 +10,7 @@ in the source distribution for its full text. #include "darwin/Platform.h" +#include #include #include #include @@ -38,6 +39,7 @@ in the source distribution for its full text. #include "DateMeter.h" #include "DateTimeMeter.h" #include "FileDescriptorMeter.h" +#include "GPUMeter.h" #include "HostnameMeter.h" #include "LoadAverageMeter.h" #include "Macros.h" @@ -144,6 +146,7 @@ const MeterClass* const Platform_meterTypes[] = { &DiskIOMeter_class, &NetworkIOMeter_class, &FileDescriptorMeter_class, + &GPUMeter_class, &BlankMeter_class, NULL }; @@ -284,6 +287,57 @@ double Platform_setCPUValues(Meter* mtr, unsigned int cpu) { return CLAMP(total, 0.0, 100.0); } +void Platform_setGPUValues(Meter* mtr, double* totalUsage, unsigned long long* totalGPUTimeDiff) { + const Machine* host = mtr->host; + const DarwinMachine* dhost = (const DarwinMachine *)host; + + assert(*totalGPUTimeDiff == -1ULL); + (void)totalGPUTimeDiff; + + mtr->curItems = 1; + mtr->values[0] = NAN; + + if (!dhost->GPUService) + return; + + static uint64_t prevMonotonicMs; + + // Ensure there is a small time interval between the creation of the + // CF property tables. If this function is called in quick successions + // (e.g. for multiple meter instances), we might get "0% utilization" + // as a result. + if (host->monotonicMs <= prevMonotonicMs) { + mtr->values[0] = *totalUsage; + return; + } + + CFMutableDictionaryRef properties = NULL; + kern_return_t ret = IORegistryEntryCreateCFProperties(dhost->GPUService, &properties, kCFAllocatorDefault, kNilOptions); + if (ret != KERN_SUCCESS || !properties) + return; + + CFDictionaryRef perfStats = CFDictionaryGetValue(properties, CFSTR("PerformanceStatistics")); + if (!perfStats) + goto cleanup; + + assert(CFGetTypeID(perfStats) == CFDictionaryGetTypeID()); + + CFNumberRef deviceUtil = CFDictionaryGetValue(perfStats, CFSTR("Device Utilization %")); + if (!deviceUtil) + goto cleanup; + + int device = -1; + CFNumberGetValue(deviceUtil, kCFNumberIntType, &device); + *totalUsage = (double)device; + + prevMonotonicMs = host->monotonicMs; + +cleanup: + CFRelease(properties); + + mtr->values[0] = *totalUsage; +} + void Platform_setMemoryValues(Meter* mtr) { const DarwinMachine* dhost = (const DarwinMachine*) mtr->host; #ifdef HAVE_STRUCT_VM_STATISTICS64 diff --git a/darwin/Platform.h b/darwin/Platform.h index f67db8ff4..23dd52089 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -58,6 +58,8 @@ pid_t Platform_getMaxPid(void); double Platform_setCPUValues(Meter* mtr, unsigned int cpu); +void Platform_setGPUValues(Meter* mtr, double* totalUsage, unsigned long long* totalGPUTimeDiff); + void Platform_setMemoryValues(Meter* mtr); void Platform_setSwapValues(Meter* mtr);