Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPUMeter code refactor & macOS support #1620

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
106 changes: 45 additions & 61 deletions linux/GPUMeter.c → GPUMeter.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,19 @@ in the source distribution for its full text.

#include "config.h" // IWYU pragma: keep

#include "linux/GPUMeter.h"
#include "GPUMeter.h"

#include <math.h>

#include "CRT.h"
#include "Platform.h"
#include "RichString.h"
#include "linux/LinuxMachine.h"


static size_t activeMeters;

bool GPUMeter_active(void) {
return activeMeters > 0;
}
#include "XUtils.h"

struct EngineData {
const char* key; /* owned by LinuxMachine */
unsigned long long int timeDiff;
};

static struct EngineData GPUMeter_engineData[4];
static unsigned long long int prevResidueTime, curResidueTime;
static double totalUsage;
static unsigned long long int totalGPUTimeDiff;
struct GPUMeterEngineData GPUMeter_engineData[4];
static double totalUsage = NAN;
static unsigned long long int totalGPUTimeDiff = -1ULL;

static const int GPUMeter_attributes[] = {
GPU_ENGINE_1,
Expand All @@ -38,6 +29,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)
Expand Down Expand Up @@ -83,71 +80,58 @@ 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) <= ARRAYSIZE(GPUMeter_attributes) - 1);

assert(ARRAYSIZE(GPUMeter_engineData) + 1 == ARRAYSIZE(GPUMeter_attributes));
Platform_setGPUValues(this, &totalUsage, &totalGPUTimeDiff);

totalGPUTimeDiff = saturatingSub(lhost->curGpuTime, lhost->prevGpuTime);
monotonictimeDelta = host->monotonicMs - host->prevMonotonicMs;

prevResidueTime = curResidueTime;
curResidueTime = lhost->curGpuTime;

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);

curResidueTime = saturatingSub(curResidueTime, gpuEngineData->curTime);

this->values[i] = 100.0 * GPUMeter_engineData[i].timeDiff / (1000 * 1000) / monotonictimeDelta;
if (!isNonnegative(totalUsage)) {
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "N/A");
return;
}

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) {
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_appendnAscii(out, CRT_colors[METER_TEXT], "%(", 2);
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], "%");
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;

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);
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], "%");
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], ")");
}
}
}

Expand All @@ -171,7 +155,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",
Expand Down
8 changes: 8 additions & 0 deletions linux/GPUMeter.h → GPUMeter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ myhtopheaders = \
EnvScreen.h \
FileDescriptorMeter.h \
FunctionBar.h \
GPUMeter.h \
Hashtable.h \
Header.h \
HeaderLayout.h \
Expand Down Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down Expand Up @@ -367,6 +367,7 @@ darwin_platform_headers = \
zfs/ZfsCompressedArcMeter.h

darwin_platform_sources = \
GPUMeter.c \
darwin/Platform.c \
darwin/PlatformHelpers.c \
darwin/DarwinMachine.c \
Expand Down
7 changes: 7 additions & 0 deletions darwin/DarwinMachine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions darwin/DarwinMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/

#include <IOKit/IOKitLib.h>
#include <mach/mach_host.h>
#include <sys/sysctl.h>

Expand All @@ -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;

Expand Down
54 changes: 54 additions & 0 deletions darwin/Platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ in the source distribution for its full text.

#include "darwin/Platform.h"

#include <assert.h>
#include <errno.h>
#include <math.h>
#include <stdlib.h>
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -144,6 +146,7 @@ const MeterClass* const Platform_meterTypes[] = {
&DiskIOMeter_class,
&NetworkIOMeter_class,
&FileDescriptorMeter_class,
&GPUMeter_class,
&BlankMeter_class,
NULL
};
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions darwin/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion linux/LinuxProcessTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ in the source distribution for its full text.
#include <sys/stat.h>

#include "Compat.h"
#include "GPUMeter.h"
#include "Hashtable.h"
#include "Machine.h"
#include "Macros.h"
Expand All @@ -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
Expand Down
Loading
Loading