This commit is contained in:
Dobromir Popov
2025-09-07 15:03:47 +03:00
parent 00cda24e71
commit 2d2653551b
132 changed files with 34281 additions and 5 deletions

View File

@@ -0,0 +1,21 @@
set(SOURCES
wraphelper.cpp wraphelper.h
wrapnvml.h wrapnvml.cpp
wrapadl.h wrapadl.cpp
wrapamdsysfs.h wrapamdsysfs.cpp
)
add_library(hwmon ${SOURCES})
set(HWMON_LINK_LIBRARIES devcore)
if (UNIX)
list(APPEND HWMON_LINK_LIBRARIES dl)
endif ()
target_link_libraries(hwmon ${HWMON_LINK_LIBRARIES})
target_include_directories(hwmon PRIVATE ..)
if (ETHASHCUDA)
find_package(CUDA REQUIRED)
target_include_directories(hwmon PUBLIC ${CUDA_INCLUDE_DIRS})
endif()

255
zano/libhwmon/wrapadl.cpp Normal file
View File

@@ -0,0 +1,255 @@
/*
* Wrapper for ADL, inspired by wrapnvml from John E. Stone
*
* By Philipp Andreas - github@smurfy.de
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "wrapadl.h"
#include "wraphelper.h"
#if defined(__cplusplus)
extern "C" {
#endif
void* ADL_API_CALL ADL_Main_Memory_Alloc(int iSize)
{
void* lpBuffer = malloc(iSize);
return lpBuffer;
}
wrap_adl_handle* wrap_adl_create()
{
wrap_adl_handle* adlh = nullptr;
#if defined(_WIN32)
/* Windows */
#define libatiadlxx "atiadlxx.dll"
#elif defined(__linux) && (defined(__i386__) || defined(__ARM_ARCH_7A__))
/* 32-bit linux assumed */
#define libatiadlxx "libatiadlxx.so"
#elif defined(__linux)
/* 64-bit linux assumed */
#define libatiadlxx "libatiadlxx.so"
#else
#define libatiadlxx ""
#warning "Unrecognized platform: need ADL DLL path for this platform..."
return nullptr;
#endif
#ifdef _WIN32
char tmp[512];
ExpandEnvironmentStringsA(libatiadlxx, tmp, sizeof(tmp));
#else
char tmp[512] = libatiadlxx;
#endif
void* adl_dll = wrap_dlopen(tmp);
if (adl_dll == nullptr)
{
cwarn << "Failed to obtain all required ADL function pointers";
cwarn << "AMD hardware monitoring disabled";
return nullptr;
}
adlh = (wrap_adl_handle*)calloc(1, sizeof(wrap_adl_handle));
adlh->adl_dll = adl_dll;
adlh->adlMainControlCreate = (wrap_adlReturn_t(*)(ADL_MAIN_MALLOC_CALLBACK, int))wrap_dlsym(
adlh->adl_dll, "ADL_Main_Control_Create");
adlh->adlAdapterNumberOfAdapters =
(wrap_adlReturn_t(*)(int*))wrap_dlsym(adlh->adl_dll, "ADL_Adapter_NumberOfAdapters_Get");
adlh->adlAdapterAdapterInfoGet = (wrap_adlReturn_t(*)(LPAdapterInfo, int))wrap_dlsym(
adlh->adl_dll, "ADL_Adapter_AdapterInfo_Get");
adlh->adlAdapterAdapterIdGet =
(wrap_adlReturn_t(*)(int, int*))wrap_dlsym(adlh->adl_dll, "ADL_Adapter_ID_Get");
adlh->adlOverdrive5TemperatureGet = (wrap_adlReturn_t(*)(int, int, ADLTemperature*))wrap_dlsym(
adlh->adl_dll, "ADL_Overdrive5_Temperature_Get");
adlh->adlOverdrive5FanSpeedGet = (wrap_adlReturn_t(*)(int, int, ADLFanSpeedValue*))wrap_dlsym(
adlh->adl_dll, "ADL_Overdrive5_FanSpeed_Get");
adlh->adlMainControlRefresh =
(wrap_adlReturn_t(*)(void))wrap_dlsym(adlh->adl_dll, "ADL_Main_Control_Refresh");
adlh->adlMainControlDestroy =
(wrap_adlReturn_t(*)(void))wrap_dlsym(adlh->adl_dll, "ADL_Main_Control_Destroy");
adlh->adl2MainControlCreate = (wrap_adlReturn_t(*)(ADL_MAIN_MALLOC_CALLBACK, int,
ADL_CONTEXT_HANDLE*))wrap_dlsym(adlh->adl_dll, "ADL2_Main_Control_Create");
adlh->adl2MainControlDestroy = (wrap_adlReturn_t(*)(ADL_CONTEXT_HANDLE))wrap_dlsym(
adlh->adl_dll, "ADL_Main_Control_Destroy");
adlh->adl2Overdrive6CurrentPowerGet = (wrap_adlReturn_t(*)(ADL_CONTEXT_HANDLE, int, int,
int*))wrap_dlsym(adlh->adl_dll, "ADL2_Overdrive6_CurrentPower_Get");
adlh->adl2MainControlRefresh = (wrap_adlReturn_t(*)(ADL_CONTEXT_HANDLE))wrap_dlsym(
adlh->adl_dll, "ADL2_Main_Control_Refresh");
if (adlh->adlMainControlCreate == nullptr || adlh->adlMainControlDestroy == nullptr ||
adlh->adlMainControlRefresh == nullptr || adlh->adlAdapterNumberOfAdapters == nullptr ||
adlh->adlAdapterAdapterInfoGet == nullptr || adlh->adlAdapterAdapterIdGet == nullptr ||
adlh->adlOverdrive5TemperatureGet == nullptr || adlh->adlOverdrive5FanSpeedGet == nullptr ||
adlh->adl2MainControlCreate == nullptr || adlh->adl2MainControlRefresh == nullptr ||
adlh->adl2MainControlDestroy == nullptr || adlh->adl2Overdrive6CurrentPowerGet == nullptr)
{
cwarn << "Failed to obtain all required ADL function pointers";
cwarn << "AMD hardware monitoring disabled";
wrap_dlclose(adlh->adl_dll);
free(adlh);
return nullptr;
}
adlh->adlMainControlCreate(ADL_Main_Memory_Alloc, 1);
adlh->adlMainControlRefresh();
adlh->context = nullptr;
adlh->adl2MainControlCreate(ADL_Main_Memory_Alloc, 1, &(adlh->context));
adlh->adl2MainControlRefresh(adlh->context);
int logicalGpuCount = 0;
adlh->adlAdapterNumberOfAdapters(&logicalGpuCount);
adlh->phys_logi_device_id = (int*)calloc(logicalGpuCount, sizeof(int));
adlh->adl_gpucount = 0;
int last_adapter = 0;
if (logicalGpuCount > 0)
{
adlh->log_gpucount = logicalGpuCount;
adlh->devs = (LPAdapterInfo)malloc(sizeof(AdapterInfo) * logicalGpuCount);
memset(adlh->devs, '\0', sizeof(AdapterInfo) * logicalGpuCount);
adlh->devs->iSize = sizeof(adlh->devs);
int res = adlh->adlAdapterAdapterInfoGet(adlh->devs, sizeof(AdapterInfo) * logicalGpuCount);
if (res != WRAPADL_OK)
{
cwarn << "Failed to obtain using adlAdapterAdapterInfoGet().";
cwarn << "AMD hardware monitoring disabled";
wrap_dlclose(adlh->adl_dll);
free(adlh);
return nullptr;
}
for (int i = 0; i < logicalGpuCount; i++)
{
int adapterIndex = adlh->devs[i].iAdapterIndex;
int adapterID = 0;
res = adlh->adlAdapterAdapterIdGet(adapterIndex, &adapterID);
if (res != WRAPADL_OK)
{
continue;
}
adlh->phys_logi_device_id[adlh->adl_gpucount] = adapterIndex;
if (adapterID == last_adapter)
{
continue;
}
last_adapter = adapterID;
adlh->adl_gpucount++;
}
}
return adlh;
}
int wrap_adl_destroy(wrap_adl_handle* adlh)
{
adlh->adlMainControlDestroy();
adlh->adl2MainControlDestroy(adlh->context);
wrap_dlclose(adlh->adl_dll);
free(adlh);
return 0;
}
int wrap_adl_get_gpucount(wrap_adl_handle* adlh, int* gpucount)
{
*gpucount = adlh->adl_gpucount;
return 0;
}
int wrap_adl_get_gpu_name(wrap_adl_handle* adlh, int gpuindex, char* namebuf, int bufsize)
{
if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
return -1;
memcpy(namebuf, adlh->devs[adlh->phys_logi_device_id[gpuindex]].strAdapterName, bufsize);
return 0;
}
int wrap_adl_get_gpu_pci_id(wrap_adl_handle* adlh, int gpuindex, char* idbuf, int bufsize)
{
if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
return -1;
char buf[256];
sprintf(buf, "%04x:%02x:%02x",
0, // Is probably 0
adlh->devs[adlh->phys_logi_device_id[gpuindex]].iBusNumber,
adlh->devs[adlh->phys_logi_device_id[gpuindex]].iDeviceNumber);
memcpy(idbuf, buf, bufsize);
return 0;
}
int wrap_adl_get_tempC(wrap_adl_handle* adlh, int gpuindex, unsigned int* tempC)
{
if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
return -1;
ADLTemperature* temperature = new ADLTemperature();
if (adlh->adlOverdrive5TemperatureGet(adlh->phys_logi_device_id[gpuindex], 0, temperature) !=
WRAPADL_OK)
return -1;
*tempC = unsigned(temperature->iTemperature / 1000);
delete temperature;
return 0;
}
int wrap_adl_get_fanpcnt(wrap_adl_handle* adlh, int gpuindex, unsigned int* fanpcnt)
{
if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
return -1;
ADLFanSpeedValue* fan = new ADLFanSpeedValue();
fan->iSpeedType = 1;
if (adlh->adlOverdrive5FanSpeedGet(adlh->phys_logi_device_id[gpuindex], 0, fan) != WRAPADL_OK)
return -1;
*fanpcnt = unsigned(fan->iFanSpeed);
delete fan;
return 0;
}
int wrap_adl_get_power_usage(wrap_adl_handle* adlh, int gpuindex, unsigned int* miliwatts)
{
if (gpuindex < 0 || gpuindex >= adlh->adl_gpucount)
return -1;
int power = 0;
if (adlh->adl2Overdrive6CurrentPowerGet(
adlh->context, adlh->phys_logi_device_id[gpuindex], 0, &power) != WRAPADL_OK)
return -1;
*miliwatts = (unsigned int)(power * 3.90625);
return 0;
}
#if defined(__cplusplus)
}
#endif

