2Copyright 2019-2021 Intel Corporation
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
17var PDH_FMT_LONG = 0x00000100;
18var PDH_FMT_DOUBLE = 0x00000200;
20var promise = require('promise');
21if (process.platform == 'win32')
23 var GM = require('_GenericMarshal');
24 GM.kernel32 = GM.CreateNativeProxy('kernel32.dll');
25 GM.kernel32.CreateMethod('GlobalMemoryStatusEx');
27 GM.pdh = GM.CreateNativeProxy('pdh.dll');
28 GM.pdh.CreateMethod('PdhAddEnglishCounterA');
29 GM.pdh.CreateMethod('PdhCloseQuery');
30 GM.pdh.CreateMethod('PdhCollectQueryData');
31 GM.pdh.CreateMethod('PdhGetFormattedCounterValue');
32 GM.pdh.CreateMethod('PdhGetFormattedCounterArrayA');
33 GM.pdh.CreateMethod('PdhOpenQueryA');
34 GM.pdh.CreateMethod('PdhRemoveCounter');
37function windows_cpuUtilization()
39 var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
40 p.counter = GM.CreateVariable(16);
41 p.cpu = GM.CreatePointer();
42 p.cpuTotal = GM.CreatePointer();
44 if ((err = GM.pdh.PdhOpenQueryA(0, 0, p.cpu).Val) != 0) { p._rej(err); return; }
46 // This gets the CPU Utilization for each proc
47 if ((err = GM.pdh.PdhAddEnglishCounterA(p.cpu.Deref(), GM.CreateVariable('\\Processor(*)\\% Processor Time'), 0, p.cpuTotal).Val) != 0) { p._rej(err); return; }
49 if ((err = GM.pdh.PdhCollectQueryData(p.cpu.Deref()).Val != 0)) { p._rej(err); return; }
50 p._timeout = setTimeout(function (po)
53 var bufSize = GM.CreateVariable(4);
54 var itemCount = GM.CreateVariable(4);
55 var buffer, szName, item;
57 if ((e = GM.pdh.PdhCollectQueryData(po.cpu.Deref()).Val != 0)) { po._rej(e); return; }
59 if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, 0).Val) == -2147481646)
61 buffer = GM.CreateVariable(bufSize.toBuffer().readUInt32LE());
68 if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, buffer).Val) != 0) { po._rej(e); return; }
69 for(var i=0;i<itemCount.toBuffer().readUInt32LE();++i)
71 item = buffer.Deref(i * 24, 24);
72 szName = item.Deref(0, GM.PointerSize).Deref();
73 if (szName.String == '_Total')
75 u.total = item.Deref(16, 8).toBuffer().readDoubleLE();
79 u.cpus[parseInt(szName.String)] = item.Deref(16, 8).toBuffer().readDoubleLE();
83 GM.pdh.PdhRemoveCounter(po.cpuTotal.Deref());
84 GM.pdh.PdhCloseQuery(po.cpu.Deref());
90function windows_memUtilization()
92 var info = GM.CreateVariable(64);
93 info.Deref(0, 4).toBuffer().writeUInt32LE(64);
94 GM.kernel32.GlobalMemoryStatusEx(info);
98 MemTotal: require('bignum').fromBuffer(info.Deref(8, 8).toBuffer(), { endian: 'little' }),
99 MemFree: require('bignum').fromBuffer(info.Deref(16, 8).toBuffer(), { endian: 'little' })
102 ret.percentFree = ((ret.MemFree.div(require('bignum')('1048576')).toNumber() / ret.MemTotal.div(require('bignum')('1048576')).toNumber()) * 100);//.toFixed(2);
103 ret.percentConsumed = ((ret.MemTotal.sub(ret.MemFree).div(require('bignum')('1048576')).toNumber() / ret.MemTotal.div(require('bignum')('1048576')).toNumber()) * 100);//.toFixed(2);
104 ret.MemTotal = ret.MemTotal.toString();
105 ret.MemFree = ret.MemFree.toString();
111function linux_cpuUtilization() {
112 var ret = { cpus: [] };
113 var info = require('fs').readFileSync('/proc/stat');
114 var lines = info.toString().split('\n');
118 var currSum, currIdle, utilization;
119 for (var i in lines) {
120 columns = lines[i].split(' ');
121 if (!columns[0].startsWith('cpu')) { break; }
124 while (columns[++x] == '');
125 for (y = x; y < columns.length; ++y) { currSum += parseInt(columns[y]); }
126 currIdle = parseInt(columns[3 + x]);
128 var diffIdle = currIdle - cpuLastIdle[cpuNo];
129 var diffSum = currSum - cpuLastSum[cpuNo];
131 utilization = (100 - ((diffIdle / diffSum) * 100));
133 cpuLastSum[cpuNo] = currSum;
134 cpuLastIdle[cpuNo] = currIdle;
137 ret.total = utilization;
139 ret.cpus.push(utilization);
144 var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
148function linux_memUtilization()
152 var info = require('fs').readFileSync('/proc/meminfo').toString().split('\n');
156 tokens = info[i].split(' ');
160 ret.total = parseInt(tokens[tokens.length - 2]);
162 case 'MemAvailable:':
163 ret.free = parseInt(tokens[tokens.length - 2]);
167 ret.percentFree = ((ret.free / ret.total) * 100);//.toFixed(2);
168 ret.percentConsumed = (((ret.total - ret.free) / ret.total) * 100);//.toFixed(2);
172function macos_cpuUtilization()
174 var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
175 var child = require('child_process').execFile('/bin/sh', ['sh']);
176 child.stdout.str = '';
177 child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
178 child.stdin.write('top -l 1 | grep -E "^CPU"\nexit\n');
181 var lines = child.stdout.str.split('\n');
182 if (lines[0].length > 0)
184 var usage = lines[0].split(':')[1];
185 var bdown = usage.split(',');
187 var tot = parseFloat(bdown[0].split('%')[0].trim()) + parseFloat(bdown[1].split('%')[0].trim());
188 ret._res({total: tot, cpus: []});
192 ret._rej('parse error');
197function macos_memUtilization()
200 var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
201 var child = require('child_process').execFile('/bin/sh', ['sh']);
202 child.stdout.str = '';
203 child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
204 child.stdin.write('top -l 1 | grep -E "^Phys"\nexit\n');
207 var lines = child.stdout.str.split('\n');
208 if (lines[0].length > 0)
210 var usage = lines[0].split(':')[1];
211 var bdown = usage.split(',');
212 if (bdown.length > 2){ // new style - PhysMem: 5750M used (1130M wired, 634M compressor), 1918M unused.
213 mem.MemFree = parseInt(bdown[2].trim().split(' ')[0]);
214 } else { // old style - PhysMem: 6683M used (1606M wired), 9699M unused.
215 mem.MemFree = parseInt(bdown[1].trim().split(' ')[0]);
217 mem.MemUsed = parseInt(bdown[0].trim().split(' ')[0]);
218 mem.MemTotal = (mem.MemFree + mem.MemUsed);
219 mem.percentFree = ((mem.MemFree / mem.MemTotal) * 100);//.toFixed(2);
220 mem.percentConsumed = (((mem.MemTotal - mem.MemFree) / mem.MemTotal) * 100);//.toFixed(2);
225 throw ('Parse Error');
229function windows_thermals()
233 ret = require('win-wmi').query('ROOT\\WMI', 'SELECT CurrentTemperature,InstanceName FROM MSAcpi_ThermalZoneTemperature',['CurrentTemperature','InstanceName']);
235 for (var i = 0; i < ret.length; ++i) {
236 ret[i]['CurrentTemperature'] = ((parseFloat(ret[i]['CurrentTemperature']) / 10) - 273.15).toFixed(2);
243function linux_thermals()
246 child = require('child_process').execFile('/bin/sh', ['sh']);
247 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
248 child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
249 child.stdin.write("for folder in /sys/class/thermal/thermal_zone*/; do [ -e \"$folder/temp\" ] && echo \"$(cat \"$folder/temp\"),$(cat \"$folder/type\")\"; done\nexit\n");
251 if(child.stdout.str.trim()!='')
253 var lines = child.stdout.str.trim().split('\n');
254 for (var i = 0; i < lines.length; ++i)
256 var line = lines[i].trim().split(',');
257 ret.push({CurrentTemperature: (parseFloat(line[0])/1000), InstanceName: line[1]});
260 child = require('child_process').execFile('/bin/sh', ['sh']);
261 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
262 child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
263 child.stdin.write("for mon in /sys/class/hwmon/hwmon*; do for label in \"$mon\"/temp*_label; do if [ -f $label ]; then echo $(cat \"$label\")___$(cat \"${label%_*}_input\"); fi; done; done;\nexit\n");
265 if(child.stdout.str.trim()!='')
267 var lines = child.stdout.str.trim().split('\n');
268 for (var i = 0; i < lines.length; ++i)
270 var line = lines[i].trim().split('___');
271 ret.push({ CurrentTemperature: (parseFloat(line[1])/1000), InstanceName: line[0] });
277function macos_thermals()
280 var child = require('child_process').execFile('/bin/sh', ['sh']);
281 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
282 child.stderr.on('data', function () { });
283 child.stdin.write('powermetrics --help | grep SMC\nexit\n');
285 if (child.stdout.str.trim() != '')
287 child = require('child_process').execFile('/bin/sh', ['sh']);
288 child.stdout.str = ''; child.stdout.on('data', function (c)
290 this.str += c.toString();
291 var tokens = this.str.trim().split('\n');
292 for (var i in tokens)
294 if (tokens[i].split(' die temperature: ').length > 1)
296 ret.push({CurrentTemperature: tokens[i].split(' ')[3], InstanceName: tokens[i].split(' ')[0]});
301 child.stderr.on('data', function (c) {
302 if (c.toString().split('unable to get smc values').length > 1) { // error getting sensors so just kill
307 child.stdin.write('powermetrics -s smc -i 500 -n 1\n');
308 child.waitExit(2000);
313switch(process.platform)
316 module.exports = { cpuUtilization: linux_cpuUtilization, memUtilization: linux_memUtilization, thermals: linux_thermals };
319 module.exports = { cpuUtilization: windows_cpuUtilization, memUtilization: windows_memUtilization, thermals: windows_thermals };
322 module.exports = { cpuUtilization: macos_cpuUtilization, memUtilization: macos_memUtilization, thermals: macos_thermals };