1 | 'use strict';
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | const os = require('os');
|
17 | const exec = require('child_process').exec;
|
18 | const execSync = require('child_process').execSync;
|
19 | const util = require('./util');
|
20 | const fs = require('fs');
|
21 |
|
22 | let _platform = process.platform;
|
23 |
|
24 | const _linux = (_platform === 'linux');
|
25 | const _darwin = (_platform === 'darwin');
|
26 | const _windows = (_platform === 'win32');
|
27 | const _freebsd = (_platform === 'freebsd');
|
28 | const _openbsd = (_platform === 'openbsd');
|
29 | const _netbsd = (_platform === 'netbsd');
|
30 | const _sunos = (_platform === 'sunos');
|
31 |
|
32 | const OSX_RAM_manufacturers = {
|
33 | '0x014F': 'Transcend Information',
|
34 | '0x2C00': 'Micron Technology Inc.',
|
35 | '0x802C': 'Micron Technology Inc.',
|
36 | '0x80AD': 'Hynix Semiconductor Inc.',
|
37 | '0x80CE': 'Samsung Electronics Inc.',
|
38 | '0xAD00': 'Hynix Semiconductor Inc.',
|
39 | '0xCE00': 'Samsung Electronics Inc.',
|
40 | '0x02FE': 'Elpida',
|
41 | '0x5105': 'Qimonda AG i. In.',
|
42 | '0x8551': 'Qimonda AG i. In.',
|
43 | '0x859B': 'Crucial',
|
44 | '0x04CD': 'G-Skill'
|
45 | };
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 | function mem(callback) {
|
125 |
|
126 | return new Promise((resolve) => {
|
127 | process.nextTick(() => {
|
128 |
|
129 | let result = {
|
130 | total: os.totalmem(),
|
131 | free: os.freemem(),
|
132 | used: os.totalmem() - os.freemem(),
|
133 |
|
134 | active: os.totalmem() - os.freemem(),
|
135 | available: os.freemem(),
|
136 | buffers: 0,
|
137 | cached: 0,
|
138 | slab: 0,
|
139 | buffcache: 0,
|
140 |
|
141 | swaptotal: 0,
|
142 | swapused: 0,
|
143 | swapfree: 0
|
144 | };
|
145 |
|
146 | if (_linux) {
|
147 | fs.readFile('/proc/meminfo', function (error, stdout) {
|
148 | if (!error) {
|
149 | const lines = stdout.toString().split('\n');
|
150 | result.total = parseInt(util.getValue(lines, 'memtotal'), 10);
|
151 | result.total = result.total ? result.total * 1024 : os.totalmem();
|
152 | result.free = parseInt(util.getValue(lines, 'memfree'), 10);
|
153 | result.free = result.free ? result.free * 1024 : os.freemem();
|
154 | result.used = result.total - result.free;
|
155 |
|
156 | result.buffers = parseInt(util.getValue(lines, 'buffers'), 10);
|
157 | result.buffers = result.buffers ? result.buffers * 1024 : 0;
|
158 | result.cached = parseInt(util.getValue(lines, 'cached'), 10);
|
159 | result.cached = result.cached ? result.cached * 1024 : 0;
|
160 | result.slab = parseInt(util.getValue(lines, 'slab'), 10);
|
161 | result.slab = result.slab ? result.slab * 1024 : 0;
|
162 | result.buffcache = result.buffers + result.cached + result.slab;
|
163 |
|
164 | let available = parseInt(util.getValue(lines, 'memavailable'), 10);
|
165 | result.available = available ? available * 1024 : result.free + result.buffcache;
|
166 | result.active = result.total - result.available;
|
167 |
|
168 | result.swaptotal = parseInt(util.getValue(lines, 'swaptotal'), 10);
|
169 | result.swaptotal = result.swaptotal ? result.swaptotal * 1024 : 0;
|
170 | result.swapfree = parseInt(util.getValue(lines, 'swapfree'), 10);
|
171 | result.swapfree = result.swapfree ? result.swapfree * 1024 : 0;
|
172 | result.swapused = result.swaptotal - result.swapfree;
|
173 | }
|
174 | if (callback) { callback(result); }
|
175 | resolve(result);
|
176 | });
|
177 | }
|
178 | if (_freebsd || _openbsd || _netbsd) {
|
179 | exec('/sbin/sysctl -a 2>/dev/null | grep -E "hw.realmem|hw.physmem|vm.stats.vm.v_page_count|vm.stats.vm.v_wire_count|vm.stats.vm.v_active_count|vm.stats.vm.v_inactive_count|vm.stats.vm.v_cache_count|vm.stats.vm.v_free_count|vm.stats.vm.v_page_size"', function (error, stdout) {
|
180 | if (!error) {
|
181 | let lines = stdout.toString().split('\n');
|
182 | const pagesize = parseInt(util.getValue(lines, 'vm.stats.vm.v_page_size'), 10);
|
183 | const inactive = parseInt(util.getValue(lines, 'vm.stats.vm.v_inactive_count'), 10) * pagesize;
|
184 | const cache = parseInt(util.getValue(lines, 'vm.stats.vm.v_cache_count'), 10) * pagesize;
|
185 |
|
186 | result.total = parseInt(util.getValue(lines, 'hw.realmem'), 10);
|
187 | if (isNaN(result.total)) result.total = parseInt(util.getValue(lines, 'hw.physmem'), 10);
|
188 | result.free = parseInt(util.getValue(lines, 'vm.stats.vm.v_free_count'), 10) * pagesize;
|
189 | result.buffcache = inactive + cache;
|
190 | result.available = result.buffcache + result.free;
|
191 | result.active = result.total - result.free - result.buffcache;
|
192 |
|
193 | result.swaptotal = 0;
|
194 | result.swapfree = 0;
|
195 | result.swapused = 0;
|
196 |
|
197 | }
|
198 | if (callback) { callback(result); }
|
199 | resolve(result);
|
200 | });
|
201 | }
|
202 | if (_sunos) {
|
203 | if (callback) { callback(result); }
|
204 | resolve(result);
|
205 | }
|
206 | if (_darwin) {
|
207 | exec('vm_stat 2>/dev/null | grep "Pages active"', function (error, stdout) {
|
208 | if (!error) {
|
209 | let lines = stdout.toString().split('\n');
|
210 |
|
211 | result.active = parseInt(lines[0].split(':')[1], 10) * 4096;
|
212 | result.buffcache = result.used - result.active;
|
213 | result.available = result.free + result.buffcache;
|
214 | }
|
215 | exec('sysctl -n vm.swapusage 2>/dev/null', function (error, stdout) {
|
216 | if (!error) {
|
217 | let lines = stdout.toString().split('\n');
|
218 | if (lines.length > 0) {
|
219 | let line = lines[0].replace(/,/g, '.').replace(/M/g, '');
|
220 | line = line.trim().split(' ');
|
221 | for (let i = 0; i < line.length; i++) {
|
222 | if (line[i].toLowerCase().indexOf('total') !== -1) result.swaptotal = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024;
|
223 | if (line[i].toLowerCase().indexOf('used') !== -1) result.swapused = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024;
|
224 | if (line[i].toLowerCase().indexOf('free') !== -1) result.swapfree = parseFloat(line[i].split('=')[1].trim()) * 1024 * 1024;
|
225 | }
|
226 | }
|
227 | }
|
228 | if (callback) { callback(result); }
|
229 | resolve(result);
|
230 | });
|
231 | });
|
232 | }
|
233 | if (_windows) {
|
234 | let swaptotal = 0;
|
235 | let swapused = 0;
|
236 | try {
|
237 | util.wmic('pagefile get AllocatedBaseSize, CurrentUsage').then((stdout, error) => {
|
238 | if (!error) {
|
239 | let lines = stdout.split('\r\n').filter(line => line.trim() !== '').filter((line, idx) => idx > 0);
|
240 | lines.forEach(function (line) {
|
241 | if (line !== '') {
|
242 | line = line.trim().split(/\s\s+/);
|
243 | swaptotal = swaptotal + parseInt(line[0], 10);
|
244 | swapused = swapused + parseInt(line[1], 10);
|
245 | }
|
246 | });
|
247 | }
|
248 | result.swaptotal = swaptotal * 1024 * 1024;
|
249 | result.swapused = swapused * 1024 * 1024;
|
250 | result.swapfree = result.swaptotal - result.swapused;
|
251 |
|
252 | if (callback) { callback(result); }
|
253 | resolve(result);
|
254 | });
|
255 | } catch (e) {
|
256 | if (callback) { callback(result); }
|
257 | resolve(result);
|
258 | }
|
259 | }
|
260 | });
|
261 | });
|
262 | }
|
263 |
|
264 | exports.mem = mem;
|
265 |
|
266 | function memLayout(callback) {
|
267 |
|
268 | function getManufacturer(manId) {
|
269 | if ({}.hasOwnProperty.call(OSX_RAM_manufacturers, manId)) {
|
270 | return (OSX_RAM_manufacturers[manId]);
|
271 | }
|
272 | return manId;
|
273 | }
|
274 |
|
275 | return new Promise((resolve) => {
|
276 | process.nextTick(() => {
|
277 |
|
278 | let result = [];
|
279 |
|
280 | if (_linux || _freebsd || _openbsd || _netbsd) {
|
281 | exec('export LC_ALL=C; dmidecode -t memory 2>/dev/null | grep -iE "Size:|Type|Speed|Manufacturer|Form Factor|Locator|Memory Device|Serial Number|Voltage|Part Number"; unset LC_ALL', function (error, stdout) {
|
282 | if (!error) {
|
283 | let devices = stdout.toString().split('Memory Device');
|
284 | devices.shift();
|
285 | devices.forEach(function (device) {
|
286 | let lines = device.split('\n');
|
287 | const sizeString = util.getValue(lines, 'Size');
|
288 | const size = sizeString.indexOf('GB') >= 0 ? parseInt(sizeString, 10) * 1024 * 1024 * 1024 : parseInt(sizeString, 10) * 1024 * 1024;
|
289 | if (parseInt(util.getValue(lines, 'Size'), 10) > 0) {
|
290 | result.push({
|
291 | size,
|
292 | bank: util.getValue(lines, 'Bank Locator'),
|
293 | type: util.getValue(lines, 'Type:'),
|
294 | clockSpeed: (util.getValue(lines, 'Configured Clock Speed:') ? parseInt(util.getValue(lines, 'Configured Clock Speed:'), 10) : (util.getValue(lines, 'Speed:') ? parseInt(util.getValue(lines, 'Speed:'), 10) : -1)),
|
295 | formFactor: util.getValue(lines, 'Form Factor:'),
|
296 | manufacturer: util.getValue(lines, 'Manufacturer:'),
|
297 | partNum: util.getValue(lines, 'Part Number:'),
|
298 | serialNum: util.getValue(lines, 'Serial Number:'),
|
299 | voltageConfigured: parseFloat(util.getValue(lines, 'Configured Voltage:') || -1),
|
300 | voltageMin: parseFloat(util.getValue(lines, 'Minimum Voltage:') || -1),
|
301 | voltageMax: parseFloat(util.getValue(lines, 'Maximum Voltage:') || -1),
|
302 | });
|
303 | } else {
|
304 | result.push({
|
305 | size: 0,
|
306 | bank: util.getValue(lines, 'Bank Locator'),
|
307 | type: 'Empty',
|
308 | clockSpeed: 0,
|
309 | formFactor: util.getValue(lines, 'Form Factor:'),
|
310 | partNum: '',
|
311 | serialNum: '',
|
312 | voltageConfigured: -1,
|
313 | voltageMin: -1,
|
314 | voltageMax: -1,
|
315 | });
|
316 | }
|
317 | });
|
318 | }
|
319 | if (!result.length) {
|
320 | result.push({
|
321 | size: os.totalmem(),
|
322 | bank: '',
|
323 | type: '',
|
324 | clockSpeed: 0,
|
325 | formFactor: '',
|
326 | partNum: '',
|
327 | serialNum: '',
|
328 | voltageConfigured: -1,
|
329 | voltageMin: -1,
|
330 | voltageMax: -1,
|
331 | });
|
332 |
|
333 |
|
334 | try {
|
335 | let stdout = execSync('cat /proc/cpuinfo 2>/dev/null');
|
336 | let lines = stdout.toString().split('\n');
|
337 | let model = util.getValue(lines, 'hardware', ':', true).toUpperCase();
|
338 | let version = util.getValue(lines, 'revision', ':', true).toLowerCase();
|
339 |
|
340 | if (model === 'BCM2835' || model === 'BCM2708' || model === 'BCM2709' || model === 'BCM2835' || model === 'BCM2837') {
|
341 |
|
342 | const clockSpeed = {
|
343 | '0': 400,
|
344 | '1': 450,
|
345 | '2': 450,
|
346 | '3': 3200
|
347 | };
|
348 | result[0].clockSpeed = version && version[2] && clockSpeed[version[2]] || 400;
|
349 | result[0].clockSpeed = version && version[4] && version[4] === 'd' ? '500' : result[0].clockSpeed;
|
350 | result[0].type = 'LPDDR2';
|
351 | result[0].type = version && version[2] && version[2] === '3' ? 'LPDDR4' : result[0].type;
|
352 | result[0].formFactor = 'SoC';
|
353 |
|
354 | stdout = execSync('vcgencmd get_config sdram_freq 2>/dev/null');
|
355 | lines = stdout.toString().split('\n');
|
356 | let freq = parseInt(util.getValue(lines, 'sdram_freq', '=', true), 10) || 0;
|
357 | if (freq) {
|
358 | result.clockSpeed = freq;
|
359 | }
|
360 |
|
361 | stdout = execSync('vcgencmd measure_volts sdram_p 2>/dev/null');
|
362 | lines = stdout.toString().split('\n');
|
363 | let voltage = parseFloat(util.getValue(lines, 'volt', '=', true)) || 0;
|
364 | if (voltage) {
|
365 | result[0].voltageConfigured = voltage;
|
366 | result[0].voltageMin = voltage;
|
367 | result[0].voltageMax = voltage;
|
368 | }
|
369 | }
|
370 | } catch (e) {
|
371 | util.noop();
|
372 | }
|
373 |
|
374 | }
|
375 | if (callback) { callback(result); }
|
376 | resolve(result);
|
377 | });
|
378 | }
|
379 |
|
380 | if (_darwin) {
|
381 | exec('system_profiler SPMemoryDataType', function (error, stdout) {
|
382 | if (!error) {
|
383 | let devices = stdout.toString().split(' BANK ');
|
384 | let hasBank = true;
|
385 | if (devices.length === 1) {
|
386 | devices = stdout.toString().split(' DIMM');
|
387 | hasBank = false;
|
388 | }
|
389 | devices.shift();
|
390 | devices.forEach(function (device) {
|
391 | let lines = device.split('\n');
|
392 | const bank = (hasBank ? 'BANK ' : 'DIMM') + lines[0].trim().split('/')[0];
|
393 | const size = parseInt(util.getValue(lines, ' Size'));
|
394 | if (size) {
|
395 | result.push({
|
396 | size: size * 1024 * 1024 * 1024,
|
397 | bank: bank,
|
398 | type: util.getValue(lines, ' Type:'),
|
399 | clockSpeed: parseInt(util.getValue(lines, ' Speed:'), 10),
|
400 | formFactor: '',
|
401 | manufacturer: getManufacturer(util.getValue(lines, ' Manufacturer:')),
|
402 | partNum: util.getValue(lines, ' Part Number:'),
|
403 | serialNum: util.getValue(lines, ' Serial Number:'),
|
404 | voltageConfigured: -1,
|
405 | voltageMin: -1,
|
406 | voltageMax: -1,
|
407 | });
|
408 | } else {
|
409 | result.push({
|
410 | size: 0,
|
411 | bank: bank,
|
412 | type: 'Empty',
|
413 | clockSpeed: 0,
|
414 | formFactor: '',
|
415 | manufacturer: '',
|
416 | partNum: '',
|
417 | serialNum: '',
|
418 | voltageConfigured: -1,
|
419 | voltageMin: -1,
|
420 | voltageMax: -1,
|
421 | });
|
422 | }
|
423 | });
|
424 | }
|
425 | if (callback) { callback(result); }
|
426 | resolve(result);
|
427 | });
|
428 | }
|
429 | if (_sunos) {
|
430 | if (callback) { callback(result); }
|
431 | resolve(result);
|
432 | }
|
433 | if (_windows) {
|
434 | const memoryTypes = 'Unknown|Other|DRAM|Synchronous DRAM|Cache DRAM|EDO|EDRAM|VRAM|SRAM|RAM|ROM|FLASH|EEPROM|FEPROM|EPROM|CDRAM|3DRAM|SDRAM|SGRAM|RDRAM|DDR|DDR2|DDR2 FB-DIMM|Reserved|DDR3|FBD2|DDR4|LPDDR|LPDDR2|LPDDR3|LPDDR4'.split('|');
|
435 | const FormFactors = 'Unknown|Other|SIP|DIP|ZIP|SOJ|Proprietary|SIMM|DIMM|TSOP|PGA|RIMM|SODIMM|SRIMM|SMD|SSMP|QFP|TQFP|SOIC|LCC|PLCC|BGA|FPBGA|LGA'.split('|');
|
436 |
|
437 | try {
|
438 | util.wmic('memorychip get /value').then((stdout, error) => {
|
439 | if (!error) {
|
440 | let devices = stdout.toString().split('BankL');
|
441 | devices.shift();
|
442 | devices.forEach(function (device) {
|
443 | let lines = device.split('\r\n');
|
444 | result.push({
|
445 | size: parseInt(util.getValue(lines, 'Capacity', '='), 10) || 0,
|
446 | bank: util.getValue(lines, 'abel', '='),
|
447 | type: memoryTypes[parseInt(util.getValue(lines, 'MemoryType', '='), 10)],
|
448 | clockSpeed: parseInt(util.getValue(lines, 'ConfiguredClockSpeed', '='), 10) || 0,
|
449 | formFactor: FormFactors[parseInt(util.getValue(lines, 'FormFactor', '='), 10) || 0],
|
450 | manufacturer: util.getValue(lines, 'Manufacturer', '='),
|
451 | partNum: util.getValue(lines, 'PartNumber', '='),
|
452 | serialNum: util.getValue(lines, 'SerialNumber', '='),
|
453 | voltageConfigured: (parseInt(util.getValue(lines, 'ConfiguredVoltage', '='), 10) || 0) / 1000.0,
|
454 | voltageMin: (parseInt(util.getValue(lines, 'MinVoltage', '='), 10) || 0) / 1000.0,
|
455 | voltageMax: (parseInt(util.getValue(lines, 'MaxVoltage', '='), 10) || 0) / 1000.0,
|
456 | });
|
457 | });
|
458 | }
|
459 | if (callback) { callback(result); }
|
460 | resolve(result);
|
461 | });
|
462 | } catch (e) {
|
463 | if (callback) { callback(result); }
|
464 | resolve(result);
|
465 | }
|
466 | }
|
467 | });
|
468 | });
|
469 | }
|
470 |
|
471 | exports.memLayout = memLayout;
|
472 |
|