156
zano/libhwmon/wrapadl.h Normal file
View File

@@ -0,0 +1,156 @@
/*
* Wrapper for ADL, inspired by wrapnvml from John E. Stone
*
* By Philipp Andreas - github@smurfy.de
* ADL power by Davesmacer
*/
#pragma once
#if defined(__cplusplus)
extern "C" {
#endif
typedef enum wrap_adlReturn_enum { WRAPADL_OK = 0 } wrap_adlReturn_t;
// Some ADL defines and structs from adl sdk
#if defined(__MSC_VER)
#define ADL_API_CALL __cdecl
#elif defined(_WIN32)
#define ADL_API_CALL __stdcall
#else
#define ADL_API_CALL
#endif
typedef void*(ADL_API_CALL* ADL_MAIN_MALLOC_CALLBACK)(int);
/// \brief Handle to ADL client context.
///
/// ADL clients obtain context handle from initial call to \ref ADL2_Main_Control_Create.
/// Clients have to pass the handle to each subsequent ADL call and finally destroy
/// the context with call to \ref ADL2_Main_Control_Destroy
/// \nosubgrouping
typedef void* ADL_CONTEXT_HANDLE;
#define ADL_MAX_PATH 256
typedef struct AdapterInfo
{
/// \ALL_STRUCT_MEM
/// Size of the structure.
int iSize;
/// The ADL index handle. One GPU may be associated with one or two index handles
int iAdapterIndex;
/// The unique device ID associated with this adapter.
char strUDID[ADL_MAX_PATH];
/// The BUS number associated with this adapter.
int iBusNumber;
/// The driver number associated with this adapter.
int iDeviceNumber;
/// The function number.
int iFunctionNumber;
/// The vendor ID associated with this adapter.
int iVendorID;
/// Adapter name.
char strAdapterName[ADL_MAX_PATH];
/// Display name. For example, "\\Display0" for Windows or ":0:0" for Linux.
char strDisplayName[ADL_MAX_PATH];
/// Present or not; 1 if present and 0 if not present.It the logical adapter is present, the
/// display name such as \\.\Display1 can be found from OS
int iPresent;
// @}
#if defined(_WIN32)
/// \WIN_STRUCT_MEM
/// Exist or not; 1 is exist and 0 is not present.
int iExist;
/// Driver registry path.
char strDriverPath[ADL_MAX_PATH];
/// Driver registry path Ext for.
char strDriverPathExt[ADL_MAX_PATH];
/// PNP string from Windows.
char strPNPString[ADL_MAX_PATH];
/// It is generated from EnumDisplayDevices.
int iOSDisplayIndex;
// @}
#endif /* (_WIN32) */
#if defined(LINUX)
/// \LNX_STRUCT_MEM
/// Internal X screen number from GPUMapInfo (DEPRICATED use XScreenInfo)
int iXScreenNum;
/// Internal driver index from GPUMapInfo
int iDrvIndex;
/// \deprecated Internal x config file screen identifier name. Use XScreenInfo instead.
char strXScreenConfigName[ADL_MAX_PATH];
// @}
#endif /* (LINUX) */
} AdapterInfo, *LPAdapterInfo;
typedef struct ADLTemperature
{
/// Must be set to the size of the structure
int iSize;
/// Temperature in millidegrees Celsius.
int iTemperature;
} ADLTemperature;
typedef struct ADLFanSpeedValue
{
/// Must be set to the size of the structure
int iSize;
/// Possible valies: \ref ADL_DL_FANCTRL_SPEED_TYPE_PERCENT or \ref
/// ADL_DL_FANCTRL_SPEED_TYPE_RPM
int iSpeedType;
/// Fan speed value
int iFanSpeed;
/// The only flag for now is: \ref ADL_DL_FANCTRL_FLAG_USER_DEFINED_SPEED
int iFlags;
} ADLFanSpeedValue;
/*
* Handle to hold the function pointers for the entry points we need,
* and the shared library itself.
*/
typedef struct
{
void* adl_dll;
int adl_gpucount;
int log_gpucount;
int* phys_logi_device_id;
LPAdapterInfo devs;
ADL_CONTEXT_HANDLE context;
wrap_adlReturn_t (*adlMainControlCreate)(ADL_MAIN_MALLOC_CALLBACK, int);
wrap_adlReturn_t (*adlAdapterNumberOfAdapters)(int*);
wrap_adlReturn_t (*adlAdapterAdapterInfoGet)(LPAdapterInfo, int);
wrap_adlReturn_t (*adlAdapterAdapterIdGet)(int, int*);
wrap_adlReturn_t (*adlOverdrive5TemperatureGet)(int, int, ADLTemperature*);
wrap_adlReturn_t (*adlOverdrive5FanSpeedGet)(int, int, ADLFanSpeedValue*);
wrap_adlReturn_t (*adlMainControlRefresh)(void);
wrap_adlReturn_t (*adlMainControlDestroy)(void);
wrap_adlReturn_t (*adl2MainControlCreate)(ADL_MAIN_MALLOC_CALLBACK, int, ADL_CONTEXT_HANDLE*);
wrap_adlReturn_t (*adl2MainControlDestroy)(ADL_CONTEXT_HANDLE);
wrap_adlReturn_t (*adl2Overdrive6CurrentPowerGet)(ADL_CONTEXT_HANDLE, int, int, int*);
wrap_adlReturn_t (*adl2MainControlRefresh)(ADL_CONTEXT_HANDLE);
} wrap_adl_handle;
wrap_adl_handle* wrap_adl_create();
int wrap_adl_destroy(wrap_adl_handle* adlh);
int wrap_adl_get_gpucount(wrap_adl_handle* adlh, int* gpucount);
int wrap_adl_get_gpu_name(wrap_adl_handle* adlh, int gpuindex, char* namebuf, int bufsize);
int wrap_adl_get_gpu_pci_id(wrap_adl_handle* adlh, int gpuindex, char* idbuf, int bufsize);
int wrap_adl_get_tempC(wrap_adl_handle* adlh, int gpuindex, unsigned int* tempC);
int wrap_adl_get_fanpcnt(wrap_adl_handle* adlh, int gpuindex, unsigned int* fanpcnt);
int wrap_adl_get_power_usage(wrap_adl_handle* adlh, int gpuindex, unsigned int* milliwatts);
#if defined(__cplusplus)
}
#endif

