#include "nvml_dynamic.h" #include #include #ifdef _WIN32 #include #else #include #endif // NVML type definitions (from nvml.h) typedef int nvmlReturn_t; typedef void* nvmlDevice_t; typedef struct { unsigned int gpu; unsigned int memory; } nvmlUtilization_t; typedef struct { unsigned long long total; unsigned long long free; unsigned long long used; } nvmlMemory_t; // Function pointer types typedef nvmlReturn_t (*nvmlInit_v2_fn)(void); typedef nvmlReturn_t (*nvmlShutdown_fn)(void); typedef nvmlReturn_t (*nvmlSystemGetDriverVersion_fn)(char*, unsigned int); typedef nvmlReturn_t (*nvmlDeviceGetCount_fn)(unsigned int*); typedef nvmlReturn_t (*nvmlDeviceGetHandleByIndex_v2_fn)(unsigned int, nvmlDevice_t*); typedef nvmlReturn_t (*nvmlDeviceGetName_fn)(nvmlDevice_t, char*, unsigned int); typedef nvmlReturn_t (*nvmlDeviceGetUtilizationRates_fn)(nvmlDevice_t, nvmlUtilization_t*); typedef nvmlReturn_t (*nvmlDeviceGetMemoryInfo_fn)(nvmlDevice_t, nvmlMemory_t*); typedef nvmlReturn_t (*nvmlDeviceGetTemperature_fn)(nvmlDevice_t, unsigned int, unsigned int*); typedef nvmlReturn_t (*nvmlDeviceGetPowerUsage_fn)(nvmlDevice_t, unsigned int*); typedef nvmlReturn_t (*nvmlDeviceGetClockInfo_fn)(nvmlDevice_t, unsigned int, unsigned int*); typedef nvmlReturn_t (*nvmlDeviceGetPcieThroughput_fn)(nvmlDevice_t, unsigned int, unsigned int*); typedef nvmlReturn_t (*nvmlDeviceGetUUID_fn)(nvmlDevice_t, char*, unsigned int); typedef nvmlReturn_t (*nvmlDeviceGetVbiosVersion_fn)(nvmlDevice_t, char*, unsigned int); // NVML constants #define NVML_SUCCESS 0 #define NVML_TEMPERATURE_GPU 0 #define NVML_CLOCK_SM 0 #define NVML_CLOCK_MEM 1 #define NVML_PCIE_UTIL_TX_BYTES 0 #define NVML_PCIE_UTIL_RX_BYTES 1 struct nvml_dynamic { void* handle; char last_error[256]; int available; // Function pointers nvmlInit_v2_fn init; nvmlShutdown_fn shutdown; nvmlSystemGetDriverVersion_fn get_driver_version; nvmlDeviceGetCount_fn get_count; nvmlDeviceGetHandleByIndex_v2_fn get_handle_by_index; nvmlDeviceGetName_fn get_name; nvmlDeviceGetUtilizationRates_fn get_utilization; nvmlDeviceGetMemoryInfo_fn get_memory; nvmlDeviceGetTemperature_fn get_temperature; nvmlDeviceGetPowerUsage_fn get_power_usage; nvmlDeviceGetClockInfo_fn get_clock; nvmlDeviceGetUUID_fn get_uuid; nvmlDeviceGetVbiosVersion_fn get_vbios; }; static void set_error(nvml_dynamic_t* nvml, const char* msg) { if (nvml) { strncpy(nvml->last_error, msg, sizeof(nvml->last_error) - 1); nvml->last_error[sizeof(nvml->last_error) - 1] = '\0'; } } #ifdef _WIN32 static void* load_lib(const char* name) { return LoadLibraryA(name); } static void* get_sym(void* handle, const char* name) { return (void*)GetProcAddress((HMODULE)handle, name); } static void close_lib(void* handle) { FreeLibrary((HMODULE)handle); } #else static void* load_lib(const char* name) { return dlopen(name, RTLD_NOW); } static void* get_sym(void* handle, const char* name) { return dlsym(handle, name); } static void close_lib(void* handle) { dlclose(handle); } #endif nvml_dynamic_t* nvml_load(void) { nvml_dynamic_t* nvml = (nvml_dynamic_t*)calloc(1, sizeof(nvml_dynamic_t)); if (!nvml) return NULL; // Try to load NVML library #ifdef _WIN32 nvml->handle = load_lib("nvml.dll"); if (!nvml->handle) { nvml->handle = load_lib("C:\\Windows\\System32\\nvml.dll"); } #else nvml->handle = load_lib("libnvidia-ml.so.1"); if (!nvml->handle) { nvml->handle = load_lib("libnvidia-ml.so"); } #endif if (!nvml->handle) { set_error(nvml, "NVML library not found - NVIDIA driver may not be installed"); nvml->available = 0; return nvml; } // Load function pointers nvml->init = (nvmlInit_v2_fn)get_sym(nvml->handle, "nvmlInit_v2"); nvml->shutdown = (nvmlShutdown_fn)get_sym(nvml->handle, "nvmlShutdown"); nvml->get_driver_version = (nvmlSystemGetDriverVersion_fn)get_sym(nvml->handle, "nvmlSystemGetDriverVersion"); nvml->get_count = (nvmlDeviceGetCount_fn)get_sym(nvml->handle, "nvmlDeviceGetCount"); nvml->get_handle_by_index = (nvmlDeviceGetHandleByIndex_v2_fn)get_sym(nvml->handle, "nvmlDeviceGetHandleByIndex_v2"); nvml->get_name = (nvmlDeviceGetName_fn)get_sym(nvml->handle, "nvmlDeviceGetName"); nvml->get_utilization = (nvmlDeviceGetUtilizationRates_fn)get_sym(nvml->handle, "nvmlDeviceGetUtilizationRates"); nvml->get_memory = (nvmlDeviceGetMemoryInfo_fn)get_sym(nvml->handle, "nvmlDeviceGetMemoryInfo"); nvml->get_temperature = (nvmlDeviceGetTemperature_fn)get_sym(nvml->handle, "nvmlDeviceGetTemperature"); nvml->get_power_usage = (nvmlDeviceGetPowerUsage_fn)get_sym(nvml->handle, "nvmlDeviceGetPowerUsage"); nvml->get_clock = (nvmlDeviceGetClockInfo_fn)get_sym(nvml->handle, "nvmlDeviceGetClockInfo"); nvml->get_uuid = (nvmlDeviceGetUUID_fn)get_sym(nvml->handle, "nvmlDeviceGetUUID"); nvml->get_vbios = (nvmlDeviceGetVbiosVersion_fn)get_sym(nvml->handle, "nvmlDeviceGetVbiosVersion"); // Check required functions if (!nvml->init || !nvml->shutdown || !nvml->get_count || !nvml->get_handle_by_index) { set_error(nvml, "Failed to load required NVML functions"); close_lib(nvml->handle); nvml->handle = NULL; nvml->available = 0; return nvml; } // Initialize NVML nvmlReturn_t result = nvml->init(); if (result != NVML_SUCCESS) { set_error(nvml, "Failed to initialize NVML"); close_lib(nvml->handle); nvml->handle = NULL; nvml->available = 0; return nvml; } nvml->available = 1; return nvml; } void nvml_unload(nvml_dynamic_t* nvml) { if (!nvml) return; if (nvml->handle) { if (nvml->shutdown) { nvml->shutdown(); } close_lib(nvml->handle); } free(nvml); } int nvml_is_available(const nvml_dynamic_t* nvml) { return nvml ? nvml->available : 0; } const char* nvml_last_error(const nvml_dynamic_t* nvml) { return nvml ? nvml->last_error : "NULL nvml handle"; } int nvml_get_gpu_count(nvml_dynamic_t* nvml) { if (!nvml || !nvml->available || !nvml->get_count) { return -1; } unsigned int count = 0; nvmlReturn_t result = nvml->get_count(&count); if (result != NVML_SUCCESS) { set_error(nvml, "Failed to get GPU count"); return -1; } return (int)count; } int nvml_get_gpu_info(nvml_dynamic_t* nvml, uint32_t index, gpu_info_t* info) { if (!nvml || !nvml->available || !info) { return -1; } memset(info, 0, sizeof(*info)); info->index = index; nvmlDevice_t device; nvmlReturn_t result = nvml->get_handle_by_index(index, &device); if (result != NVML_SUCCESS) { set_error(nvml, "Failed to get device handle"); return -1; } // Get name if (nvml->get_name) { nvml->get_name(device, info->name, sizeof(info->name)); } // Get utilization if (nvml->get_utilization) { nvmlUtilization_t util; result = nvml->get_utilization(device, &util); if (result == NVML_SUCCESS) { info->utilization = util.gpu; } } // Get memory if (nvml->get_memory) { nvmlMemory_t mem; result = nvml->get_memory(device, &mem); if (result == NVML_SUCCESS) { info->memory_used = mem.used; info->memory_total = mem.total; } } // Get temperature if (nvml->get_temperature) { unsigned int temp; result = nvml->get_temperature(device, NVML_TEMPERATURE_GPU, &temp); if (result == NVML_SUCCESS) { info->temperature = temp; } } // Get power usage if (nvml->get_power_usage) { unsigned int power; result = nvml->get_power_usage(device, &power); if (result == NVML_SUCCESS) { info->power_draw = power; } } // Get clocks if (nvml->get_clock) { unsigned int clock; result = nvml->get_clock(device, NVML_CLOCK_SM, &clock); if (result == NVML_SUCCESS) { info->clock_sm = clock; } result = nvml->get_clock(device, NVML_CLOCK_MEM, &clock); if (result == NVML_SUCCESS) { info->clock_memory = clock; } } // Get UUID if (nvml->get_uuid) { nvml->get_uuid(device, info->uuid, sizeof(info->uuid)); } // Get VBIOS version if (nvml->get_vbios) { nvml->get_vbios(device, info->vbios_version, sizeof(info->vbios_version)); } return 0; }