graphics() adding nvidia-smi support (linux, win)

This commit is contained in:
Sebastian Hildebrandt 2020-12-17 18:33:01 +01:00
parent 250f44dfab
commit 0da06759cb
5 changed files with 138 additions and 32 deletions

View File

@ -34,7 +34,7 @@ This is amazing. Started as a small project just for myself, it now has > 10,000
## Upcoming
**MacOS on ARM (Apple silicon support), Windows on ARM**: November 11th 2020 - We will have a closer look on that! I just ordered new hardware (any support is highly appreciated - [Buy me a coffe](https://www.buymeacoffee.com/systeminfo)). As soon as I have the new hardware here, I work very hard on it to get full native support for those platforms!
**MacOS on ARM (Apple silicon support), Windows on ARM**: November 11th 2020 - We will have a closer look on that! I just ordered new hardware (any support is highly appreciated - [Buy me a coffee](https://www.buymeacoffee.com/systeminfo)). As soon as I have the new hardware here, I work very hard on it to get full native support for those platforms!
**Version 5**: we are planning a new major version with some minor breaking changes and some additional features. Will try to make this available Q1 of 2021.

View File

@ -46,7 +46,7 @@
<p><span class="bold">Affected versions:</span>
< 4.31.1<br>
<span class="bold">Date:</span> 2020-12-11<br>
<span class="bold">CVE indentifier</span> CVE-2020-28448
<span class="bold">CVE indentifier</span> CVE-2020-26274, CVE-2020-28448
</p>
<h4>Impact</h4>

View File

@ -14,11 +14,13 @@
// ----------------------------------------------------------------------------------
const os = require('os');
const fs = require('fs');
const exec = require('child_process').exec;
const execSync = require('child_process').execSync;
const util = require('./util');
let _platform = process.platform;
let _nvidiaSmiPath = '';
const _linux = (_platform === 'linux');
const _darwin = (_platform === 'darwin');
@ -204,7 +206,8 @@ function graphics(callback) {
bus: '',
busAddress: '',
vram: -1,
vramDynamic: false
vramDynamic: false,
pciID: ''
};
let isGraphicsController = false;
// PCI bus IDs
@ -238,9 +241,14 @@ function graphics(callback) {
bus: '',
busAddress: '',
vram: -1,
vramDynamic: false
vramDynamic: false,
};
}
const pciIDCandidate = lines[i].split(' ')[0];
if (/[\da-fA-F]{2}:[\da-fA-F]{2}\.[\da-fA-F]/.test(pciIDCandidate)) {
currentController.busAddress = pciIDCandidate;
}
isGraphicsController = true;
let endpos = lines[i].search(/\[[0-9a-f]{4}:[0-9a-f]{4}]|$/);
let parts = lines[i].substr(vgapos, endpos - vgapos).split(':');
@ -310,7 +318,7 @@ function graphics(callback) {
return devices
}, {});
for (let deviceId in devices) {
const device = devices[deviceId]
const device = devices[deviceId];
if (device['CL_DEVICE_TYPE'] === 'CL_DEVICE_TYPE_GPU') {
let busAddress;
if (device['CL_DEVICE_TOPOLOGY_AMD']) {
@ -354,9 +362,114 @@ function graphics(callback) {
}
}
}
return controllers
return controllers;
}
function getNvidiaSmi() {
if (_nvidiaSmiPath) {
return _nvidiaSmiPath;
}
if (_windows) {
try {
const basePath = util.WINDIR + '\\System32\\DriverStore\\FileRepository';
const dirContent = fs.readdirSync(basePath);
const candidateDirs = dirContent.filter(dir => dir.startsWith('nv'));
const targetDir = candidateDirs.find(dir => {
const content = fs.readdirSync([basePath, dir].join('/'));
return content.includes('nvidia-smi.exe');
});
if (targetDir) {
_nvidiaSmiPath = [basePath, targetDir, 'nvidia-smi.exe'].join('/');
}
} catch (e) {
util.noop()
}
} else if (_linux) {
_nvidiaSmiPath = 'nvidia-smi';
}
return _nvidiaSmiPath;
}
function nvidiaSmi(options) {
const nvidiaSmiExe = getNvidiaSmi();
if (nvidiaSmiExe) {
const nvidiaSmiOpts = '--query-gpu=driver_version,pci.sub_device_id,name,pci.bus_id,fan.speed,memory.total,memory.used,memory.free,utilization.gpu,utilization.memory,temperature.gpu,temperature.memory,power.draw,power.limit,clocks.gr,clocks.mem --format=csv,noheader,nounits'
try {
const res = execSync(nvidiaSmiExe + ' ' + nvidiaSmiOpts, options);
return res;
} catch (e) {
util.noop();
}
}
return '';
}
function nvidiaDevices() {
function safeParseNumber(value) {
if ([null, undefined].includes(value)) {
return value;
}
return parseFloat(value);
}
const stdout = nvidiaSmi();
if (!stdout) {
return [];
}
const gpus = stdout.split('\n').filter(Boolean);
const results = gpus.map(gpu => {
const splittedData = gpu.split(', ').map(value => value.includes('N/A') ? undefined : value);
if (splittedData.length === 16) {
return {
driverVersion: splittedData[0],
subDeviceId: splittedData[1],
name: splittedData[2],
pciBus: splittedData[3],
fanSpeed: safeParseNumber(splittedData[4]),
memoryTotal: safeParseNumber(splittedData[5]),
memoryUsed: safeParseNumber(splittedData[6]),
memoryFree: safeParseNumber(splittedData[7]),
utilizationGpu: safeParseNumber(splittedData[8]),
utilizationMemory: safeParseNumber(splittedData[9]),
temperatureGpu: safeParseNumber(splittedData[10]),
temperatureMemory: safeParseNumber(splittedData[11]),
powerDraw: safeParseNumber(splittedData[12]),
powerLimit: safeParseNumber(splittedData[13]),
clockCore: safeParseNumber(splittedData[14]),
clockMemory: safeParseNumber(splittedData[15]),
}
}
});
return results;
}
function mergeControllerNvidia(controller, nvidia) {
if (nvidia.driverVersion) { controller.driverVersion = nvidia.driverVersion; }
if (nvidia.subDeviceId) { controller.subDeviceId = nvidia.subDeviceId; }
if (nvidia.name) { controller.name = nvidia.name; }
if (nvidia.pciBus) { controller.pciBus = nvidia.pciBus; }
if (nvidia.fanSpeed) { controller.fanSpeed = nvidia.fanSpeed; }
if (nvidia.memoryTotal) { controller.memoryTotal = nvidia.memoryTotal; }
if (nvidia.memoryUsed) { controller.memoryUsed = nvidia.memoryUsed; }
if (nvidia.memoryFree) { controller.memoryFree = nvidia.memoryFree; }
if (nvidia.utilizationGpu) { controller.utilizationGpu = nvidia.utilizationGpu; }
if (nvidia.utilizationMemory) { controller.utilizationMemory = nvidia.utilizationMemory; }
if (nvidia.temperatureGpu) { controller.temperatureGpu = nvidia.temperatureGpu; }
if (nvidia.temperatureMemory) { controller.temperatureMemory = nvidia.temperatureMemory; }
if (nvidia.powerDraw) { controller.powerDraw = nvidia.powerDraw; }
if (nvidia.powerLimit) { controller.powerLimit = nvidia.powerLimit; }
if (nvidia.clockCore) { controller.clockCore = nvidia.clockCore; }
if (nvidia.clockMemory) { controller.clockMemory = nvidia.clockMemory; }
return controller
}
function parseLinesLinuxEdid(edid) {
// parsen EDID
// --> model
@ -592,6 +705,11 @@ function graphics(callback) {
if (!error) {
let lines = stdout.toString().split('\n');
result.controllers = parseLinesLinuxControllers(lines);
const nvidiaData = nvidiaDevices();
// needs to be rewritten ... using no spread operators
result.controllers = result.controllers.map(( controller ) => { // match by busAddress
return mergeControllerNvidia(controller, nvidiaData.find(({ pciBus }) => pciBus.endsWith(controller.busAddress)) || {} );
})
}
let cmd = "clinfo --raw";
exec(cmd, function (error, stdout) {
@ -643,13 +761,18 @@ function graphics(callback) {
workload.push(util.powerShell('Get-CimInstance -Namespace root\\wmi -ClassName WmiMonitorConnectionParams | fl'));
workload.push(util.powerShell('gwmi WmiMonitorID -Namespace root\\wmi | ForEach-Object {(($_.ManufacturerName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.ProductCodeID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.UserFriendlyName -notmatch 0 | foreach {[char]$_}) -join "") + "|" + (($_.SerialNumberID -notmatch 0 | foreach {[char]$_}) -join "") + "|" + $_.InstanceName}'));
const nvidiaData = nvidiaDevices();
Promise.all(
workload
).then(data => {
// controller
let csections = data[0].split(/\n\s*\n/);
result.controllers = parseLinesWindowsControllers(csections);
// needs to be rewritten ... using no spread operators
result.controllers = result.controllers.map((controller) => { // match by subDeviceId
return mergeControllerNvidia(controller, nvidiaData.find(device => controller.subDeviceId.toLowerCase() === device.subDeviceId.split('x')[1].toLowerCase()) || {});
})
// displays
let dsections = data[1].split(/\n\s*\n/);
// result.displays = parseLinesWindowsDisplays(dsections);
@ -732,12 +855,17 @@ function graphics(callback) {
if (sections[i].trim() !== '') {
let lines = sections[i].trim().split('\r\n');
let subDeviceId = util.getValue(lines, 'PNPDeviceID', '=').match(/SUBSYS_[a-fA-F\d]{8}/)[0];
if (subDeviceId) {
subDeviceId = subDeviceId.split('_')[1];
}
controllers.push({
vendor: util.getValue(lines, 'AdapterCompatibility', '='),
model: util.getValue(lines, 'name', '='),
bus: util.getValue(lines, 'PNPDeviceID', '=').startsWith('PCI') ? 'PCI' : '',
vram: parseInt(util.getValue(lines, 'AdapterRAM', '='), 10) / 1024 / 1024,
vramDynamic: (util.getValue(lines, 'VideoMemoryType', '=') === '2')
vramDynamic: (util.getValue(lines, 'VideoMemoryType', '=') === '2'),
subDeviceId
});
_resolutionx = util.toInt(util.getValue(lines, 'CurrentHorizontalResolution', '=')) || _resolutionx;
_resolutiony = util.toInt(util.getValue(lines, 'CurrentVerticalResolution', '=')) || _resolutiony;
@ -749,30 +877,6 @@ function graphics(callback) {
return controllers;
}
// function parseLinesWindowsDisplays(sections) {
// let displays = [];
// for (let i in sections) {
// if (sections.hasOwnProperty(i)) {
// if (sections[i].trim() !== '') {
// let lines = sections[i].trim().split('\r\n');
// displays.push({
// vendor: util.getValue(lines, 'MonitorManufacturer', '='),
// model: util.getValue(lines, 'Name', '='),
// main: false,
// builtin: false,
// connection: '',
// sizex: -1,
// sizey: -1,
// pixeldepth: -1,
// resolutionx: util.toInt(util.getValue(lines, 'ScreenWidth', '=')),
// resolutiony: util.toInt(util.getValue(lines, 'ScreenHeight', '=')),
// });
// }
// }
// }
// return displays;
// }
function parseLinesWindowsDisplaysPowershell(ssections, msections, dsections, tsections, isections) {
let displays = [];
let vendor = '';

1
lib/index.d.ts vendored
View File

@ -255,6 +255,7 @@ export namespace Systeminformation {
busAddress?: string;
vram: number;
vramDynamic: boolean;
subDeviceId?: string;
}
interface GraphicsDisplayData {

View File

@ -830,3 +830,4 @@ exports.stringToLower = stringToLower;
exports.stringToString = stringToString;
exports.stringSubstr = stringSubstr;
exports.stringTrim = stringTrim;
exports.WINDIR = WINDIR;