-
-
Notifications
You must be signed in to change notification settings - Fork 599
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 4 commits
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 |
|---|---|---|
|
|
@@ -798,3 +798,98 @@ static void Platform_getOSRelease(char* buffer, size_t bufferLen) { | |
| const char* Platform_getRelease(void) { | ||
| return Generic_unameRelease(Platform_getOSRelease); | ||
| } | ||
|
|
||
| void Platform_setGPUProcesses(DarwinProcessTable* dpt) { | ||
| const Machine* host = dpt->super.super.host; | ||
| const DarwinMachine* dhost = (const DarwinMachine*) host; | ||
|
|
||
| if (!dhost->GPUService) | ||
| return; | ||
|
|
||
| io_iterator_t iterator; | ||
| if (IORegistryEntryGetChildIterator(dhost->GPUService, kIOServicePlane, &iterator) != KERN_SUCCESS) | ||
| return; | ||
|
|
||
| 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; | ||
|
|
||
| uint64_t gpuTime = 0; | ||
| const Table* table = &dpt->super.super; | ||
| DarwinProcess* dp = (DarwinProcess*) Hashtable_get(table->table, pid); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Also avoids this extra variable only used once. |
||
| if (!dp) | ||
| goto cleanup; | ||
|
|
||
| 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; | ||
| } | ||
|
BenBE marked this conversation as resolved.
Outdated
|
||
|
|
||
| uint64_t accumulatedGPUTime = 0; | ||
| CFNumberGetValue(accumulatedGPUTimeRef, kCFNumberLongLongType, &accumulatedGPUTime); | ||
|
|
||
| gpuTime += accumulatedGPUTime; | ||
|
BenBE marked this conversation as resolved.
|
||
| } | ||
|
|
||
| CFRelease(appUsage); | ||
| } else if (IOObjectConformsTo(entry, "IOAccelSubmitter2")) { | ||
| CFNumberRef accumulatedGPUTimeRef = IORegistryEntryCreateCFProperty(entry, CFSTR("accumulatedGPUTime"), kCFAllocatorDefault, kNilOptions); | ||
| if (!accumulatedGPUTimeRef) | ||
| goto cleanup; | ||
|
|
||
| uint64_t accumulatedGPUTime = 0; | ||
| CFNumberGetValue(accumulatedGPUTimeRef, kCFNumberLongLongType, &accumulatedGPUTime); | ||
|
|
||
| gpuTime += accumulatedGPUTime; | ||
|
BenBE marked this conversation as resolved.
|
||
|
|
||
| CFRelease(accumulatedGPUTimeRef); | ||
| } else { | ||
| goto cleanup; | ||
| } | ||
|
|
||
| if (gpuTime > 0) { | ||
| uint64_t gputimeDelta = saturatingSub(gpuTime, dp->gpu_time); | ||
| uint64_t monotonicTimeDelta = host->monotonicMs - host->prevMonotonicMs; | ||
| dp->gpu_percent = 100.0F * gputimeDelta / (1000 * 1000) / monotonicTimeDelta; | ||
| dp->gpu_time = gpuTime; | ||
| dp->gpu_time_updated = true; | ||
| } | ||
|
|
||
| cleanup: | ||
| IOObjectRelease(entry); | ||
| } | ||
|
|
||
| // Clear GPU time and percent for processes not found in the GPU process list | ||
| const Vector* rows = dpt->super.super.rows; | ||
| int table_size = Vector_size(rows); | ||
| for (int i = 0; i < table_size; ++i) { | ||
| DarwinProcess* dp = (DarwinProcess*) Vector_get(rows, i); | ||
| if (!dp->gpu_time_updated) { | ||
| dp->gpu_time = 0; | ||
| dp->gpu_percent = 0.0F; | ||
| } | ||
| } | ||
|
|
||
| IOObjectRelease(iterator); | ||
| } | ||
| 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 = "GPU_TIME", .title = "GPU TIME", .description = "Total GPU time", .flags = PROCESS_FLAG_LINUX_GPU, .defaultSortDesc = true, }, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given this is meant as an analog to |
||
| [PERCENT_GPU] = { .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) { | ||
|
|
@@ -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: { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a question. What motivated you to rename the field from For me, the renaming of the fields doesn't make them easier to read. This is beside the compatibility problem I pointed out to you before, which you have fixed. I just wonder why this idea in the first place.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because I noticed that the previous percentages were all named like
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh I see. Thus it was an error when the GPU percentage fields were introduced in htop Linux monitoring. Because htop had no requirement on whether the word PERCENT should be placed at the start or at the end. If you
It's not my call. Maybe BenBE can decide this.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given there are other fields like |
||
| 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); | ||
|
|
||
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.
Please provide some (short) inline documentation about what the contents of buffers are expected to be. Reference to corresponding documentation is fine; throw a copy to archive.org if possible in case the link breaks.
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.
By the way, I don't like the assumption that
pid_talways has the same width asint. It's better tosscanfinto anint-type buffer then cast topid_twhen using.