node-gpuinfo 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +336 -0
- package/binding.gyp +69 -0
- package/build/Release/gpu.exp +0 -0
- package/build/Release/gpu.lib +0 -0
- package/build/Release/gpu.node +0 -0
- package/build/Release/gpu.pdb +0 -0
- package/build/binding.sln +19 -0
- package/build/gpu.vcxproj +175 -0
- package/build/gpu.vcxproj.filters +169 -0
- package/example.js +69 -0
- package/index.js +33 -0
- package/package.json +68 -0
- package/src/binding.cpp +201 -0
- package/src/gpu_info.c +130 -0
- package/src/gpu_info.h +86 -0
- package/src/includes/adlx/ADLX.h +367 -0
- package/src/includes/adlx/ADLXDefines.h +1345 -0
- package/src/includes/adlx/ADLXHelper/ADLXHelper.c +175 -0
- package/src/includes/adlx/ADLXHelper/ADLXHelper.h +245 -0
- package/src/includes/adlx/ADLXHelper/WinAPIS.c +64 -0
- package/src/includes/adlx/ADLXStructures.h +206 -0
- package/src/includes/adlx/ADLXVersion.h +18 -0
- package/src/includes/adlx/I3DSettings.h +3476 -0
- package/src/includes/adlx/I3DSettings1.h +292 -0
- package/src/includes/adlx/I3DSettings2.h +317 -0
- package/src/includes/adlx/IApplications.h +397 -0
- package/src/includes/adlx/IChangedEvent.h +71 -0
- package/src/includes/adlx/ICollections.h +325 -0
- package/src/includes/adlx/IDesktops.h +918 -0
- package/src/includes/adlx/IDisplay3DLUT.h +663 -0
- package/src/includes/adlx/IDisplayGamma.h +683 -0
- package/src/includes/adlx/IDisplayGamut.h +760 -0
- package/src/includes/adlx/IDisplaySettings.h +3476 -0
- package/src/includes/adlx/IDisplays.h +2676 -0
- package/src/includes/adlx/IDisplays1.h +191 -0
- package/src/includes/adlx/IDisplays2.h +188 -0
- package/src/includes/adlx/IDisplays3.h +256 -0
- package/src/includes/adlx/IGPUAutoTuning.h +460 -0
- package/src/includes/adlx/IGPUManualFanTuning.h +1007 -0
- package/src/includes/adlx/IGPUManualGFXTuning.h +607 -0
- package/src/includes/adlx/IGPUManualPowerTuning.h +340 -0
- package/src/includes/adlx/IGPUManualVRAMTuning.h +576 -0
- package/src/includes/adlx/IGPUPresetTuning.h +469 -0
- package/src/includes/adlx/IGPUTuning.h +1239 -0
- package/src/includes/adlx/IGPUTuning1.h +197 -0
- package/src/includes/adlx/II2C.h +198 -0
- package/src/includes/adlx/ILog.h +72 -0
- package/src/includes/adlx/IMultiMedia.h +578 -0
- package/src/includes/adlx/IPerformanceMonitoring.h +2520 -0
- package/src/includes/adlx/IPerformanceMonitoring1.h +134 -0
- package/src/includes/adlx/IPerformanceMonitoring2.h +341 -0
- package/src/includes/adlx/IPerformanceMonitoring3.h +199 -0
- package/src/includes/adlx/IPowerTuning.h +473 -0
- package/src/includes/adlx/IPowerTuning1.h +515 -0
- package/src/includes/adlx/ISmartAccessMemory.h +114 -0
- package/src/includes/adlx/ISystem.h +1557 -0
- package/src/includes/adlx/ISystem1.h +237 -0
- package/src/includes/adlx/ISystem2.h +643 -0
- package/src/linux/amd_linux.c +269 -0
- package/src/linux/intel_linux.c +20 -0
- package/src/linux/nvidia_linux.c +257 -0
- package/src/macos/amd_mac.c +131 -0
- package/src/macos/intel_mac.c +131 -0
- package/src/macos/nvidia_mac.c +21 -0
- package/src/vendor/amd.c +37 -0
- package/src/vendor/intel.c +37 -0
- package/src/vendor/nvidia.c +37 -0
- package/src/windows/amd_windows.c +468 -0
- package/src/windows/intel_windows.c +157 -0
- package/src/windows/nvidia_windows.c +252 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
#include "../gpu_info.h"
|
|
2
|
+
#include <stdio.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <dirent.h>
|
|
6
|
+
#include <unistd.h>
|
|
7
|
+
|
|
8
|
+
static int is_amd_device(const char* card_path) {
|
|
9
|
+
char vendor_path[512];
|
|
10
|
+
snprintf(vendor_path, sizeof(vendor_path), "%s/device/vendor", card_path);
|
|
11
|
+
|
|
12
|
+
FILE* f = fopen(vendor_path, "r");
|
|
13
|
+
if (!f) return 0;
|
|
14
|
+
|
|
15
|
+
unsigned int vendor;
|
|
16
|
+
int result = fscanf(f, "%x", &vendor);
|
|
17
|
+
fclose(f);
|
|
18
|
+
|
|
19
|
+
// AMD vendor ID is 0x1002
|
|
20
|
+
return (result == 1 && vendor == 0x1002);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
gpu_error_t amd_linux_get_gpu_count(int32_t* count) {
|
|
24
|
+
if (!count) return GPU_ERROR_INVALID_PARAM;
|
|
25
|
+
|
|
26
|
+
DIR* dir;
|
|
27
|
+
struct dirent* entry;
|
|
28
|
+
int amd_count = 0;
|
|
29
|
+
|
|
30
|
+
dir = opendir("/sys/class/drm");
|
|
31
|
+
if (!dir) {
|
|
32
|
+
*count = 0;
|
|
33
|
+
return GPU_ERROR_API_FAILED;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
while ((entry = readdir(dir)) != NULL) {
|
|
37
|
+
// Look for card* directories
|
|
38
|
+
if (strncmp(entry->d_name, "card", 4) == 0 &&
|
|
39
|
+
strlen(entry->d_name) == 5 &&
|
|
40
|
+
entry->d_name[4] >= '0' && entry->d_name[4] <= '9') {
|
|
41
|
+
|
|
42
|
+
char card_path[512];
|
|
43
|
+
snprintf(card_path, sizeof(card_path), "/sys/class/drm/%s", entry->d_name);
|
|
44
|
+
|
|
45
|
+
if (is_amd_device(card_path)) {
|
|
46
|
+
amd_count++;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
closedir(dir);
|
|
52
|
+
*count = amd_count;
|
|
53
|
+
return GPU_SUCCESS;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static int read_sysfs_string(const char* path, char* buffer, size_t size) {
|
|
57
|
+
FILE* f = fopen(path, "r");
|
|
58
|
+
if (!f) return 0;
|
|
59
|
+
|
|
60
|
+
if (fgets(buffer, size, f)) {
|
|
61
|
+
// Remove trailing newline
|
|
62
|
+
size_t len = strlen(buffer);
|
|
63
|
+
if (len > 0 && buffer[len-1] == '\n') {
|
|
64
|
+
buffer[len-1] = '\0';
|
|
65
|
+
}
|
|
66
|
+
fclose(f);
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fclose(f);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static long read_sysfs_long(const char* path) {
|
|
75
|
+
FILE* f = fopen(path, "r");
|
|
76
|
+
if (!f) return -1;
|
|
77
|
+
|
|
78
|
+
long value;
|
|
79
|
+
int result = fscanf(f, "%ld", &value);
|
|
80
|
+
fclose(f);
|
|
81
|
+
|
|
82
|
+
return (result == 1) ? value : -1;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
gpu_error_t amd_linux_get_gpu_info(int32_t index, gpu_info_t* info) {
|
|
86
|
+
if (!info) return GPU_ERROR_INVALID_PARAM;
|
|
87
|
+
|
|
88
|
+
// Find the correct card device
|
|
89
|
+
DIR* dir = opendir("/sys/class/drm");
|
|
90
|
+
if (!dir) return GPU_ERROR_API_FAILED;
|
|
91
|
+
|
|
92
|
+
struct dirent* entry;
|
|
93
|
+
int current_index = 0;
|
|
94
|
+
char card_path[512] = {0};
|
|
95
|
+
int found = 0;
|
|
96
|
+
|
|
97
|
+
while ((entry = readdir(dir)) != NULL) {
|
|
98
|
+
if (strncmp(entry->d_name, "card", 4) == 0 &&
|
|
99
|
+
strlen(entry->d_name) == 5 &&
|
|
100
|
+
entry->d_name[4] >= '0' && entry->d_name[4] <= '9') {
|
|
101
|
+
|
|
102
|
+
char temp_path[512];
|
|
103
|
+
snprintf(temp_path, sizeof(temp_path), "/sys/class/drm/%s", entry->d_name);
|
|
104
|
+
|
|
105
|
+
if (is_amd_device(temp_path)) {
|
|
106
|
+
if (current_index == index) {
|
|
107
|
+
strncpy(card_path, temp_path, sizeof(card_path) - 1);
|
|
108
|
+
found = 1;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
current_index++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
closedir(dir);
|
|
117
|
+
|
|
118
|
+
if (!found) return GPU_ERROR_INVALID_PARAM;
|
|
119
|
+
|
|
120
|
+
// Initialize structure
|
|
121
|
+
memset(info, 0, sizeof(gpu_info_t));
|
|
122
|
+
info->index = index;
|
|
123
|
+
info->vendor = GPU_VENDOR_AMD;
|
|
124
|
+
|
|
125
|
+
// Read GPU name from device
|
|
126
|
+
char name_path[512];
|
|
127
|
+
snprintf(name_path, sizeof(name_path), "%s/device/product_name", card_path);
|
|
128
|
+
if (!read_sysfs_string(name_path, info->name, sizeof(info->name))) {
|
|
129
|
+
// Fallback: try model or just use generic name
|
|
130
|
+
snprintf(name_path, sizeof(name_path), "%s/device/model", card_path);
|
|
131
|
+
if (!read_sysfs_string(name_path, info->name, sizeof(info->name))) {
|
|
132
|
+
snprintf(info->name, sizeof(info->name), "AMD GPU %d", index);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Read device ID for UUID
|
|
137
|
+
char device_path[512];
|
|
138
|
+
snprintf(device_path, sizeof(device_path), "%s/device/device", card_path);
|
|
139
|
+
unsigned int device_id = 0;
|
|
140
|
+
FILE* f = fopen(device_path, "r");
|
|
141
|
+
if (f) {
|
|
142
|
+
fscanf(f, "%x", &device_id);
|
|
143
|
+
fclose(f);
|
|
144
|
+
}
|
|
145
|
+
snprintf(info->uuid, sizeof(info->uuid), "AMD-Linux-0x%04X-%d", device_id, index);
|
|
146
|
+
|
|
147
|
+
// Read PCI bus ID
|
|
148
|
+
char pci_path[512];
|
|
149
|
+
snprintf(pci_path, sizeof(pci_path), "%s/device/uevent", card_path);
|
|
150
|
+
f = fopen(pci_path, "r");
|
|
151
|
+
if (f) {
|
|
152
|
+
char line[256];
|
|
153
|
+
while (fgets(line, sizeof(line), f)) {
|
|
154
|
+
if (strncmp(line, "PCI_SLOT_NAME=", 14) == 0) {
|
|
155
|
+
strncpy(info->pci_bus_id, line + 14, sizeof(info->pci_bus_id) - 1);
|
|
156
|
+
// Remove newline
|
|
157
|
+
size_t len = strlen(info->pci_bus_id);
|
|
158
|
+
if (len > 0 && info->pci_bus_id[len-1] == '\n') {
|
|
159
|
+
info->pci_bus_id[len-1] = '\0';
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
fclose(f);
|
|
165
|
+
}
|
|
166
|
+
if (info->pci_bus_id[0] == '\0') {
|
|
167
|
+
snprintf(info->pci_bus_id, sizeof(info->pci_bus_id), "PCI:%d", index);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Read memory information (in bytes, convert to MB)
|
|
171
|
+
char mem_path[512];
|
|
172
|
+
snprintf(mem_path, sizeof(mem_path), "%s/device/mem_info_vram_total", card_path);
|
|
173
|
+
long mem_total = read_sysfs_long(mem_path);
|
|
174
|
+
if (mem_total > 0) {
|
|
175
|
+
info->memory_total = (int32_t)(mem_total / (1024 * 1024));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
snprintf(mem_path, sizeof(mem_path), "%s/device/mem_info_vram_used", card_path);
|
|
179
|
+
long mem_used = read_sysfs_long(mem_path);
|
|
180
|
+
if (mem_used > 0) {
|
|
181
|
+
info->memory_used = (int32_t)(mem_used / (1024 * 1024));
|
|
182
|
+
if (info->memory_total > 0) {
|
|
183
|
+
info->memory_free = info->memory_total - info->memory_used;
|
|
184
|
+
info->memory_utilization = (float)info->memory_used / info->memory_total * 100.0f;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Read GPU utilization
|
|
189
|
+
char util_path[512];
|
|
190
|
+
snprintf(util_path, sizeof(util_path), "%s/device/gpu_busy_percent", card_path);
|
|
191
|
+
long gpu_util = read_sysfs_long(util_path);
|
|
192
|
+
if (gpu_util >= 0) {
|
|
193
|
+
info->gpu_utilization = (float)gpu_util;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Read temperature (in millidegrees, convert to Celsius)
|
|
197
|
+
char temp_path[512];
|
|
198
|
+
snprintf(temp_path, sizeof(temp_path), "%s/device/hwmon/hwmon*/temp1_input", card_path);
|
|
199
|
+
// Note: This glob pattern won't work directly, we'd need to enumerate hwmon directories
|
|
200
|
+
// For now, try hwmon0 and hwmon1
|
|
201
|
+
for (int hwmon = 0; hwmon < 4; hwmon++) {
|
|
202
|
+
snprintf(temp_path, sizeof(temp_path), "%s/device/hwmon/hwmon%d/temp1_input", card_path, hwmon);
|
|
203
|
+
long temp = read_sysfs_long(temp_path);
|
|
204
|
+
if (temp > 0) {
|
|
205
|
+
info->temperature = temp / 1000.0f;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Read power usage (in microwatts, convert to watts)
|
|
211
|
+
char power_path[512];
|
|
212
|
+
for (int hwmon = 0; hwmon < 4; hwmon++) {
|
|
213
|
+
snprintf(power_path, sizeof(power_path), "%s/device/hwmon/hwmon%d/power1_average", card_path, hwmon);
|
|
214
|
+
long power = read_sysfs_long(power_path);
|
|
215
|
+
if (power > 0) {
|
|
216
|
+
info->power_usage = power / 1000000.0f;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Read clock speeds (in Hz, convert to MHz)
|
|
222
|
+
char clock_path[512];
|
|
223
|
+
snprintf(clock_path, sizeof(clock_path), "%s/device/pp_dpm_sclk", card_path);
|
|
224
|
+
f = fopen(clock_path, "r");
|
|
225
|
+
if (f) {
|
|
226
|
+
char line[256];
|
|
227
|
+
// Find the line with asterisk (current clock)
|
|
228
|
+
while (fgets(line, sizeof(line), f)) {
|
|
229
|
+
if (strchr(line, '*')) {
|
|
230
|
+
unsigned int clock_mhz;
|
|
231
|
+
if (sscanf(line, "%*d: %uMhz", &clock_mhz) == 1) {
|
|
232
|
+
info->core_clock = clock_mhz;
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
fclose(f);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
snprintf(clock_path, sizeof(clock_path), "%s/device/pp_dpm_mclk", card_path);
|
|
241
|
+
f = fopen(clock_path, "r");
|
|
242
|
+
if (f) {
|
|
243
|
+
char line[256];
|
|
244
|
+
while (fgets(line, sizeof(line), f)) {
|
|
245
|
+
if (strchr(line, '*')) {
|
|
246
|
+
unsigned int clock_mhz;
|
|
247
|
+
if (sscanf(line, "%*d: %uMhz", &clock_mhz) == 1) {
|
|
248
|
+
info->memory_clock = clock_mhz;
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
fclose(f);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Read fan speed (percentage)
|
|
257
|
+
char fan_path[512];
|
|
258
|
+
for (int hwmon = 0; hwmon < 4; hwmon++) {
|
|
259
|
+
snprintf(fan_path, sizeof(fan_path), "%s/device/hwmon/hwmon%d/pwm1", card_path, hwmon);
|
|
260
|
+
long fan_pwm = read_sysfs_long(fan_path);
|
|
261
|
+
if (fan_pwm >= 0) {
|
|
262
|
+
// PWM is typically 0-255
|
|
263
|
+
info->fan_speed = (fan_pwm / 255.0f) * 100.0f;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return GPU_SUCCESS;
|
|
269
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#include "../gpu_info.h"
|
|
2
|
+
#include <stdio.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
|
|
5
|
+
// Intel GPU detection on Linux is not yet implemented
|
|
6
|
+
// Would require i915 sysfs interface or similar
|
|
7
|
+
|
|
8
|
+
gpu_error_t intel_linux_get_gpu_count(int32_t* count) {
|
|
9
|
+
if (!count) return GPU_ERROR_INVALID_PARAM;
|
|
10
|
+
|
|
11
|
+
// Not yet implemented
|
|
12
|
+
*count = 0;
|
|
13
|
+
return GPU_SUCCESS;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
gpu_error_t intel_linux_get_gpu_info(int32_t index, gpu_info_t* info) {
|
|
17
|
+
if (!info) return GPU_ERROR_INVALID_PARAM;
|
|
18
|
+
|
|
19
|
+
return GPU_ERROR_NOT_SUPPORTED;
|
|
20
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#include "../gpu_info.h"
|
|
2
|
+
#include <dlfcn.h>
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <string.h>
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
|
|
7
|
+
static void* nvml_library = NULL;
|
|
8
|
+
static int nvml_initialized = 0;
|
|
9
|
+
|
|
10
|
+
// NVML structures (same as Windows)
|
|
11
|
+
typedef struct {
|
|
12
|
+
unsigned long long total;
|
|
13
|
+
unsigned long long free;
|
|
14
|
+
unsigned long long used;
|
|
15
|
+
} nvmlMemory_t;
|
|
16
|
+
|
|
17
|
+
typedef struct {
|
|
18
|
+
unsigned int gpu;
|
|
19
|
+
unsigned int memory;
|
|
20
|
+
} nvmlUtilization_t;
|
|
21
|
+
|
|
22
|
+
// PCI info structure (same as Windows)
|
|
23
|
+
typedef struct {
|
|
24
|
+
char busId[16]; // PCI bus ID in format: "0000:00:00.0"
|
|
25
|
+
unsigned int domain;
|
|
26
|
+
unsigned int bus;
|
|
27
|
+
unsigned int device;
|
|
28
|
+
unsigned int pciDeviceId;
|
|
29
|
+
unsigned int pciSubSystemId;
|
|
30
|
+
} nvmlPciInfo_t;
|
|
31
|
+
|
|
32
|
+
// Function pointers (same as Windows)
|
|
33
|
+
static int (*nvmlInit_v2)(void) = NULL;
|
|
34
|
+
static int (*nvmlShutdown)(void) = NULL;
|
|
35
|
+
static int (*nvmlDeviceGetCount_v2)(unsigned int*) = NULL;
|
|
36
|
+
static int (*nvmlDeviceGetHandleByIndex)(unsigned int, void**) = NULL;
|
|
37
|
+
static int (*nvmlDeviceGetName)(void*, char*, unsigned int) = NULL;
|
|
38
|
+
static int (*nvmlDeviceGetUUID)(void*, char*, unsigned int) = NULL;
|
|
39
|
+
static int (*nvmlDeviceGetMemoryInfo)(void*, nvmlMemory_t*) = NULL;
|
|
40
|
+
static int (*nvmlDeviceGetUtilizationRates)(void*, nvmlUtilization_t*) = NULL;
|
|
41
|
+
static int (*nvmlDeviceGetTemperature)(void*, int, unsigned int*) = NULL;
|
|
42
|
+
static int (*nvmlDeviceGetPowerUsage)(void*, unsigned int*) = NULL;
|
|
43
|
+
static int (*nvmlDeviceGetClockInfo)(void*, int, unsigned int*) = NULL;
|
|
44
|
+
static int (*nvmlDeviceGetFanSpeed)(void*, unsigned int*) = NULL;
|
|
45
|
+
static int (*nvmlDeviceGetPciInfo)(void*, nvmlPciInfo_t*) = NULL;
|
|
46
|
+
|
|
47
|
+
static gpu_error_t load_nvml_linux(void) {
|
|
48
|
+
if (nvml_initialized) return GPU_SUCCESS;
|
|
49
|
+
|
|
50
|
+
// Try to load NVML library from common locations
|
|
51
|
+
nvml_library = dlopen("libnvidia-ml.so", RTLD_LAZY);
|
|
52
|
+
if (!nvml_library) {
|
|
53
|
+
nvml_library = dlopen("libnvidia-ml.so.1", RTLD_LAZY);
|
|
54
|
+
}
|
|
55
|
+
if (!nvml_library) {
|
|
56
|
+
return GPU_ERROR_NOT_SUPPORTED;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Load all NVML functions
|
|
60
|
+
nvmlInit_v2 = (int(*)(void))dlsym(nvml_library, "nvmlInit_v2");
|
|
61
|
+
nvmlShutdown = (int(*)(void))dlsym(nvml_library, "nvmlShutdown");
|
|
62
|
+
nvmlDeviceGetCount_v2 = (int(*)(unsigned int*))dlsym(nvml_library, "nvmlDeviceGetCount_v2");
|
|
63
|
+
nvmlDeviceGetHandleByIndex = (int(*)(unsigned int, void**))dlsym(nvml_library, "nvmlDeviceGetHandleByIndex");
|
|
64
|
+
nvmlDeviceGetName = (int(*)(void*, char*, unsigned int))dlsym(nvml_library, "nvmlDeviceGetName");
|
|
65
|
+
nvmlDeviceGetUUID = (int(*)(void*, char*, unsigned int))dlsym(nvml_library, "nvmlDeviceGetUUID");
|
|
66
|
+
nvmlDeviceGetMemoryInfo = (int(*)(void*, nvmlMemory_t*))dlsym(nvml_library, "nvmlDeviceGetMemoryInfo");
|
|
67
|
+
nvmlDeviceGetUtilizationRates = (int(*)(void*, nvmlUtilization_t*))dlsym(nvml_library, "nvmlDeviceGetUtilizationRates");
|
|
68
|
+
nvmlDeviceGetTemperature = (int(*)(void*, int, unsigned int*))dlsym(nvml_library, "nvmlDeviceGetTemperature");
|
|
69
|
+
nvmlDeviceGetPowerUsage = (int(*)(void*, unsigned int*))dlsym(nvml_library, "nvmlDeviceGetPowerUsage");
|
|
70
|
+
nvmlDeviceGetClockInfo = (int(*)(void*, int, unsigned int*))dlsym(nvml_library, "nvmlDeviceGetClockInfo");
|
|
71
|
+
nvmlDeviceGetFanSpeed = (int(*)(void*, unsigned int*))dlsym(nvml_library, "nvmlDeviceGetFanSpeed");
|
|
72
|
+
nvmlDeviceGetPciInfo = (int(*)(void*, nvmlPciInfo_t*))dlsym(nvml_library, "nvmlDeviceGetPciInfo");
|
|
73
|
+
|
|
74
|
+
// Check for essential functions
|
|
75
|
+
if (!nvmlInit_v2 || !nvmlDeviceGetCount_v2 || !nvmlDeviceGetHandleByIndex) {
|
|
76
|
+
dlclose(nvml_library);
|
|
77
|
+
nvml_library = NULL;
|
|
78
|
+
return GPU_ERROR_NOT_SUPPORTED;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (nvmlInit_v2() != 0) {
|
|
82
|
+
dlclose(nvml_library);
|
|
83
|
+
nvml_library = NULL;
|
|
84
|
+
return GPU_ERROR_NOT_SUPPORTED;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
nvml_initialized = 1;
|
|
88
|
+
return GPU_SUCCESS;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
gpu_error_t nvidia_linux_get_gpu_count(int32_t* count) {
|
|
92
|
+
if (!count) return GPU_ERROR_INVALID_PARAM;
|
|
93
|
+
|
|
94
|
+
gpu_error_t result = load_nvml_linux();
|
|
95
|
+
if (result != GPU_SUCCESS) {
|
|
96
|
+
*count = 0;
|
|
97
|
+
return GPU_SUCCESS;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
unsigned int nv_count = 0;
|
|
101
|
+
if (nvmlDeviceGetCount_v2(&nv_count) != 0) {
|
|
102
|
+
*count = 0;
|
|
103
|
+
return GPU_ERROR_API_FAILED;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
*count = (int32_t)nv_count;
|
|
107
|
+
return GPU_SUCCESS;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
gpu_error_t nvidia_linux_get_gpu_info(int32_t index, gpu_info_t* info) {
|
|
111
|
+
if (!info) return GPU_ERROR_INVALID_PARAM;
|
|
112
|
+
|
|
113
|
+
gpu_error_t result = load_nvml_linux();
|
|
114
|
+
if (result != GPU_SUCCESS) {
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
void* device;
|
|
119
|
+
if (nvmlDeviceGetHandleByIndex((unsigned int)index, &device) != 0) {
|
|
120
|
+
return GPU_ERROR_API_FAILED;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
memset(info, 0, sizeof(gpu_info_t));
|
|
124
|
+
info->index = index;
|
|
125
|
+
info->vendor = GPU_VENDOR_NVIDIA;
|
|
126
|
+
|
|
127
|
+
// Get GPU name
|
|
128
|
+
char name[256];
|
|
129
|
+
if (nvmlDeviceGetName && nvmlDeviceGetName(device, name, sizeof(name)) == 0) {
|
|
130
|
+
strncpy(info->name, name, sizeof(info->name) - 1);
|
|
131
|
+
} else {
|
|
132
|
+
strncpy(info->name, "NVIDIA GPU", sizeof(info->name) - 1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Get UUID
|
|
136
|
+
char uuid[64];
|
|
137
|
+
if (nvmlDeviceGetUUID && nvmlDeviceGetUUID(device, uuid, sizeof(uuid)) == 0) {
|
|
138
|
+
strncpy(info->uuid, uuid, sizeof(info->uuid) - 1);
|
|
139
|
+
} else {
|
|
140
|
+
snprintf(info->uuid, sizeof(info->uuid), "NVIDIA-%d", index);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Get PCI bus information
|
|
144
|
+
if (nvmlDeviceGetPciInfo) {
|
|
145
|
+
nvmlPciInfo_t pciInfo;
|
|
146
|
+
if (nvmlDeviceGetPciInfo(device, &pciInfo) == 0) {
|
|
147
|
+
// Use the actual PCI bus ID from NVML
|
|
148
|
+
strncpy(info->pci_bus_id, pciInfo.busId, sizeof(info->pci_bus_id) - 1);
|
|
149
|
+
|
|
150
|
+
// Debug output to see all PCI information (optional)
|
|
151
|
+
/*printf("GPU %d PCI Info:\n", index);
|
|
152
|
+
printf(" Bus ID: %s\n", pciInfo.busId);
|
|
153
|
+
printf(" Domain: 0x%04X\n", pciInfo.domain);
|
|
154
|
+
printf(" Bus: 0x%02X\n", pciInfo.bus);
|
|
155
|
+
printf(" Device: 0x%02X\n", pciInfo.device);
|
|
156
|
+
printf(" Device ID: 0x%04X\n", pciInfo.pciDeviceId);
|
|
157
|
+
printf(" Subsystem ID: 0x%04X\n", pciInfo.pciSubSystemId);*/
|
|
158
|
+
} else {
|
|
159
|
+
// Fallback: generate from index
|
|
160
|
+
snprintf(info->pci_bus_id, sizeof(info->pci_bus_id), "PCI:%d", index);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
// Fallback: generate from index
|
|
164
|
+
snprintf(info->pci_bus_id, sizeof(info->pci_bus_id), "PCI:%d", index);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Get memory info
|
|
168
|
+
nvmlMemory_t memory;
|
|
169
|
+
if (nvmlDeviceGetMemoryInfo && nvmlDeviceGetMemoryInfo(device, &memory) == 0) {
|
|
170
|
+
info->memory_total = memory.total / (1024 * 1024); // Convert to MB
|
|
171
|
+
info->memory_used = memory.used / (1024 * 1024);
|
|
172
|
+
info->memory_free = memory.free / (1024 * 1024);
|
|
173
|
+
|
|
174
|
+
// Calculate memory capacity utilization (for reference)
|
|
175
|
+
float capacity_utilization = 0.0f;
|
|
176
|
+
if (memory.total > 0) {
|
|
177
|
+
capacity_utilization = (float)memory.used / memory.total * 100.0f;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Debug output (optional)
|
|
181
|
+
/*printf("GPU %d Memory: %llu/%llu MB (%.1f%% capacity)\n",
|
|
182
|
+
index, memory.used / (1024 * 1024), memory.total / (1024 * 1024),
|
|
183
|
+
capacity_utilization);*/
|
|
184
|
+
} else {
|
|
185
|
+
// Fallback values
|
|
186
|
+
info->memory_total = 8 * 1024;
|
|
187
|
+
info->memory_used = 0;
|
|
188
|
+
info->memory_free = 8 * 1024;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Get utilization rates
|
|
192
|
+
nvmlUtilization_t utilization;
|
|
193
|
+
if (nvmlDeviceGetUtilizationRates && nvmlDeviceGetUtilizationRates(device, &utilization) == 0) {
|
|
194
|
+
info->gpu_utilization = (float)utilization.gpu;
|
|
195
|
+
info->memory_utilization = (float)utilization.memory; // Memory bandwidth utilization
|
|
196
|
+
|
|
197
|
+
// Debug output (optional)
|
|
198
|
+
/*printf("GPU %d Utilization: GPU=%u%%, Memory=%u%%\n",
|
|
199
|
+
index, utilization.gpu, utilization.memory);*/
|
|
200
|
+
} else {
|
|
201
|
+
info->gpu_utilization = 0.0f;
|
|
202
|
+
info->memory_utilization = 0.0f;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Get temperature (GPU core)
|
|
206
|
+
unsigned int temperature;
|
|
207
|
+
if (nvmlDeviceGetTemperature && nvmlDeviceGetTemperature(device, 0, &temperature) == 0) {
|
|
208
|
+
info->temperature = (float)temperature;
|
|
209
|
+
} else {
|
|
210
|
+
info->temperature = 0.0f;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Get power usage (in milliwatts)
|
|
214
|
+
unsigned int power;
|
|
215
|
+
if (nvmlDeviceGetPowerUsage && nvmlDeviceGetPowerUsage(device, &power) == 0) {
|
|
216
|
+
info->power_usage = (float)power / 1000.0f; // Convert to watts
|
|
217
|
+
} else {
|
|
218
|
+
info->power_usage = 0.0f;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Get core clock (graphics clock)
|
|
222
|
+
unsigned int clock;
|
|
223
|
+
if (nvmlDeviceGetClockInfo && nvmlDeviceGetClockInfo(device, 0, &clock) == 0) {
|
|
224
|
+
info->core_clock = clock;
|
|
225
|
+
} else {
|
|
226
|
+
info->core_clock = 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Get memory clock
|
|
230
|
+
if (nvmlDeviceGetClockInfo && nvmlDeviceGetClockInfo(device, 1, &clock) == 0) {
|
|
231
|
+
info->memory_clock = clock;
|
|
232
|
+
} else {
|
|
233
|
+
info->memory_clock = 0;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Get fan speed
|
|
237
|
+
unsigned int fan_speed;
|
|
238
|
+
if (nvmlDeviceGetFanSpeed && nvmlDeviceGetFanSpeed(device, &fan_speed) == 0) {
|
|
239
|
+
info->fan_speed = (float)fan_speed;
|
|
240
|
+
} else {
|
|
241
|
+
info->fan_speed = 0.0f;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return GPU_SUCCESS;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Cleanup function
|
|
248
|
+
void nvidia_linux_cleanup(void) {
|
|
249
|
+
if (nvml_initialized && nvmlShutdown) {
|
|
250
|
+
nvmlShutdown();
|
|
251
|
+
}
|
|
252
|
+
if (nvml_library) {
|
|
253
|
+
dlclose(nvml_library);
|
|
254
|
+
nvml_library = NULL;
|
|
255
|
+
}
|
|
256
|
+
nvml_initialized = 0;
|
|
257
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#include "../gpu_info.h"
|
|
2
|
+
#include <CoreFoundation/CoreFoundation.h>
|
|
3
|
+
#include <IOKit/IOKitLib.h>
|
|
4
|
+
#include <IOKit/graphics/IOGraphicsLib.h>
|
|
5
|
+
#include <stdio.h>
|
|
6
|
+
#include <string.h>
|
|
7
|
+
|
|
8
|
+
gpu_error_t amd_macos_get_gpu_count(int32_t* count) {
|
|
9
|
+
if (!count) return GPU_ERROR_INVALID_PARAM;
|
|
10
|
+
|
|
11
|
+
int amd_count = 0;
|
|
12
|
+
io_iterator_t iterator;
|
|
13
|
+
|
|
14
|
+
// Get list of all GPUs
|
|
15
|
+
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
|
|
16
|
+
IOServiceMatching("IOPCIDevice"),
|
|
17
|
+
&iterator);
|
|
18
|
+
|
|
19
|
+
if (kr != KERN_SUCCESS) {
|
|
20
|
+
*count = 0;
|
|
21
|
+
return GPU_ERROR_API_FAILED;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
io_object_t service;
|
|
25
|
+
while ((service = IOIteratorNext(iterator))) {
|
|
26
|
+
// Check if it's a display device
|
|
27
|
+
CFTypeRef vendorID = IORegistryEntryCreateCFProperty(service, CFSTR("vendor-id"),
|
|
28
|
+
kCFAllocatorDefault, 0);
|
|
29
|
+
|
|
30
|
+
if (vendorID) {
|
|
31
|
+
UInt32 vendor = 0;
|
|
32
|
+
CFDataGetBytes(vendorID, CFRangeMake(0, sizeof(UInt32)), (UInt8*)&vendor);
|
|
33
|
+
CFRelease(vendorID);
|
|
34
|
+
|
|
35
|
+
// AMD vendor ID is 0x1002
|
|
36
|
+
if (vendor == 0x1002) {
|
|
37
|
+
amd_count++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
IOObjectRelease(service);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
IOObjectRelease(iterator);
|
|
45
|
+
*count = amd_count;
|
|
46
|
+
return GPU_SUCCESS;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
gpu_error_t amd_macos_get_gpu_info(int32_t index, gpu_info_t* info) {
|
|
50
|
+
if (!info) return GPU_ERROR_INVALID_PARAM;
|
|
51
|
+
|
|
52
|
+
int current_index = 0;
|
|
53
|
+
io_iterator_t iterator;
|
|
54
|
+
|
|
55
|
+
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
|
|
56
|
+
IOServiceMatching("IOPCIDevice"),
|
|
57
|
+
&iterator);
|
|
58
|
+
|
|
59
|
+
if (kr != KERN_SUCCESS) {
|
|
60
|
+
return GPU_ERROR_API_FAILED;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
io_object_t service;
|
|
64
|
+
int found = 0;
|
|
65
|
+
|
|
66
|
+
while ((service = IOIteratorNext(iterator))) {
|
|
67
|
+
CFTypeRef vendorID = IORegistryEntryCreateCFProperty(service, CFSTR("vendor-id"),
|
|
68
|
+
kCFAllocatorDefault, 0);
|
|
69
|
+
|
|
70
|
+
if (vendorID) {
|
|
71
|
+
UInt32 vendor = 0;
|
|
72
|
+
CFDataGetBytes(vendorID, CFRangeMake(0, sizeof(UInt32)), (UInt8*)&vendor);
|
|
73
|
+
CFRelease(vendorID);
|
|
74
|
+
|
|
75
|
+
if (vendor == 0x1002) { // AMD
|
|
76
|
+
if (current_index == index) {
|
|
77
|
+
found = 1;
|
|
78
|
+
|
|
79
|
+
memset(info, 0, sizeof(gpu_info_t));
|
|
80
|
+
info->index = index;
|
|
81
|
+
info->vendor = GPU_VENDOR_AMD;
|
|
82
|
+
|
|
83
|
+
// Get model name
|
|
84
|
+
CFTypeRef model = IORegistryEntryCreateCFProperty(service, CFSTR("model"),
|
|
85
|
+
kCFAllocatorDefault, 0);
|
|
86
|
+
if (model && CFGetTypeID(model) == CFDataGetTypeID()) {
|
|
87
|
+
const char* modelStr = (const char*)CFDataGetBytePtr(model);
|
|
88
|
+
snprintf(info->name, sizeof(info->name), "%s", modelStr);
|
|
89
|
+
CFRelease(model);
|
|
90
|
+
} else {
|
|
91
|
+
snprintf(info->name, sizeof(info->name), "AMD GPU %d", index);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get device ID for UUID
|
|
95
|
+
CFTypeRef deviceID = IORegistryEntryCreateCFProperty(service, CFSTR("device-id"),
|
|
96
|
+
kCFAllocatorDefault, 0);
|
|
97
|
+
if (deviceID) {
|
|
98
|
+
UInt32 devID = 0;
|
|
99
|
+
CFDataGetBytes(deviceID, CFRangeMake(0, sizeof(UInt32)), (UInt8*)&devID);
|
|
100
|
+
snprintf(info->uuid, sizeof(info->uuid), "AMD-macOS-0x%04X", devID);
|
|
101
|
+
CFRelease(deviceID);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
snprintf(info->pci_bus_id, sizeof(info->pci_bus_id), "PCI:%d", index);
|
|
105
|
+
|
|
106
|
+
// macOS does not provide detailed GPU monitoring APIs
|
|
107
|
+
// Metal framework and IOKit have limited metrics access
|
|
108
|
+
info->memory_total = 0;
|
|
109
|
+
info->memory_used = 0;
|
|
110
|
+
info->memory_free = 0;
|
|
111
|
+
info->gpu_utilization = 0.0f;
|
|
112
|
+
info->memory_utilization = 0.0f;
|
|
113
|
+
info->temperature = 0.0f;
|
|
114
|
+
info->power_usage = 0.0f;
|
|
115
|
+
info->core_clock = 0;
|
|
116
|
+
info->memory_clock = 0;
|
|
117
|
+
info->fan_speed = 0.0f;
|
|
118
|
+
|
|
119
|
+
IOObjectRelease(service);
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
current_index++;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
IOObjectRelease(service);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
IOObjectRelease(iterator);
|
|
130
|
+
return found ? GPU_SUCCESS : GPU_ERROR_API_FAILED;
|
|
131
|
+
}
|