feat: add GPU information retrieval for darwin using cgo (#40)
* feat: add GPU information retrieval for darwin using cgo * fix kIOMasterPortDefault thing * return 0
This commit is contained in:
parent
f220134e5b
commit
65bf012579
@ -1,4 +1,4 @@
|
||||
//go:build darwin
|
||||
//go:build darwin && !cgo
|
||||
|
||||
package gpu
|
||||
|
||||
|
65
pkg/gpu/gpu_darwin_cgo.go
Normal file
65
pkg/gpu/gpu_darwin_cgo.go
Normal file
@ -0,0 +1,65 @@
|
||||
//go:build darwin && cgo
|
||||
|
||||
package gpu
|
||||
|
||||
// #cgo LDFLAGS: -framework IOKit -framework CoreFoundation
|
||||
// #include "stat/gpu_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func GoStrings(argc C.int, argv **C.char) []string {
|
||||
length := int(argc)
|
||||
tmpslice := unsafe.Slice(argv, length)
|
||||
gostrings := make([]string, length)
|
||||
for i, s := range tmpslice {
|
||||
gostrings[i] = C.GoString(s)
|
||||
}
|
||||
return gostrings
|
||||
}
|
||||
|
||||
func extractGPUInfo(key *C.char) ([]string, error) {
|
||||
devices := C.find_devices(key)
|
||||
if devices != nil {
|
||||
defer C.free(unsafe.Pointer(devices))
|
||||
length := 0
|
||||
for {
|
||||
device := *(**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(devices)) + uintptr(length)*unsafe.Sizeof(*devices)))
|
||||
if device == nil {
|
||||
break
|
||||
}
|
||||
length++
|
||||
}
|
||||
gpu := GoStrings(C.int(length), devices)
|
||||
return gpu, nil
|
||||
}
|
||||
return nil, errors.New("cannot find key")
|
||||
}
|
||||
|
||||
func GetGPUModel() ([]string, error) {
|
||||
vendorNames := []string{
|
||||
"AMD", "Intel", "Nvidia", "Apple",
|
||||
}
|
||||
|
||||
key := C.CString("model")
|
||||
defer C.free(unsafe.Pointer(key))
|
||||
|
||||
gi, err := extractGPUInfo(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var gpuModel []string
|
||||
for _, model := range gi {
|
||||
for _, vendor := range vendorNames {
|
||||
if strings.Contains(model, vendor) {
|
||||
gpuModel = append(gpuModel, model)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return gpuModel, nil
|
||||
}
|
144
pkg/gpu/stat/gpu_darwin.c
Normal file
144
pkg/gpu/stat/gpu_darwin.c
Normal file
@ -0,0 +1,144 @@
|
||||
#include "gpu_darwin.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define IOSERVICE_GPU "IOAccelerator"
|
||||
#define IOSERVICE_PCI "IOPCIDevice"
|
||||
|
||||
void *find_properties(io_registry_entry_t service, int depth, CFStringRef key,
|
||||
CFStringRef dict_key) {
|
||||
CFTypeRef properties = IORegistryEntrySearchCFProperty(
|
||||
service, kIOServicePlane, key, kCFAllocatorDefault,
|
||||
kIORegistryIterateRecursively);
|
||||
|
||||
if (properties) {
|
||||
if (CFGetTypeID(properties) == CFStringGetTypeID()) {
|
||||
CFStringRef cfStr = (CFStringRef)properties;
|
||||
char buffer[1024];
|
||||
CFStringGetCString(cfStr, buffer, sizeof(buffer), kCFStringEncodingUTF8);
|
||||
CFRelease(properties);
|
||||
return strdup(buffer);
|
||||
} else if (CFGetTypeID(properties) == CFDictionaryGetTypeID()) {
|
||||
CFDictionaryRef cfDict = (CFDictionaryRef)properties;
|
||||
CFNumberRef cfValue = (CFNumberRef)CFDictionaryGetValue(cfDict, dict_key);
|
||||
if (cfValue == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
int value;
|
||||
if (!CFNumberGetValue(cfValue, kCFNumberIntType, &value)) {
|
||||
return NULL;
|
||||
}
|
||||
return (void *)(intptr_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char **find_devices(char *key) {
|
||||
io_service_t io_reg_err;
|
||||
io_iterator_t iterator;
|
||||
int capacity = 10;
|
||||
|
||||
char **cards = malloc(capacity * sizeof(char *));
|
||||
if (!cards) {
|
||||
fprintf(stderr, "Memory allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io_reg_err = IOServiceGetMatchingServices(
|
||||
kIOMainPortDefault, IOServiceMatching(IOSERVICE_GPU), &iterator);
|
||||
if (io_reg_err != KERN_SUCCESS) {
|
||||
printf("Error getting GPU entry\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
io_object_t service;
|
||||
int index = 0;
|
||||
while ((service = IOIteratorNext(iterator)) != MACH_PORT_NULL) {
|
||||
CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorDefault, key,
|
||||
kCFStringEncodingUTF8);
|
||||
char *result = find_properties(service, 0, cfStr, CFSTR(""));
|
||||
CFRelease(cfStr);
|
||||
IOObjectRelease(service);
|
||||
|
||||
if (result != NULL) {
|
||||
if (index >= capacity) {
|
||||
capacity += 1;
|
||||
char **new_cards = (char **)realloc(cards, capacity * sizeof(char *));
|
||||
if (!new_cards) {
|
||||
fprintf(stderr, "Memory reallocation failed\n");
|
||||
for (int i = 0; i < index; i++) {
|
||||
free(cards[i]);
|
||||
}
|
||||
free(cards);
|
||||
free(result);
|
||||
return NULL;
|
||||
}
|
||||
cards = new_cards;
|
||||
}
|
||||
cards[index] = result;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (result == NULL && strcmp(key, "model") == 0) {
|
||||
IOObjectRelease(iterator);
|
||||
|
||||
io_reg_err = IOServiceGetMatchingServices(
|
||||
kIOMainPortDefault, IOServiceMatching(IOSERVICE_PCI), &iterator);
|
||||
if (io_reg_err != KERN_SUCCESS) {
|
||||
printf("Error getting PCI entry\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
IOObjectRelease(iterator);
|
||||
|
||||
char **result_cards = (char **)realloc(cards, sizeof(char *) * (index + 1));
|
||||
if (!result_cards) {
|
||||
fprintf(stderr, "Memory reallocation failed\n");
|
||||
for (int i = 0; i < index; i++) {
|
||||
free(cards[i]);
|
||||
}
|
||||
free(cards);
|
||||
return NULL;
|
||||
}
|
||||
result_cards[index] = NULL;
|
||||
|
||||
return result_cards;
|
||||
}
|
||||
|
||||
int find_utilization(char *key, char *dict_key) {
|
||||
void *result_ptr;
|
||||
io_service_t io_reg_err;
|
||||
io_iterator_t iterator;
|
||||
|
||||
io_reg_err = IOServiceGetMatchingServices(
|
||||
kIOMainPortDefault, IOServiceMatching(IOSERVICE_GPU), &iterator);
|
||||
if (io_reg_err != KERN_SUCCESS) {
|
||||
printf("Error getting GPU entry\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
io_object_t service = IOIteratorNext(iterator);
|
||||
if (service != MACH_PORT_NULL) {
|
||||
CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorDefault, key,
|
||||
kCFStringEncodingUTF8);
|
||||
CFStringRef cfDictStr = CFStringCreateWithCString(
|
||||
kCFAllocatorDefault, dict_key, kCFStringEncodingUTF8);
|
||||
result_ptr = find_properties(service, 0, cfStr, cfDictStr);
|
||||
CFRelease(cfStr);
|
||||
CFRelease(cfDictStr);
|
||||
}
|
||||
|
||||
IOObjectRelease(service);
|
||||
IOObjectRelease(iterator);
|
||||
|
||||
if (result_ptr == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)(intptr_t)result_ptr;
|
||||
}
|
15
pkg/gpu/stat/gpu_darwin.h
Normal file
15
pkg/gpu/stat/gpu_darwin.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __SMC_H__
|
||||
#define __SMC_H__ 1
|
||||
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#if (defined __MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 120000)
|
||||
#define kIOMainPortDefault kIOMasterPortDefault
|
||||
#endif
|
||||
|
||||
void *find_properties(io_registry_entry_t, int, CFStringRef, CFStringRef);
|
||||
char **find_devices(char *);
|
||||
int find_utilization(char *, char *);
|
||||
|
||||
#endif
|
@ -1,4 +1,4 @@
|
||||
//go:build darwin
|
||||
//go:build darwin && !cgo
|
||||
|
||||
package stat
|
||||
|
||||
|
25
pkg/gpu/stat/stat_darwin_cgo.go
Normal file
25
pkg/gpu/stat/stat_darwin_cgo.go
Normal file
@ -0,0 +1,25 @@
|
||||
//go:build darwin && cgo
|
||||
|
||||
package stat
|
||||
|
||||
// #cgo LDFLAGS: -framework IOKit -framework CoreFoundation
|
||||
// #include "gpu_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func extractGPUStat(key *C.char, dict_key *C.char) (int, error) {
|
||||
utilization := C.find_utilization(key, dict_key)
|
||||
return int(utilization), nil
|
||||
}
|
||||
|
||||
func GetGPUStat() (float64, error) {
|
||||
key := C.CString("PerformanceStatistics")
|
||||
dict_key := C.CString("Device Utilization %")
|
||||
defer C.free(unsafe.Pointer(key))
|
||||
defer C.free(unsafe.Pointer(dict_key))
|
||||
|
||||
gs, _ := extractGPUStat(key, dict_key)
|
||||
return float64(gs), nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user