master
js 289 lines 10 KB
Raw
1 /*
2 Copyright 2019-2021 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 var PDH_FMT_LONG = 0x00000100;
18 var PDH_FMT_DOUBLE = 0x00000200;
19
20 var promise = require('promise');
21 if (process.platform == 'win32')
22 {
23 var GM = require('_GenericMarshal');
24 GM.kernel32 = GM.CreateNativeProxy('kernel32.dll');
25 GM.kernel32.CreateMethod('GlobalMemoryStatusEx');
26
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');
35 }
36
37 function windows_cpuUtilization()
38 {
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();
43 var err = 0;
44 if ((err = GM.pdh.PdhOpenQueryA(0, 0, p.cpu).Val) != 0) { p._rej(err); return; }
45
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; }
48
49 if ((err = GM.pdh.PdhCollectQueryData(p.cpu.Deref()).Val != 0)) { p._rej(err); return; }
50 p._timeout = setTimeout(function (po)
51 {
52 var u = { cpus: [] };
53 var bufSize = GM.CreateVariable(4);
54 var itemCount = GM.CreateVariable(4);
55 var buffer, szName, item;
56 var e;
57 if ((e = GM.pdh.PdhCollectQueryData(po.cpu.Deref()).Val != 0)) { po._rej(e); return; }
58
59 if ((e = GM.pdh.PdhGetFormattedCounterArrayA(po.cpuTotal.Deref(), PDH_FMT_DOUBLE, bufSize, itemCount, 0).Val) == -2147481646)
60 {
61 buffer = GM.CreateVariable(bufSize.toBuffer().readUInt32LE());
62 }
63 else
64 {
65 po._rej(e);
66 return;
67 }
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)
70 {
71 item = buffer.Deref(i * 24, 24);
72 szName = item.Deref(0, GM.PointerSize).Deref();
73 if (szName.String == '_Total')
74 {
75 u.total = item.Deref(16, 8).toBuffer().readDoubleLE();
76 }
77 else
78 {
79 u.cpus[parseInt(szName.String)] = item.Deref(16, 8).toBuffer().readDoubleLE();
80 }
81 }
82
83 GM.pdh.PdhRemoveCounter(po.cpuTotal.Deref());
84 GM.pdh.PdhCloseQuery(po.cpu.Deref());
85 p._res(u);
86 }, 100, p);
87
88 return (p);
89 }
90 function windows_memUtilization()
91 {
92 var info = GM.CreateVariable(64);
93 info.Deref(0, 4).toBuffer().writeUInt32LE(64);
94 GM.kernel32.GlobalMemoryStatusEx(info);
95
96 var ret =
97 {
98 MemTotal: require('bignum').fromBuffer(info.Deref(8, 8).toBuffer(), { endian: 'little' }),
99 MemFree: require('bignum').fromBuffer(info.Deref(16, 8).toBuffer(), { endian: 'little' })
100 };
101
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();
106 return (ret);
107 }
108
109 var cpuLastIdle = [];
110 var cpuLastSum = [];
111 function linux_cpuUtilization() {
112 var ret = { cpus: [] };
113 var info = require('fs').readFileSync('/proc/stat');
114 var lines = info.toString().split('\n');
115 var columns;
116 var x, y;
117 var cpuNo = 0;
118 var currSum, currIdle, utilization;
119 for (var i in lines) {
120 columns = lines[i].split(' ');
121 if (!columns[0].startsWith('cpu')) { break; }
122
123 x = 0, currSum = 0;
124 while (columns[++x] == '');
125 for (y = x; y < columns.length; ++y) { currSum += parseInt(columns[y]); }
126 currIdle = parseInt(columns[3 + x]);
127
128 var diffIdle = currIdle - cpuLastIdle[cpuNo];
129 var diffSum = currSum - cpuLastSum[cpuNo];
130
131 utilization = (100 - ((diffIdle / diffSum) * 100));
132
133 cpuLastSum[cpuNo] = currSum;
134 cpuLastIdle[cpuNo] = currIdle;
135
136 if (!ret.total) {
137 ret.total = utilization;
138 } else {
139 ret.cpus.push(utilization);
140 }
141 ++cpuNo;
142 }
143
144 var p = new promise(function (res, rej) { this._res = res; this._rej = rej; });
145 p._res(ret);
146 return (p);
147 }
148 function linux_memUtilization()
149 {
150 var ret = {};
151
152 var info = require('fs').readFileSync('/proc/meminfo').toString().split('\n');
153 var tokens;
154 for(var i in info)
155 {
156 tokens = info[i].split(' ');
157 switch(tokens[0])
158 {
159 case 'MemTotal:':
160 ret.total = parseInt(tokens[tokens.length - 2]);
161 break;
162 case 'MemFree:':
163 ret.free = parseInt(tokens[tokens.length - 2]);
164 break;
165 }
166 }
167 ret.percentFree = ((ret.free / ret.total) * 100);//.toFixed(2);
168 ret.percentConsumed = (((ret.total - ret.free) / ret.total) * 100);//.toFixed(2);
169 return (ret);
170 }
171
172 function macos_cpuUtilization()
173 {
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');
179 child.waitExit();
180
181 var lines = child.stdout.str.split('\n');
182 if (lines[0].length > 0)
183 {
184 var usage = lines[0].split(':')[1];
185 var bdown = usage.split(',');
186
187 var tot = parseFloat(bdown[0].split('%')[0].trim()) + parseFloat(bdown[1].split('%')[0].trim());
188 ret._res({total: tot, cpus: []});
189 }
190 else
191 {
192 ret._rej('parse error');
193 }
194
195 return (ret);
196 }
197 function macos_memUtilization()
198 {
199 var mem = { };
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');
205 child.waitExit();
206
207 var lines = child.stdout.str.split('\n');
208 if (lines[0].length > 0)
209 {
210 var usage = lines[0].split(':')[1];
211 var bdown = usage.split(',');
212
213 mem.MemTotal = parseInt(bdown[0].trim().split(' ')[0]);
214 mem.MemFree = parseInt(bdown[1].trim().split(' ')[0]);
215 mem.percentFree = ((mem.MemFree / mem.MemTotal) * 100);//.toFixed(2);
216 mem.percentConsumed = (((mem.MemTotal - mem.MemFree) / mem.MemTotal) * 100);//.toFixed(2);
217 return (mem);
218 }
219 else
220 {
221 throw ('Parse Error');
222 }
223 }
224
225 function windows_thermals()
226 {
227 var ret = [];
228 try {
229 ret = require('win-wmi').query('ROOT\\WMI', 'SELECT CurrentTemperature,InstanceName FROM MSAcpi_ThermalZoneTemperature',['CurrentTemperature','InstanceName']);
230 if (ret[0]) {
231 for (var i = 0; i < ret.length; ++i) {
232 ret[i]['CurrentTemperature'] = ((parseFloat(ret[i]['CurrentTemperature']) / 10) - 273.15).toFixed(2);
233 }
234 }
235 } catch (ex) { }
236 return (ret);
237 }
238
239 function linux_thermals()
240 {
241 child = require('child_process').execFile('/bin/sh', ['sh']);
242 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
243 child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
244 child.stdin.write("cat /sys/class/thermal/thermal_zone*/temp | awk '{ print $0 / 1000 }'\nexit\n");
245 child.waitExit();
246 var ret = child.stdout.str.trim().split('\n');
247 if (ret.length == 1 && ret[0] == '') { ret = []; }
248 return (ret);
249 }
250
251 function macos_thermals()
252 {
253 var ret = [];
254 var child = require('child_process').execFile('/bin/sh', ['sh']);
255 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
256 child.stderr.on('data', function () { });
257 child.stdin.write('powermetrics --help | grep SMC\nexit\n');
258 child.waitExit();
259
260 if (child.stdout.str.trim() != '')
261 {
262 child = require('child_process').execFile('/bin/sh', ['sh']);
263 child.stdout.str = ''; child.stdout.on('data', function (c)
264 {
265 this.str += c.toString();
266 var tokens = this.str.trim().split('\n');
267 for (var i in tokens)
268 {
269 if (tokens[i].split(' die temperature: ').length > 1)
270 {
271 ret.push(tokens[i].split(' ')[3]);
272 this.parent.kill();
273 }
274 }
275 });
276 child.stderr.str = ''; child.stderr.on('data', function (c) { this.str += c.toString(); });
277 child.stdin.write('powermetrics -s smc\n');
278 child.waitExit(5000);
279 }
280 return (ret);
281 }
282
283 const platformConfig = {
284 linux: { cpuUtilization: linux_cpuUtilization, memUtilization: linux_memUtilization, thermals: linux_thermals },
285 win32: { cpuUtilization: windows_cpuUtilization, memUtilization: windows_memUtilization, thermals: windows_thermals },
286 darwin: { cpuUtilization: macos_cpuUtilization, memUtilization: macos_memUtilization, thermals: macos_thermals }
287 };
288
289 module.exports = platformConfig[process.platform];