Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions linux/GPU.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ in the source distribution for its full text.
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>

#include "XUtils.h"
#include "linux/Compat.h"
#include "linux/LinuxMachine.h"

Expand Down Expand Up @@ -45,6 +47,42 @@ static bool is_duplicate_client(const ClientInfo* parsed, ClientID id, const cha
return false;
}

/*
* Parses the value of a "drm-resident-<region>" / "drm-memory-<region>" fdinfo
* line (the text right after the ':') into a byte count. Rejects malformed or
* unrecognized unit suffixes and multiplications that would overflow.
*/
static bool parse_drm_memory_value(const char* text, unsigned long long int* outBytes) {
char* endptr;
errno = 0;
unsigned long long int value = strtoull(text, &endptr, 10);
if (errno != 0)
return false;

while (*endptr == ' ')
endptr++;

unsigned long long int multiplier;
if (*endptr == '\0')
multiplier = 1;
else if (String_eq(endptr, "KiB"))
multiplier = 1024ULL;
else if (String_eq(endptr, "MiB"))
multiplier = 1024ULL * 1024ULL;
else if (String_eq(endptr, "GiB"))
multiplier = 1024ULL * 1024ULL * 1024ULL;
else if (String_eq(endptr, "TiB"))
multiplier = 1024ULL * 1024ULL * 1024ULL * 1024ULL;
else
return false;

if (multiplier > 1 && value > ULLONG_MAX / multiplier)
return false;

*outBytes = value * multiplier;
return true;
}

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;
Expand Down Expand Up @@ -83,6 +121,7 @@ void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t
DIR* fdinfoDir = NULL;
ClientInfo* parsed_ids = NULL;
unsigned long long int new_gpu_time = 0;
unsigned long long int new_gpu_memory = 0;

/* check only if active in last check or last scan was more than 5s ago */
if (lp->gpu_activityMs != 0 && host->monotonicMs - lp->gpu_activityMs < 5000) {
Expand Down Expand Up @@ -193,6 +232,33 @@ void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t
update_machine_gpu(lpt, value, engineStart, delim - engineStart);
}
}
} else if ((line[0] == 'm' && String_startsWith(line, "memory-")) ||
(line[0] == 'r' && String_startsWith(line, "resident-"))) {
/*
* "drm-resident-<region>" is the current key for backing-store size;
* "drm-memory-<region>" is its deprecated alias (amdgpu only). A given
* driver emits only one of the two per region, so summing both is safe.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Deduplicate the deprecated alias by region.

When amdgpu emits drm-resident-vram and drm-memory-vram in one fdinfo entry, this branch adds both values. The kernel emits both keys from the same resident statistic. Client-ID deduplication does not apply within one entry, so GPU_MEMORY can report twice the actual value. Prefer drm-resident-<region> and use drm-memory-<region> only when that region has no resident key. (github.com)

*/
if (sstate == SECST_DUPLICATE)
continue;

const char* delim = strchr(line, ':');
if (!delim)
continue;

unsigned long long int bytesValue;
if (!parse_drm_memory_value(delim + 1, &bytesValue))
continue;

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_memory += bytesValue;
}
} /* finished parsing lines */

Expand Down Expand Up @@ -228,6 +294,7 @@ void GPU_readProcessData(LinuxProcessTable* lpt, LinuxProcess* lp, openat_arg_t
out:

lp->gpu_time = new_gpu_time;
lp->gpu_memory = new_gpu_memory;

while (parsed_ids) {
ClientInfo* next = parsed_ids->next;
Expand Down
4 changes: 4 additions & 0 deletions linux/LinuxProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
#endif
[GPU_TIME] = { .name = "GPU_TIME", .title = "GPU_TIME ", .description = "Total GPU time", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = 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, },
[GPU_MEMORY] = { .name = "GPU_MEMORY", .title = "GPU_MEM ", .description = "GPU memory used by the process", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, },
};

Process* LinuxProcess_new(const Machine* host) {
Expand Down Expand Up @@ -239,6 +240,7 @@ static void LinuxProcess_rowWriteField(const Row* super, RichString* str, Proces
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: Row_printNanoseconds(str, lp->gpu_time, coloring); return;
case GPU_MEMORY: Row_printBytes(str, lp->gpu_memory, coloring); return;
case M_DRS: Row_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return;
case M_LRS:
if (lp->m_lrs) {
Expand Down Expand Up @@ -449,6 +451,8 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce
}
case GPU_TIME:
return SPACESHIP_NUMBER(p1->gpu_time, p2->gpu_time);
case GPU_MEMORY:
return SPACESHIP_NUMBER(p1->gpu_memory, p2->gpu_memory);
case ISCONTAINER:
return SPACESHIP_NUMBER(v1->isRunningInContainer, v2->isRunningInContainer);
default:
Expand Down
2 changes: 2 additions & 0 deletions linux/LinuxProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ typedef struct LinuxProcess_ {
unsigned long long int gpu_time;
/* GPU utilization in percent */
float gpu_percent;
/* GPU memory used by the process, in bytes */
unsigned long long int gpu_memory;
/* Activity of GPU: 0 if active, otherwise time of last scan in milliseconds */
uint64_t gpu_activityMs;

Expand Down
1 change: 1 addition & 0 deletions linux/ProcessField.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ in the source distribution for its full text.
GPU_PERCENT = 133, \
ISCONTAINER = 134, \
M_EPSS = 135, \
GPU_MEMORY = 136, \
// End of list


Expand Down