View File

@@ -0,0 +1,288 @@
/*
* Wrapper for AMD SysFS on linux, using adapted code from amdcovc by matszpk
*
* By Philipp Andreas - github@smurfy.de
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#if defined(__linux)
#include <dirent.h>
#endif
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <algorithm>
#include <climits>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
#include <limits>
#include <regex>
#include <string>
#include "wrapamdsysfs.h"
#include "wraphelper.h"
static bool getFileContentValue(const char* filename, unsigned int& value)
{
value = 0;
std::ifstream ifs(filename, std::ios::binary);
std::string line;
std::getline(ifs, line);
char* p = (char*)line.c_str();
char* p2;
errno = 0;
value = strtoul(p, &p2, 0);
if (errno != 0)
return false;
return (p != p2);
}
wrap_amdsysfs_handle* wrap_amdsysfs_create()
{
wrap_amdsysfs_handle* sysfsh = nullptr;
#if defined(__linux)
namespace fs = boost::filesystem;
std::vector<pciInfo> devices; // Used to collect devices
char dbuf[120];
// Check directory exist
fs::path drm_dir("/sys/class/drm");
if (!fs::exists(drm_dir) || !fs::is_directory(drm_dir))
return nullptr;
// Regex patterns to identify directory elements
std::regex cardPattern("^card[0-9]{1,}$");
std::regex hwmonPattern("^hwmon[0-9]{1,}$");
// Loop directory contents
for (fs::directory_iterator dirEnt(drm_dir); dirEnt != fs::directory_iterator(); ++dirEnt)
{
// Skip non relevant entries
if (!fs::is_directory(dirEnt->path()) ||
!std::regex_match(dirEnt->path().filename().string(), cardPattern))
continue;
std::string devName = dirEnt->path().filename().string();
unsigned int devIndex = std::stoi(devName.substr(4), nullptr, 10);
unsigned int vendorId = 0;
unsigned int hwmonIndex = UINT_MAX;
// Get AMD cards only (vendor 4098)
fs::path vendor_file("/sys/class/drm/" + devName + "/device/vendor");
snprintf(dbuf, 120, "/sys/class/drm/%s/device/vendor", devName.c_str());
if (!fs::exists(vendor_file) || !fs::is_regular_file(vendor_file) ||
!getFileContentValue(dbuf, vendorId) || vendorId != 4098)
continue;
// Check it has dependant hwmon directory
fs::path hwmon_dir("/sys/class/drm/" + devName + "/device/hwmon");
if (!fs::exists(hwmon_dir) || !fs::is_directory(hwmon_dir))
continue;
// Loop subelements in hwmon directory
for (fs::directory_iterator hwmonEnt(hwmon_dir); hwmonEnt != fs::directory_iterator();
++hwmonEnt)
{
// Skip non relevant entries
if (!fs::is_directory(hwmonEnt->path()) ||
!std::regex_match(hwmonEnt->path().filename().string(), hwmonPattern))
continue;
unsigned int v = std::stoi(hwmonEnt->path().filename().string().substr(5), nullptr, 10);
hwmonIndex = std::min(hwmonIndex, v);
}
if (hwmonIndex == UINT_MAX)
continue;
// Detect Pci Id
fs::path uevent_file("/sys/class/drm/" + devName + "/device/uevent");
if (!fs::exists(uevent_file) || !fs::is_regular_file(uevent_file))
continue;
snprintf(dbuf, 120, "/sys/class/drm/card%d/device/uevent", devIndex);
std::ifstream ifs(dbuf, std::ios::binary);
std::string line;
int PciDomain = -1, PciBus = -1, PciDevice = -1, PciFunction = -1;
while (std::getline(ifs, line))
{
if (line.length() > 24 && line.substr(0, 13) == "PCI_SLOT_NAME")
{
std::string pciId = line.substr(14);
std::vector<std::string> pciIdParts;
boost::split(pciIdParts, pciId, [](char c) { return (c == ':' || c == '.'); });
try
{
PciDomain = std::stoi(pciIdParts.at(0), nullptr, 10);
PciBus = std::stoi(pciIdParts.at(1), nullptr, 10);
PciDevice = std::stoi(pciIdParts.at(2), nullptr, 10);
PciFunction = std::stoi(pciIdParts.at(3), nullptr, 10);
}
catch (const std::exception&)
{
PciDomain = PciBus = PciDevice = PciFunction = -1;
}
break;
}
}
// If we got an error skip
if (PciDomain == -1)
continue;
// We got all information needed
// push in the list of collected devices
pciInfo pInfo = pciInfo();
pInfo.DeviceId = devIndex;
pInfo.HwMonId = hwmonIndex;
pInfo.PciDomain = PciDomain;
pInfo.PciBus = PciBus;
pInfo.PciDevice = PciDevice;
devices.push_back(pInfo);
}
// Nothing collected - exit
if (!devices.size())
{
cwarn << "Failed to obtain all required AMD file pointers";
cwarn << "AMD hardware monitoring disabled";
return nullptr;
}
unsigned int gpucount = devices.size();
sysfsh = (wrap_amdsysfs_handle*)calloc(1, sizeof(wrap_amdsysfs_handle));
if (sysfsh == nullptr)
{
cwarn << "Failed allocate memory";
cwarn << "AMD hardware monitoring disabled";
return sysfsh;
}
sysfsh->sysfs_gpucount = gpucount;
sysfsh->sysfs_device_id = (unsigned int*)calloc(gpucount, sizeof(unsigned int));
sysfsh->sysfs_hwmon_id = (unsigned int*)calloc(gpucount, sizeof(unsigned int));
sysfsh->sysfs_pci_domain_id = (unsigned int*)calloc(gpucount, sizeof(unsigned int));
sysfsh->sysfs_pci_bus_id = (unsigned int*)calloc(gpucount, sizeof(unsigned int));
sysfsh->sysfs_pci_device_id = (unsigned int*)calloc(gpucount, sizeof(unsigned int));
gpucount = 0;
for (auto const& device : devices)
{
sysfsh->sysfs_device_id[gpucount] = device.DeviceId;
sysfsh->sysfs_hwmon_id[gpucount] = device.HwMonId;
sysfsh->sysfs_pci_domain_id[gpucount] = device.PciDomain;
sysfsh->sysfs_pci_bus_id[gpucount] = device.PciBus;
sysfsh->sysfs_pci_device_id[gpucount] = device.PciDevice;
gpucount++;
}
#endif
return sysfsh;
}
int wrap_amdsysfs_destroy(wrap_amdsysfs_handle* sysfsh)
{
free(sysfsh);
return 0;
}
int wrap_amdsysfs_get_gpucount(wrap_amdsysfs_handle* sysfsh, int* gpucount)
{
*gpucount = sysfsh->sysfs_gpucount;
return 0;
}
int wrap_amdsysfs_get_tempC(wrap_amdsysfs_handle* sysfsh, int index, unsigned int* tempC)
{
if (index < 0 || index >= sysfsh->sysfs_gpucount)
return -1;
int gpuindex = sysfsh->sysfs_device_id[index];
int hwmonindex = sysfsh->sysfs_hwmon_id[index];
if (hwmonindex < 0)
return -1;
char dbuf[120];
snprintf(
dbuf, 120, "/sys/class/drm/card%d/device/hwmon/hwmon%d/temp1_input", gpuindex, hwmonindex);
unsigned int temp = 0;
getFileContentValue(dbuf, temp);
if (temp > 0)
*tempC = temp / 1000;
return 0;
}
int wrap_amdsysfs_get_fanpcnt(wrap_amdsysfs_handle* sysfsh, int index, unsigned int* fanpcnt)
{
if (index < 0 || index >= sysfsh->sysfs_gpucount)
return -1;
int gpuindex = sysfsh->sysfs_device_id[index];
int hwmonindex = sysfsh->sysfs_hwmon_id[index];
if (hwmonindex < 0)
return -1;
unsigned int pwm = 0, pwmMax = 255, pwmMin = 0;
char dbuf[120];
snprintf(dbuf, 120, "/sys/class/drm/card%d/device/hwmon/hwmon%d/pwm1", gpuindex, hwmonindex);
getFileContentValue(dbuf, pwm);
snprintf(
dbuf, 120, "/sys/class/drm/card%d/device/hwmon/hwmon%d/pwm1_max", gpuindex, hwmonindex);
getFileContentValue(dbuf, pwmMax);
snprintf(
dbuf, 120, "/sys/class/drm/card%d/device/hwmon/hwmon%d/pwm1_min", gpuindex, hwmonindex);
getFileContentValue(dbuf, pwmMin);
*fanpcnt = (unsigned int)(double(pwm - pwmMin) / double(pwmMax - pwmMin) * 100.0);
return 0;
}
int wrap_amdsysfs_get_power_usage(wrap_amdsysfs_handle* sysfsh, int index, unsigned int* milliwatts)
{
try
{
if (index < 0 || index >= sysfsh->sysfs_gpucount)
return -1;
int gpuindex = sysfsh->sysfs_device_id[index];
char dbuf[120];
snprintf(dbuf, 120, "/sys/kernel/debug/dri/%d/amdgpu_pm_info", gpuindex);
std::ifstream ifs(dbuf, std::ios::binary);
std::string line;
while (std::getline(ifs, line))
{
std::smatch sm;
std::regex regex(R"(([\d|\.]+) W \(average GPU\))");
if (std::regex_search(line, sm, regex))
{
if (sm.size() == 2)
{
double watt = atof(sm.str(1).c_str());
*milliwatts = (unsigned int)(watt * 1000);
return 0;
}
}
}
}
catch (const std::exception& ex)
{
cwarn << "Error in amdsysfs_get_power_usage: " << ex.what();
}
return -1;
}

View File

@@ -0,0 +1,40 @@
/*
* Wrapper for AMD SysFS on linux, using adapted code from amdcovc by matszpk
*
* By Philipp Andreas - github@smurfy.de
Reworked and simplified by Andrea Lanfranchi (github @AndreaLanfranchi)
*/
#pragma once
typedef struct
{
int sysfs_gpucount;
unsigned int* sysfs_device_id;
unsigned int* sysfs_hwmon_id;
unsigned int* sysfs_pci_domain_id;
unsigned int* sysfs_pci_bus_id;
unsigned int* sysfs_pci_device_id;
} wrap_amdsysfs_handle;
typedef struct
{
int DeviceId = -1;
int HwMonId = -1;
int PciDomain = -1;
int PciBus = -1;
int PciDevice = -1;
} pciInfo;
wrap_amdsysfs_handle* wrap_amdsysfs_create();
int wrap_amdsysfs_destroy(wrap_amdsysfs_handle* sysfsh);
int wrap_amdsysfs_get_gpucount(wrap_amdsysfs_handle* sysfsh, int* gpucount);
int wrap_amdsysfs_get_tempC(wrap_amdsysfs_handle* sysfsh, int index, unsigned int* tempC);
int wrap_amdsysfs_get_fanpcnt(wrap_amdsysfs_handle* sysfsh, int index, unsigned int* fanpcnt);
int wrap_amdsysfs_get_power_usage(
wrap_amdsysfs_handle* sysfsh, int index, unsigned int* milliwatts);

