-
-
Notifications
You must be signed in to change notification settings - Fork 555
Darwin: Add GPU monitoring support #1855
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
base: main
Are you sure you want to change the base?
Changes from 1 commit
83c3a9e
9327a96
937a7d7
1ccd9b7
3ac5de9
6055adb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -365,14 +365,9 @@ void Platform_setGPUValues(Meter* mtr, double* totalUsage, unsigned long long* t | |||||||||||||||||||
| 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")); | ||||||||||||||||||||
| CFDictionaryRef perfStats = IORegistryEntryCreateCFProperty(dhost->GPUService, CFSTR("PerformanceStatistics"), kCFAllocatorDefault, kNilOptions); | ||||||||||||||||||||
BenBE marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||
| if (!perfStats) | ||||||||||||||||||||
| goto cleanup; | ||||||||||||||||||||
| return; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| assert(CFGetTypeID(perfStats) == CFDictionaryGetTypeID()); | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -387,7 +382,7 @@ void Platform_setGPUValues(Meter* mtr, double* totalUsage, unsigned long long* t | |||||||||||||||||||
| prevMonotonicMs = host->monotonicMs; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| cleanup: | ||||||||||||||||||||
| CFRelease(properties); | ||||||||||||||||||||
| CFRelease(perfStats); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| mtr->values[0] = *totalUsage; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
@@ -798,3 +793,86 @@ static void Platform_getOSRelease(char* buffer, size_t bufferLen) { | |||||||||||||||||||
| const char* Platform_getRelease(void) { | ||||||||||||||||||||
| return Generic_unameRelease(Platform_getOSRelease); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Hashtable* Platform_getGPUProcesses(const Machine* host) { | ||||||||||||||||||||
| const DarwinMachine* dhost = (const DarwinMachine*)host; | ||||||||||||||||||||
| if (!dhost->GPUService) { | ||||||||||||||||||||
| return NULL; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Hashtable* gps = Hashtable_new(64, true); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| io_iterator_t iterator; | ||||||||||||||||||||
| if (IORegistryEntryGetChildIterator(dhost->GPUService, kIOServicePlane, &iterator) == KERN_SUCCESS) { | ||||||||||||||||||||
BenBE marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||
| io_registry_entry_t entry; | ||||||||||||||||||||
| while ((entry = IOIteratorNext(iterator))) { | ||||||||||||||||||||
| io_struct_inband_t buffer; | ||||||||||||||||||||
| uint32_t size = sizeof(buffer); | ||||||||||||||||||||
| if (IORegistryEntryGetProperty(entry, "IOUserClientCreator", buffer, &size) != KERN_SUCCESS) | ||||||||||||||||||||
| goto cleanup; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| pid_t pid; | ||||||||||||||||||||
| if (sscanf(buffer, "pid %d", &pid) != 1) | ||||||||||||||||||||
| goto cleanup; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| unsigned long long gpuTime = 0; | ||||||||||||||||||||
| unsigned long long* data = Hashtable_get(gps, (ht_key_t) pid); | ||||||||||||||||||||
| if (data) | ||||||||||||||||||||
| gpuTime = *data; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if (IOObjectConformsTo(entry, "IOGPUDeviceUserClient")) { | ||||||||||||||||||||
| CFArrayRef appUsage = IORegistryEntryCreateCFProperty(entry, CFSTR("AppUsage"), kCFAllocatorDefault, kNilOptions); | ||||||||||||||||||||
| if (!appUsage) | ||||||||||||||||||||
| goto cleanup; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| assert(CFGetTypeID(appUsage) == CFArrayGetTypeID()); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Sum up the GPU time for all command queues for this client | ||||||||||||||||||||
| size_t count = CFArrayGetCount(appUsage); | ||||||||||||||||||||
| for (size_t i = 0; i < count; ++i) { | ||||||||||||||||||||
| CFDictionaryRef appUsageEntry = CFArrayGetValueAtIndex(appUsage, i); | ||||||||||||||||||||
| assert(CFGetTypeID(appUsageEntry) == CFDictionaryGetTypeID()); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| CFNumberRef accumulatedGPUTimeRef = CFDictionaryGetValue(appUsageEntry, CFSTR("accumulatedGPUTime")); | ||||||||||||||||||||
| if (!accumulatedGPUTimeRef) { | ||||||||||||||||||||
| CFRelease(appUsage); | ||||||||||||||||||||
| goto cleanup; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| unsigned long long accumulatedGPUTime = 0; | ||||||||||||||||||||
| CFNumberGetValue(accumulatedGPUTimeRef, kCFNumberLongLongType, &accumulatedGPUTime); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| gpuTime += accumulatedGPUTime; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| CFRelease(appUsage); | ||||||||||||||||||||
| } else if (IOObjectConformsTo(entry, "IOAccelSubmitter2")) { | ||||||||||||||||||||
| CFNumberRef accumulatedGPUTimeRef = IORegistryEntryCreateCFProperty(entry, CFSTR("accumulatedGPUTime"), kCFAllocatorDefault, kNilOptions); | ||||||||||||||||||||
| if (!accumulatedGPUTimeRef) | ||||||||||||||||||||
| goto cleanup; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| unsigned long long accumulatedGPUTime = 0; | ||||||||||||||||||||
| CFNumberGetValue(accumulatedGPUTimeRef, kCFNumberLongLongType, &accumulatedGPUTime); | ||||||||||||||||||||
| CFRelease(accumulatedGPUTimeRef); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| gpuTime += accumulatedGPUTime; | ||||||||||||||||||||
|
||||||||||||||||||||
| CFRelease(accumulatedGPUTimeRef); | |
| gpuTime += accumulatedGPUTime; | |
| gpuTime += accumulatedGPUTime; | |
| CFRelease(accumulatedGPUTimeRef); |
Re-order things to have resource management as last action in branch.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not handle this early when fetching the item from the hashtable. This will create some entries with zeros, but it makes the overall handling much simpler, as you can skip later NULL checks (knowing you have memory).
Another thing I kinda dislike here is the amount of malloc calls (memory fragmentation) this causes. But given we are talking ull here (why not write uint64_t instead?), why not drop this hole thing entirely and store the actual data into the pointers directly using uintptr_t? Given the unit of gpuTime this might be limiting on 32 bit systems, of which we do not have too many (~0) left that would also support this interface, I guess …
Given this only affects Darwin 10.13 and 10.14 in compat mode, let's put an assert(sizeof(uintptr_t) > sizeof(uint32_t)); in and be done on that front …
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BenBE No, I can think of a better idea. It is to remove this new Hashtable, and instead expand DarwinProcess for this GPU time information.
Consider that this new Hashtable also uses PID as the indexing key, the two tables can be merged to one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Explorer09 That make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Be careful that you may encounter items where no process object has been stored yet. There are some items in the process struct that are only set upon first encountering the entry. Apart from this, the idea makes sense.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| cleanup: | |
| IOObjectRelease(entry); | |
| } | |
| IOObjectRelease(iterator); | |
| cleanup: | |
| IOObjectRelease(entry); | |
| } | |
| IOObjectRelease(iterator); |
Labels should go first column of the line. Also let's give the code some space.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,8 +110,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", .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, }, | ||
| [TIME_GPU] = { .name = "TIME_GPU", .title = "GPU TIME+", .description = "Total GPU time", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, | ||
| [PERCENT_GPU] = { .name = "PERCENT_GPU", .title = " GPU% ", .description = "Percentage of the GPU time the process used in the last sampling", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, | ||
BenBE marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| Process* LinuxProcess_new(const Machine* host) { | ||
|
|
@@ -245,8 +245,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: Row_printNanoseconds(str, lp->gpu_time, coloring); return; | ||
| case PERCENT_GPU: Row_printPercentage(lp->gpu_percent, buffer, n, 5, &attr); break; | ||
| case TIME_GPU: Row_printNanoseconds(str, lp->gpu_time, coloring); return; | ||
| case M_DRS: Row_printBytes(str, lp->m_drs * lhost->pageSize, coloring); return; | ||
| case M_LRS: | ||
| if (lp->m_lrs) { | ||
|
|
@@ -455,14 +455,14 @@ 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: { | ||
| case PERCENT_GPU: { | ||
|
||
| int r = compareRealNumbers(p1->gpu_percent, p2->gpu_percent); | ||
| if (r) | ||
| return r; | ||
|
|
||
| return SPACESHIP_NUMBER(p1->gpu_time, p2->gpu_time); | ||
| } | ||
| case GPU_TIME: | ||
| case TIME_GPU: | ||
| return SPACESHIP_NUMBER(p1->gpu_time, p2->gpu_time); | ||
| case ISCONTAINER: | ||
| return SPACESHIP_NUMBER(v1->isRunningInContainer, v2->isRunningInContainer); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.