diff --git a/CRT.c b/CRT.c index 05acc92ac..c4c9921ac 100644 --- a/CRT.c +++ b/CRT.c @@ -199,6 +199,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Magenta, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), + [GPU_ENGINE_1] = ColorPair(Green, Black), + [GPU_ENGINE_2] = ColorPair(Yellow, Black), + [GPU_ENGINE_3] = ColorPair(Red, Black), + [GPU_ENGINE_4] = A_BOLD | ColorPair(Blue, Black), + [GPU_RESIDUE] = ColorPair(Magenta, Black), [PANEL_EDIT] = ColorPair(White, Blue), [SCREENS_OTH_BORDER] = ColorPair(Blue, Blue), [SCREENS_OTH_TEXT] = ColorPair(Black, Blue), @@ -312,6 +317,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = A_BOLD, [CPU_STEAL] = A_DIM, [CPU_GUEST] = A_DIM, + [GPU_ENGINE_1] = A_BOLD, + [GPU_ENGINE_2] = A_NORMAL, + [GPU_ENGINE_3] = A_REVERSE | A_BOLD, + [GPU_ENGINE_4] = A_REVERSE, + [GPU_RESIDUE] = A_BOLD, [PANEL_EDIT] = A_BOLD, [SCREENS_OTH_BORDER] = A_DIM, [SCREENS_OTH_TEXT] = A_DIM, @@ -425,6 +435,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, White), [CPU_STEAL] = ColorPair(Cyan, White), [CPU_GUEST] = ColorPair(Cyan, White), + [GPU_ENGINE_1] = ColorPair(Green, White), + [GPU_ENGINE_2] = ColorPair(Yellow, White), + [GPU_ENGINE_3] = ColorPair(Red, White), + [GPU_ENGINE_4] = ColorPair(Blue, White), + [GPU_RESIDUE] = ColorPair(Magenta, White), [PANEL_EDIT] = ColorPair(White, Blue), [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black, White), [SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black, White), @@ -538,6 +553,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Black, Black), [CPU_GUEST] = ColorPair(Black, Black), + [GPU_ENGINE_1] = ColorPair(Green, Black), + [GPU_ENGINE_2] = ColorPair(Yellow, Black), + [GPU_ENGINE_3] = ColorPair(Red, Black), + [GPU_ENGINE_4] = ColorPair(Blue, Black), + [GPU_RESIDUE] = ColorPair(Magenta, Black), [PANEL_EDIT] = ColorPair(White, Blue), [SCREENS_OTH_BORDER] = ColorPair(Blue, Black), [SCREENS_OTH_TEXT] = ColorPair(Blue, Black), @@ -651,6 +671,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Black, Blue), [CPU_STEAL] = ColorPair(White, Blue), [CPU_GUEST] = ColorPair(White, Blue), + [GPU_ENGINE_1] = A_BOLD | ColorPair(Green, Blue), + [GPU_ENGINE_2] = A_BOLD | ColorPair(Yellow, Blue), + [GPU_ENGINE_3] = A_BOLD | ColorPair(Red, Blue), + [GPU_ENGINE_4] = A_BOLD | ColorPair(White, Blue), + [GPU_RESIDUE] = A_BOLD | ColorPair(Magenta, Blue), [PANEL_EDIT] = ColorPair(White, Blue), [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow, Blue), [SCREENS_OTH_TEXT] = ColorPair(Cyan, Blue), @@ -762,6 +787,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), + [GPU_ENGINE_1] = ColorPair(Green, Black), + [GPU_ENGINE_2] = ColorPair(Yellow, Black), + [GPU_ENGINE_3] = ColorPair(Red, Black), + [GPU_ENGINE_4] = ColorPair(Blue, Black), + [GPU_RESIDUE] = ColorPair(Magenta, Black), [PANEL_EDIT] = ColorPair(White, Cyan), [SCREENS_OTH_BORDER] = ColorPair(White, Black), [SCREENS_OTH_TEXT] = ColorPair(Cyan, Black), diff --git a/CRT.h b/CRT.h index f5fd94527..a7ea3bb21 100644 --- a/CRT.h +++ b/CRT.h @@ -123,6 +123,11 @@ typedef enum ColorElements_ { CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, + GPU_ENGINE_1, + GPU_ENGINE_2, + GPU_ENGINE_3, + GPU_ENGINE_4, + GPU_RESIDUE, PANEL_EDIT, SCREENS_OTH_BORDER, SCREENS_OTH_TEXT, diff --git a/Makefile.am b/Makefile.am index 080411f35..201991e2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -172,6 +172,8 @@ linux_platform_headers = \ generic/hostname.h \ generic/uname.h \ linux/CGroupUtils.h \ + linux/GPU.h \ + linux/GPUMeter.h \ linux/HugePageMeter.h \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ @@ -196,6 +198,8 @@ linux_platform_sources = \ 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/GPU.c b/linux/GPU.c new file mode 100644 index 000000000..af62c4175 --- /dev/null +++ b/linux/GPU.c @@ -0,0 +1,256 @@ +/* +htop - GPU.c +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "linux/GPU.h" + +#include +#include +#include +#include +#include + +#include "XUtils.h" + +#include "linux/LinuxMachine.h" + + +#define INVALID_CLIENT_ID ((unsigned long long int)-1) + + +struct client_id { + char* pdev; + unsigned long long int id; + struct client_id* next; +}; + +enum section_state { + SECST_UNKNOWN, + SECST_DUPLICATE, + SECST_NEW, +}; + +static bool is_duplicate_client(const struct client_id* parsed, unsigned long long int id, const char* pdev) { + for (; parsed; parsed = parsed->next) { + if (id == parsed->id && String_eq_nullable(pdev, parsed->pdev)) { + return true; + } + } + + return false; +} + +static void update_machine_gpu(LinuxProcessTable* lpt, unsigned long long int time, const char* engine, size_t engine_len) { + Machine* host = lpt->super.super.host; + LinuxMachine* lhost = (LinuxMachine*) host; + GPUEngineData** engineData = &lhost->gpuEngineData; + + while (*engineData) { + if (strncmp((*engineData)->key, engine, engine_len) == 0 && (*engineData)->key[engine_len] == '\0') + break; + + engineData = &((*engineData)->next); + } + + if (!*engineData) { + GPUEngineData* newData; + + newData = xMalloc(sizeof(*newData)); + *newData = (GPUEngineData) { + .prevTime = 0, + .curTime = 0, + .key = xStrndup(engine, engine_len), + .next = NULL, + }; + + *engineData = newData; + } + + (*engineData)->curTime += time; + lhost->curGpuTime += time; +} + +/* + * Documentation reference: + * https://www.kernel.org/doc/html/latest/gpu/drm-usage-stats.html + */ +void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t procFd) { + int fdinfoFd = -1; + DIR* fdinfoDir = NULL; + struct client_id* parsed_ids = NULL; + unsigned long long int new_gpu_time = 0; + + fdinfoFd = Compat_openat(procFd, "fdinfo", O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC); + if (fdinfoFd == -1) + goto out; + + fdinfoDir = fdopendir(fdinfoFd); + if (!fdinfoDir) + goto out; + fdinfoFd = -1; + +#ifndef HAVE_OPENAT + char fdinfoPathBuf[32]; + xSnprintf(fdinfoPathBuf, sizeof(fdinfoPathBuf), "/proc/%u/fdinfo", lp->super.pid); +#endif + + while (true) { + const struct dirent* entry; + const char* ename; + FILE* fp; + char* pdev = NULL; + unsigned long long int client_id = INVALID_CLIENT_ID; + enum section_state sstate = SECST_UNKNOWN; + char buf[256]; + + entry = readdir(fdinfoDir); + if (!entry) + break; + ename = entry->d_name; + + if (String_eq(ename, ".") || + String_eq(ename, "..") || + String_eq(ename, "0") || + String_eq(ename, "1") || + String_eq(ename, "2")) + continue; + +#ifdef HAVE_OPENAT + fp = Compat_fopenat(dirfd(fdinfoDir), ename, "r"); +#else + fp = Compat_fopenat(fdinfoPathBuf, ename, "r"); +#endif + if (!fp) + continue; + + while (fgets(buf, sizeof(buf), fp)) { + if (!String_startsWith(buf, "drm-")) + continue; + + if (String_startsWith(buf, "drm-client-id:")) { + char *endptr; + + if (sstate == SECST_NEW) { + struct client_id* new; + + assert(client_id != INVALID_CLIENT_ID); + + new = xMalloc(sizeof(*new)); + *new = (struct client_id) { + .id = client_id, + .pdev = pdev, + .next = parsed_ids, + }; + pdev = NULL; + + parsed_ids = new; + } + + sstate = SECST_UNKNOWN; + + errno = 0; + client_id = strtoull(buf + strlen("drm-client-id:"), &endptr, 10); + if (errno || *endptr != '\n') + client_id = INVALID_CLIENT_ID; + } else if (String_startsWith(buf, "drm-pdev:")) { + const char* p; + char* q; + + p = buf + strlen("drm-pdev:"); + + while (isspace((unsigned char)*p)) + p++; + + q = strchr(p, '\n'); + if (q) + *q = '\0'; + + assert(!pdev || String_eq(pdev, p)); + if (!pdev) + pdev = xStrdup(p); + } else if (String_startsWith(buf, "drm-engine-")) { + const char* delim; + const char* engineStart; + char* endptr; + unsigned long long int value; + + if (sstate == SECST_DUPLICATE) + continue; + + if (String_startsWith(buf, "drm-engine-capacity-")) + continue; + + delim = strchr(buf, ':'); + engineStart = buf + strlen("drm-engine-"); + + errno = 0; + value = strtoull(delim + 1, &endptr, 10); + if (errno == 0 && String_startsWith(endptr, " ns")) { + if (sstate == SECST_UNKNOWN) { + if (client_id != INVALID_CLIENT_ID && !is_duplicate_client(parsed_ids, client_id, pdev)) + sstate = SECST_NEW; + else + sstate = SECST_DUPLICATE; + } + + if (sstate == SECST_NEW) { + new_gpu_time += value; + update_machine_gpu(lpt, value, engineStart, delim - engineStart); + } + } + } + } /* finished parsing lines */ + + fclose(fp); + + if (sstate == SECST_NEW) { + struct client_id* new; + + assert(client_id != INVALID_CLIENT_ID); + + new = xMalloc(sizeof(*new)); + *new = (struct client_id) { + .id = client_id, + .pdev = pdev, + .next = parsed_ids, + }; + pdev = NULL; + + parsed_ids = new; + } + + free(pdev); + } /* finished parsing fdinfo entries */ + + if (new_gpu_time > 0) { + const Machine* host = lp->super.super.host; + unsigned long long int gputimeDelta; + uint64_t monotonictimeDelta; + + Row_updateFieldWidth(GPU_TIME, ceil(log10(new_gpu_time))); + + gputimeDelta = saturatingSub(new_gpu_time, lp->gpu_time); + monotonictimeDelta = host->monotonicMs - host->prevMonotonicMs; + lp->gpu_percent = 100.0f * gputimeDelta / (1000 * 1000) / monotonictimeDelta; + } else + lp->gpu_percent = 0.0f; + +out: + + lp->gpu_time = new_gpu_time; + + while (parsed_ids) { + struct client_id* next = parsed_ids->next; + free(parsed_ids->pdev); + free(parsed_ids); + parsed_ids = next; + } + + if (fdinfoDir) + closedir(fdinfoDir); + if (fdinfoFd != -1) + close(fdinfoFd); +} diff --git a/linux/GPU.h b/linux/GPU.h new file mode 100644 index 000000000..b502d1471 --- /dev/null +++ b/linux/GPU.h @@ -0,0 +1,17 @@ +#ifndef HEADER_GPU +#define HEADER_GPU +/* +htop - GPU.h +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Compat.h" +#include "linux/LinuxProcess.h" +#include "linux/LinuxProcessTable.h" + + +void GPU_readProcessData(LinuxProcessTable* lpl, LinuxProcess* lp, openat_arg_t procFd); + +#endif /* HEADER_GPU */ diff --git a/linux/GPUMeter.c b/linux/GPUMeter.c new file mode 100644 index 000000000..7d2ea17db --- /dev/null +++ b/linux/GPUMeter.c @@ -0,0 +1,168 @@ +/* +htop - GPUMeter.c +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "linux/GPUMeter.h" + +#include "CRT.h" +#include "RichString.h" +#include "linux/LinuxMachine.h" + + +static unsigned int activeMeters; + +bool GPUMeter_active(void) { + return activeMeters > 0; +} + +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; + +static const int GPUMeter_attributes[] = { + GPU_ENGINE_1, + GPU_ENGINE_2, + GPU_ENGINE_3, + GPU_ENGINE_4, + GPU_RESIDUE, +}; + +static int humanTimeUnit(char* buffer, size_t size, unsigned long long int value) { + + if (value < 1000) + return snprintf(buffer, size, "%3lluns", value); + + value /= 1000; + + if (value < 1000) + return snprintf(buffer, size, "%3lluus", value); + + value /= 1000; + + if (value < 1000) + return snprintf(buffer, size, "%3llums", value); + + value /= 1000; + + if (value < 600) + return snprintf(buffer, size, "%3llus", value); + + value /= 60; + + if (value < 600) + return snprintf(buffer, size, "%3llum", value); + + value /= 60; + + if (value < 96) + return snprintf(buffer, size, "%3lluh", value); + + value /= 24; + + return snprintf(buffer, size, "%3llud", 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; + + 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; + } + + 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, '%'); +} + +static void GPUMeter_display(const Object* cast, RichString* out) { + char buffer[50]; + int written; + const Meter* this = (const Meter*)cast; + unsigned int i; + + 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); + written = humanTimeUnit(buffer, sizeof(buffer), totalGPUTimeDiff); + RichString_appendnAscii(out, CRT_colors[METER_VALUE], buffer, written); + RichString_appendnAscii(out, CRT_colors[METER_TEXT], ")", 1); + + 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], GPUMeter_engineData[i].key); + RichString_appendnAscii(out, CRT_colors[METER_TEXT], ":", 1); + 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); + } +} + +static void GPUMeter_init(Meter* this ATTR_UNUSED) { + activeMeters++; +} + +static void GPUMeter_done(Meter* this ATTR_UNUSED) { + assert(activeMeters > 0); + activeMeters--; +} + +const MeterClass GPUMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = GPUMeter_display, + }, + .init = GPUMeter_init, + .done = GPUMeter_done, + .updateValues = GPUMeter_updateValues, + .defaultMode = BAR_METERMODE, + .maxItems = ARRAYSIZE(GPUMeter_engineData) + 1, + .total = 100.0, + .attributes = GPUMeter_attributes, + .name = "GPU", + .uiName = "GPU usage", + .caption = "GPU" +}; diff --git a/linux/GPUMeter.h b/linux/GPUMeter.h new file mode 100644 index 000000000..a770ec779 --- /dev/null +++ b/linux/GPUMeter.h @@ -0,0 +1,19 @@ +#ifndef HEADER_GPUMeter +#define HEADER_GPUMeter +/* +htop - GPUMeter.h +(C) 2023 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include + +#include "Meter.h" + + +extern const MeterClass GPUMeter_class; + +bool GPUMeter_active(void); + +#endif /* HEADER_GPUMeter */ diff --git a/linux/LinuxMachine.c b/linux/LinuxMachine.c index 34cd5d0b1..b6f6d021e 100644 --- a/linux/LinuxMachine.c +++ b/linux/LinuxMachine.c @@ -699,7 +699,17 @@ Machine* Machine_new(UsersTable* usersTable, uid_t userId) { void Machine_delete(Machine* super) { LinuxMachine* this = (LinuxMachine*) super; + GPUEngineData* gpuEngineData = this->gpuEngineData; + Machine_done(super); + + while (gpuEngineData) { + GPUEngineData* next = gpuEngineData->next; + free(gpuEngineData->key); + free(gpuEngineData); + gpuEngineData = next; + } + free(this->cpuData); free(this); } diff --git a/linux/LinuxMachine.h b/linux/LinuxMachine.h index c3076a304..4dd630b24 100644 --- a/linux/LinuxMachine.h +++ b/linux/LinuxMachine.h @@ -57,6 +57,12 @@ typedef struct CPUData_ { bool online; } CPUData; +typedef struct GPUEngineData_ { + unsigned long long int prevTime, curTime; + char* key; + struct GPUEngineData_* next; +} GPUEngineData; + typedef struct LinuxMachine_ { Machine super; @@ -77,6 +83,9 @@ typedef struct LinuxMachine_ { memory_t availableMem; + unsigned long long int prevGpuTime, curGpuTime; + GPUEngineData* gpuEngineData; + ZfsArcStats zfs; ZramStats zram; ZswapStats zswap; diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index d5f2b2289..494f9c876 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -101,6 +101,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { #ifdef SCHEDULER_SUPPORT [SCHEDULERPOLICY] = { .name = "SCHEDULERPOLICY", .title = "SCHED ", .description = "Current scheduling policy of the process", .flags = PROCESS_FLAG_SCHEDPOL, }, #endif + [GPU_TIME] = { .name = "GPU_TIME", .title = " GPU_TIME", .description = "Total GPU time in nano seconds", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, .autoWidth = true, .autoTitleRightAlign = true, }, + [GPU_PERCENT] = { .name = "GPU_PERCENT", .title = "GPU% ", .description = "Percentage of the GPU time the process used in the last sampling", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, }; Process* LinuxProcess_new(const Machine* host) { @@ -231,6 +233,8 @@ static void LinuxProcess_rowWriteField(const Row* super, RichString* str, Proces switch (field) { case CMINFLT: Row_printCount(str, lp->cminflt, coloring); return; case CMAJFLT: Row_printCount(str, lp->cmajflt, coloring); return; + case GPU_PERCENT: Row_printPercentage(lp->gpu_percent, buffer, n, 5, &attr); break; + case GPU_TIME: xSnprintf(buffer, n, "%*llu", Row_fieldWidths[GPU_TIME], lp->gpu_time); break; case M_DRS: Row_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; case M_LRS: if (lp->m_lrs) { @@ -407,6 +411,17 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce return SPACESHIP_NUMBER(p1->autogroup_id, p2->autogroup_id); case AUTOGROUP_NICE: return SPACESHIP_NUMBER(p1->autogroup_nice, p2->autogroup_nice); + case GPU_PERCENT: { + int r; + + r = compareRealNumbers(p1->gpu_percent, p2->gpu_percent); + if (r) + return r; + + return SPACESHIP_NUMBER(p1->gpu_time, p2->gpu_time); + } + case GPU_TIME: + return SPACESHIP_NUMBER(p1->gpu_time, p2->gpu_time); default: return Process_compareByKey_Base(v1, v2, key); } diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index 1cd0eaaff..152994c49 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -30,6 +30,7 @@ in the source distribution for its full text. #define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000 #define PROCESS_FLAG_LINUX_DELAYACCT 0x00040000 #define PROCESS_FLAG_LINUX_AUTOGROUP 0x00080000 +#define PROCESS_FLAG_LINUX_GPU 0x00100000 typedef struct LinuxProcess_ { Process super; @@ -105,6 +106,11 @@ typedef struct LinuxProcess_ { char* secattr; unsigned long long int last_mlrs_calctime; + /* Total GPU time used in nano seconds */ + unsigned long long int gpu_time; + /* GPU utilization in percent */ + float gpu_percent; + /* Autogroup scheduling (CFS) information */ long int autogroup_id; int autogroup_nice; diff --git a/linux/LinuxProcessTable.c b/linux/LinuxProcessTable.c index a55348431..c590405e6 100644 --- a/linux/LinuxProcessTable.c +++ b/linux/LinuxProcessTable.c @@ -49,6 +49,8 @@ in the source distribution for its full text. #include "Settings.h" #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 @@ -1545,6 +1547,13 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar } #endif + if (ss->flags & PROCESS_FLAG_LINUX_GPU || GPUMeter_active()) { + if (parent) + lp->gpu_time = ((const LinuxProcess*)parent)->gpu_time; + else + GPU_readProcessData(this, lp, procFd); + } + if (!proc->cmdline && statCommand[0] && (proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) { Process_updateCmdline(proc, statCommand, 0, strlen(statCommand)); @@ -1602,9 +1611,9 @@ static bool LinuxProcessTable_recurseProcTree(LinuxProcessTable* this, openat_ar void ProcessTable_goThroughEntries(ProcessTable* super) { LinuxProcessTable* this = (LinuxProcessTable*) super; - const Machine* host = super->super.host; + Machine* host = super->super.host; const Settings* settings = host->settings; - const LinuxMachine* lhost = (const LinuxMachine*) host; + LinuxMachine* lhost = (LinuxMachine*) host; if (settings->ss->flags & PROCESS_FLAG_LINUX_AUTOGROUP) { // Refer to sched(7) 'autogroup feature' section @@ -1616,6 +1625,19 @@ void ProcessTable_goThroughEntries(ProcessTable* super) { this->haveAutogroup = false; } + /* Shift GPU values */ + { + GPUEngineData* engine; + + lhost->prevGpuTime = lhost->curGpuTime; + lhost->curGpuTime = 0; + + for (engine = lhost->gpuEngineData; engine; engine = engine->next) { + engine->prevTime = engine->curTime; + engine->curTime = 0; + } + } + /* PROCDIR is an absolute path */ assert(PROCDIR[0] == '/'); #ifdef HAVE_OPENAT diff --git a/linux/Platform.c b/linux/Platform.c index 33bd8ced4..b4c408514 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -54,6 +54,7 @@ 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" @@ -253,6 +254,7 @@ const MeterClass* const Platform_meterTypes[] = { &SystemdMeter_class, &SystemdUserMeter_class, &FileDescriptorMeter_class, + &GPUMeter_class, NULL }; diff --git a/linux/ProcessField.h b/linux/ProcessField.h index 17cafa964..fa10bcf3f 100644 --- a/linux/ProcessField.h +++ b/linux/ProcessField.h @@ -46,6 +46,8 @@ in the source distribution for its full text. AUTOGROUP_ID = 127, \ AUTOGROUP_NICE = 128, \ CCGROUP = 129, \ + GPU_TIME = 130, \ + GPU_PERCENT = 131, \ // End of list