View File

@@ -0,0 +1,35 @@
/*
* Wrappers to emulate dlopen() on other systems like Windows
*/
#include "wraphelper.h"
#if defined(_WIN32)
void* wrap_dlopen(const char* filename)
{
return (void*)LoadLibrary(filename);
}
void* wrap_dlsym(void* h, const char* sym)
{
return (void*)GetProcAddress((HINSTANCE)h, sym);
}
int wrap_dlclose(void* h)
{
/* FreeLibrary returns non-zero on success */
return (!FreeLibrary((HINSTANCE)h));
}
#else
/* assume we can use dlopen itself... */
void* wrap_dlopen(const char* filename)
{
return dlopen(filename, RTLD_NOW);
}
void* wrap_dlsym(void* h, const char* sym)
{
return dlsym(h, sym);
}
int wrap_dlclose(void* h)
{
return dlclose(h);
}
#endif

View File

@@ -0,0 +1,20 @@
/*
* Wrappers to emulate dlopen() on other systems like Windows
*/
#pragma once
#include <libdevcore/Log.h>
#if defined(_WIN32)
#include <windows.h>
void* wrap_dlopen(const char* filename);
void* wrap_dlsym(void* h, const char* sym);
int wrap_dlclose(void* h);
#else
/* assume we can use dlopen itself... */
#include <dlfcn.h>
void* wrap_dlopen(const char* filename);
void* wrap_dlsym(void* h, const char* sym);
int wrap_dlclose(void* h);
#endif

197
zano/libhwmon/wrapnvml.cpp Normal file
View File

@@ -0,0 +1,197 @@
/*
* A trivial little dlopen()-based wrapper library for the
* NVIDIA NVML library, to allow runtime discovery of NVML on an
* arbitrary system. This is all very hackish and simple-minded, but
* it serves my immediate needs in the short term until NVIDIA provides
* a static NVML wrapper library themselves, hopefully in
* CUDA 6.5 or maybe sometime shortly after.
*
* This trivial code is made available under the "new" 3-clause BSD license,
* and/or any of the GPL licenses you prefer.
* Feel free to use the code and modify as you see fit.
*
* John E. Stone - john.stone@gmail.com
*
* Modified to work with progminer by
*
* Philipp Andreas - github@smurfy.de
*/
#include <stdio.h>
#include <stdlib.h>
#include "wrapnvml.h"
#if defined(__cplusplus)
extern "C" {
#endif
wrap_nvml_handle* wrap_nvml_create()
{
wrap_nvml_handle* nvmlh = nullptr;
/*
* We use hard-coded library installation locations for the time being...
* No idea where or if libnvidia-ml.so is installed on MacOS X, a
* deep scouring of the filesystem on one of the Mac CUDA build boxes
* I used turned up nothing, so for now it's not going to work on OSX.
*/
#if defined(_WIN32)
/* Windows */
#define libnvidia_ml "%PROGRAMFILES%/NVIDIA Corporation/NVSMI/nvml.dll"
#elif defined(__linux) && (defined(__i386__) || defined(__ARM_ARCH_7A__))
/* In rpm based linux distributions link name is with extension .1 */
/* 32-bit linux assumed */
#define libnvidia_ml "libnvidia-ml.so.1"
#elif defined(__linux)
/* 64-bit linux assumed */
#define libnvidia_ml "libnvidia-ml.so.1"
#else
#define libnvidia_ml ""
#warning "Unrecognized platform: need NVML DLL path for this platform..."
return nullptr;
#endif
#ifdef _WIN32
char tmp[512];
ExpandEnvironmentStringsA(libnvidia_ml, tmp, sizeof(tmp));
#else
char tmp[512] = libnvidia_ml;
#endif
void* nvml_dll = wrap_dlopen(tmp);
if (nvml_dll == nullptr)
{
cwarn << "Failed to obtain all required NVML function pointers";
cwarn << "NVIDIA hardware monitoring disabled";
return nullptr;
}
nvmlh = (wrap_nvml_handle*)calloc(1, sizeof(wrap_nvml_handle));
nvmlh->nvml_dll = nvml_dll;
nvmlh->nvmlInit = (wrap_nvmlReturn_t(*)(void))wrap_dlsym(nvmlh->nvml_dll, "nvmlInit");
nvmlh->nvmlDeviceGetCount =
(wrap_nvmlReturn_t(*)(int*))wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetCount_v2");
nvmlh->nvmlDeviceGetHandleByIndex = (wrap_nvmlReturn_t(*)(int, wrap_nvmlDevice_t*))wrap_dlsym(
nvmlh->nvml_dll, "nvmlDeviceGetHandleByIndex_v2");
nvmlh->nvmlDeviceGetPciInfo = (wrap_nvmlReturn_t(*)(
wrap_nvmlDevice_t, wrap_nvmlPciInfo_t*))wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetPciInfo");
nvmlh->nvmlDeviceGetName = (wrap_nvmlReturn_t(*)(wrap_nvmlDevice_t, char*, int))wrap_dlsym(
nvmlh->nvml_dll, "nvmlDeviceGetName");
nvmlh->nvmlDeviceGetTemperature = (wrap_nvmlReturn_t(*)(wrap_nvmlDevice_t, int,
unsigned int*))wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetTemperature");
nvmlh->nvmlDeviceGetFanSpeed = (wrap_nvmlReturn_t(*)(
wrap_nvmlDevice_t, unsigned int*))wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetFanSpeed");
nvmlh->nvmlDeviceGetPowerUsage = (wrap_nvmlReturn_t(*)(
wrap_nvmlDevice_t, unsigned int*))wrap_dlsym(nvmlh->nvml_dll, "nvmlDeviceGetPowerUsage");
nvmlh->nvmlShutdown = (wrap_nvmlReturn_t(*)())wrap_dlsym(nvmlh->nvml_dll, "nvmlShutdown");
if (nvmlh->nvmlInit == nullptr || nvmlh->nvmlShutdown == nullptr ||
nvmlh->nvmlDeviceGetCount == nullptr || nvmlh->nvmlDeviceGetHandleByIndex == nullptr ||
nvmlh->nvmlDeviceGetPciInfo == nullptr || nvmlh->nvmlDeviceGetName == nullptr ||
nvmlh->nvmlDeviceGetTemperature == nullptr || nvmlh->nvmlDeviceGetFanSpeed == nullptr ||
nvmlh->nvmlDeviceGetPowerUsage == nullptr)
{
cwarn << "Failed to obtain all required NVML function pointers";
cwarn << "NVIDIA hardware monitoring disabled";
wrap_dlclose(nvmlh->nvml_dll);
free(nvmlh);
return nullptr;
}
nvmlh->nvmlInit();
nvmlh->nvmlDeviceGetCount(&nvmlh->nvml_gpucount);
nvmlh->devs = (wrap_nvmlDevice_t*)calloc(nvmlh->nvml_gpucount, sizeof(wrap_nvmlDevice_t));
nvmlh->nvml_pci_domain_id = (unsigned int*)calloc(nvmlh->nvml_gpucount, sizeof(unsigned int));
nvmlh->nvml_pci_bus_id = (unsigned int*)calloc(nvmlh->nvml_gpucount, sizeof(unsigned int));
nvmlh->nvml_pci_device_id = (unsigned int*)calloc(nvmlh->nvml_gpucount, sizeof(unsigned int));
/* Obtain GPU device handles we're going to need repeatedly... */
for (int i = 0; i < nvmlh->nvml_gpucount; i++)
{
nvmlh->nvmlDeviceGetHandleByIndex(i, &nvmlh->devs[i]);
}
/* Query PCI info for each NVML device, and build table for mapping of */
/* CUDA device IDs to NVML device IDs and vice versa */
for (int i = 0; i < nvmlh->nvml_gpucount; i++)
{
wrap_nvmlPciInfo_t pciinfo;
nvmlh->nvmlDeviceGetPciInfo(nvmlh->devs[i], &pciinfo);
nvmlh->nvml_pci_domain_id[i] = pciinfo.domain;
nvmlh->nvml_pci_bus_id[i] = pciinfo.bus;
nvmlh->nvml_pci_device_id[i] = pciinfo.device;
}
return nvmlh;
}
int wrap_nvml_destroy(wrap_nvml_handle* nvmlh)
{
nvmlh->nvmlShutdown();
wrap_dlclose(nvmlh->nvml_dll);
free(nvmlh);
return 0;
}
int wrap_nvml_get_gpucount(wrap_nvml_handle* nvmlh, int* gpucount)
{
*gpucount = nvmlh->nvml_gpucount;
return 0;
}
int wrap_nvml_get_gpu_name(wrap_nvml_handle* nvmlh, int gpuindex, char* namebuf, int bufsize)
{
if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
return -1;
if (nvmlh->nvmlDeviceGetName(nvmlh->devs[gpuindex], namebuf, bufsize) != WRAPNVML_SUCCESS)
return -1;
return 0;
}
int wrap_nvml_get_tempC(wrap_nvml_handle* nvmlh, int gpuindex, unsigned int* tempC)
{
if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
return -1;
if (nvmlh->nvmlDeviceGetTemperature(
nvmlh->devs[gpuindex], 0u /* NVML_TEMPERATURE_GPU */, tempC) != WRAPNVML_SUCCESS)
return -1;
return 0;
}
int wrap_nvml_get_fanpcnt(wrap_nvml_handle* nvmlh, int gpuindex, unsigned int* fanpcnt)
{
if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
return -1;
if (nvmlh->nvmlDeviceGetFanSpeed(nvmlh->devs[gpuindex], fanpcnt) != WRAPNVML_SUCCESS)
return -1;
return 0;
}
int wrap_nvml_get_power_usage(wrap_nvml_handle* nvmlh, int gpuindex, unsigned int* milliwatts)
{
if (gpuindex < 0 || gpuindex >= nvmlh->nvml_gpucount)
return -1;
if (nvmlh->nvmlDeviceGetPowerUsage(nvmlh->devs[gpuindex], milliwatts) != WRAPNVML_SUCCESS)
return -1;
return 0;
}
#if defined(__cplusplus)
}
#endif

110
zano/libhwmon/wrapnvml.h Normal file
View File

@@ -0,0 +1,110 @@
/*
* A trivial little dlopen()-based wrapper library for the
* NVIDIA NVML library, to allow runtime discovery of NVML on an
* arbitrary system. This is all very hackish and simple-minded, but
* it serves my immediate needs in the short term until NVIDIA provides
* a static NVML wrapper library themselves, hopefully in
* CUDA 6.5 or maybe sometime shortly after.
*
* This trivial code is made available under the "new" 3-clause BSD license,
* and/or any of the GPL licenses you prefer.
* Feel free to use the code and modify as you see fit.
*
* John E. Stone - john.stone@gmail.com
*
*/
#pragma once
#include "wraphelper.h"
#if defined(__cplusplus)
extern "C" {
#endif
/*
* Ugly hacks to avoid dependencies on the real nvml.h until it starts
* getting included with the CUDA toolkit or a GDK that's got a known
* install location, etc.
*/
typedef enum wrap_nvmlReturn_enum { WRAPNVML_SUCCESS = 0 } wrap_nvmlReturn_t;
typedef void* wrap_nvmlDevice_t;
/* our own version of the PCI info struct */
typedef struct
{
char bus_id_str[16]; /* string form of bus info */
unsigned int domain;
unsigned int bus;
unsigned int device;
unsigned int pci_device_id; /* combined device and vendor id */
unsigned int pci_subsystem_id;
unsigned int res0; /* NVML internal use only */
unsigned int res1;
unsigned int res2;
unsigned int res3;
} wrap_nvmlPciInfo_t;
/*
* Handle to hold the function pointers for the entry points we need,
* and the shared library itself.
*/
typedef struct
{
void* nvml_dll;
int nvml_gpucount;
unsigned int* nvml_pci_domain_id;
unsigned int* nvml_pci_bus_id;
unsigned int* nvml_pci_device_id;
wrap_nvmlDevice_t* devs;
wrap_nvmlReturn_t (*nvmlInit)(void);
wrap_nvmlReturn_t (*nvmlDeviceGetCount)(int*);
wrap_nvmlReturn_t (*nvmlDeviceGetHandleByIndex)(int, wrap_nvmlDevice_t*);
wrap_nvmlReturn_t (*nvmlDeviceGetPciInfo)(wrap_nvmlDevice_t, wrap_nvmlPciInfo_t*);
wrap_nvmlReturn_t (*nvmlDeviceGetName)(wrap_nvmlDevice_t, char*, int);
wrap_nvmlReturn_t (*nvmlDeviceGetTemperature)(wrap_nvmlDevice_t, int, unsigned int*);
wrap_nvmlReturn_t (*nvmlDeviceGetFanSpeed)(wrap_nvmlDevice_t, unsigned int*);
wrap_nvmlReturn_t (*nvmlDeviceGetPowerUsage)(wrap_nvmlDevice_t, unsigned int*);
wrap_nvmlReturn_t (*nvmlShutdown)(void);
} wrap_nvml_handle;
wrap_nvml_handle* wrap_nvml_create();
int wrap_nvml_destroy(wrap_nvml_handle* nvmlh);
/*
* Query the number of GPUs seen by NVML
*/
int wrap_nvml_get_gpucount(wrap_nvml_handle* nvmlh, int* gpucount);
/*
* query the name of the GPU model from the CUDA device ID
*
*/
int wrap_nvml_get_gpu_name(wrap_nvml_handle* nvmlh, int gpuindex, char* namebuf, int bufsize);
/*
* Query the current GPU temperature (Celsius), from the CUDA device ID
*/
int wrap_nvml_get_tempC(wrap_nvml_handle* nvmlh, int gpuindex, unsigned int* tempC);
/*
* Query the current GPU fan speed (percent) from the CUDA device ID
*/
int wrap_nvml_get_fanpcnt(wrap_nvml_handle* nvmlh, int gpuindex, unsigned int* fanpcnt);
/*
* Query the current GPU power usage in milliwatts from the CUDA device ID
*
* This feature is only available on recent GPU generations and may be
* limited in some cases only to Tesla series GPUs.
* If the query is run on an unsupported GPU, this routine will return -1.
*/
int wrap_nvml_get_power_usage(wrap_nvml_handle* nvmlh, int gpuindex, unsigned int* milliwatts);
#if defined(__cplusplus)
}
#endif