diff --git a/CHANGELOG.md b/CHANGELOG.md index f84409e..cc49d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ New Functions +- `cpuCache`: returns CPU cache (L1, L2, L3) sizes (new in version 3.14) +- `cpuFlags`: returns CPU flags (new in version 3.14) +- `currentLoad.cpus`: returns current load per cpu/core in an array (new in version 3.14) - `shell`: returns standard shell e.g. /bin/bash (new in version 3.13) - `blockDevices`: returns array of block devices like disks, partitions, raids, roms (new in version 3.10) - `dockerContainerProcesses`: returns processes for a specific docker container (new in version 3.8) @@ -29,8 +32,9 @@ New Functions - `dockerAll`: returns a list of all docker containers including their stats (new in version 3.1) - `disksIO`: returns overall diskIO and IOPS values for all mounted volumes (new in version 3.0) -Bug Fixes +Bug Fixes / improvements +- improvement `cpuTemperature` - works now also on Raspberry Pi - bugfix `disksIO` - on OSX read and write got mixed up - several bug fixes (like assess errors in `cpuCurrentspeed`, potentially incorrect results in `users`, ...) - testet on even more platforms and linux distributions @@ -90,6 +94,7 @@ Other changes | Version | Date | Comment | | -------------- | -------------- | -------- | +| 3.14.0 | 2017-01-14 | added currentLoad per cpu/core, cpu cache and cpu flags | | 3.13.0 | 2016-11-23 | added shell (returns standard shell) | | 3.12.0 | 2016-11-17 | refactoring and extended currentLoad | | 3.11.2 | 2016-11-16 | blockDevices: improved for older lsblk versions | diff --git a/README.md b/README.md index a6ae04c..b32bb20 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ si.cpu() ### Latest Activity +- Version 3.14.0: added currentLoad per cpu/core, cpu cache (L1, L2, L3) and cpu flags - Version 3.13.0: added shell (returns standard shell) - Version 3.12.0: refactoring and extended currentLoad (better OSX coverage and added irq load). - Version 3.11.0: blockDevices now also for OSX and also extended (+ label, model, serial, protocol). @@ -124,6 +125,12 @@ This library is splitted in several sections: | - brand | X | X | e.g. 'Core(TM)2 Duo' | | - speed | X | X | in GHz e.g. '3.40' | | - cores | X | X | # cores | +| si.cpuFlags(cb) | X | X | CPU flags| +| si.cpuCache(cb) | X | X | CPU cache sizes | +| - l1d | X | X | L1D size | +| - l1i | X | X | L1I size | +| - l2 | X | X | L2 size | +| - l3 | X | X | L3 size | | si.cpuCurrentspeed(cb) | X | X | current CPU speed (in GHz)| | - avg | X | X | avg CPU speed (all cores) | | - min | X | X | min CPU speed (all cores) | @@ -227,6 +234,7 @@ This library is splitted in several sections: | - currentload_nice | X | X | CPU-Load Nice in % | | - currentload_system | X | X | CPU-Load System in % | | - currentload_irq | X | X | CPU-Load System in % | +| - cpus[] | X | X | current loads per CPU in % | | si.fullLoad(cb) | X | X | CPU-full load since bootup in % | | si.services('mysql, apache2', cb) | X | X | pass comma separated string of services | | - [0].name | X | X | name of service | diff --git a/lib/cpu.js b/lib/cpu.js index 840052f..fb92741 100644 --- a/lib/cpu.js +++ b/lib/cpu.js @@ -25,6 +25,23 @@ const _windows = (_platform == 'Windows_NT'); const NOT_SUPPORTED = 'not supported'; let _cpu_speed = '0.00'; +let _current_cpu = { + user: 0, + nice: 0, + system: 0, + idle: 0, + irq: 0, + load: 0, + tick: 0, + ms: 0, + currentload: 0, + currentload_user: 0, + currentload_nice: 0, + currentload_system: 0, + currentload_irq: 0 +}; +let _cpus = []; +let _corecount = 0; function cpuBrandManufacturer(res) { res.brand = res.brand.replace(/\(R\)+/g, "®"); @@ -203,16 +220,32 @@ function cpuTemperature(callback) { if (callback) { callback(result) } resolve(result); } else { - exec("/opt/vc/bin/vcgencmd measure_temp", function (error, stdout) { - if (!error) { - let lines = stdout.toString().split('\n'); - if (lines.length > 0 && lines[0].indexOf('=')) { - result.main = parseFloat(lines[0].split("=")[1]); - result.max = result.main - } + fs.stat('/sys/class/thermal/thermal_zone0/temp', function(err, stat) { + if(err == null) { + exec("cat /sys/class/thermal/thermal_zone0/temp", function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0) { + result.main = parseFloat(lines[0]) / 1000.0; + result.max = result.main + } + } + if (callback) { callback(result) } + resolve(result); + }); + } else { + exec("/opt/vc/bin/vcgencmd measure_temp", function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0 && lines[0].indexOf('=')) { + result.main = parseFloat(lines[0].split("=")[1]); + result.max = result.main + } + } + if (callback) { callback(result) } + resolve(result); + }); } - if (callback) { callback(result) } - resolve(result); }); } @@ -227,3 +260,281 @@ function cpuTemperature(callback) { } exports.cpuTemperature = cpuTemperature; + +// -------------------------- +// CPU Flags + +function cpuFlags(callback) { + + return new Promise((resolve, reject) => { + process.nextTick(() => { + if (_windows) { + let error = new Error(NOT_SUPPORTED); + if (callback) { callback(NOT_SUPPORTED) } + reject(error); + } + + let result = ''; + if (_linux) { + exec("lscpu", function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + if (line.split(':')[0].toUpperCase().indexOf('FLAGS') != -1) { + result = line.split(':')[1].trim().toLowerCase(); + } + }); + } + if (callback) { callback(result) } + resolve(result); + }); + } + if (_darwin) { + exec("sysctl machdep.cpu.features", function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + if (lines.length > 0 && lines[0].indexOf('machdep.cpu.features:') != -1) { + result = lines[0].split(':')[1].trim().toLowerCase(); + } + } + if (callback) { callback(result) } + resolve(result); + }); + } + }); + }); +} + +exports.cpuFlags = cpuFlags; + +// -------------------------- +// CPU Flags + +function cpuCache(callback) { + + return new Promise((resolve, reject) => { + process.nextTick(() => { + if (_windows) { + let error = new Error(NOT_SUPPORTED); + if (callback) { callback(NOT_SUPPORTED) } + reject(error); + } + + let result = {}; + if (_linux) { + exec("lscpu", function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + let parts = line.split(':'); + if (parts[0].toUpperCase().indexOf('L1D CACHE') != -1) { + result.l1d = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + if (parts[0].toUpperCase().indexOf('L1I CACHE') != -1) { + result.l1i = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + if (parts[0].toUpperCase().indexOf('L2 CACHE') != -1) { + result.l2 = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + if (parts[0].toUpperCase().indexOf('L3 CACHE') != -1) { + result.l3 = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + }); + } + if (callback) { callback(result) } + resolve(result); + }); + } + if (_darwin) { + exec("sysctl hw.l1icachesize hw.l1dcachesize hw.l2cachesize hw.l3cachesize", function (error, stdout) { + if (!error) { + let lines = stdout.toString().split('\n'); + lines.forEach(function (line) { + let parts = line.split(':'); + if (parts[0].toLowerCase().indexOf('hw.l1icachesize') != -1) { + result.l1d = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + if (parts[0].toLowerCase().indexOf('hw.l1dcachesize') != -1) { + result.l1i = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + if (parts[0].toLowerCase().indexOf('hw.l2cachesize') != -1) { + result.l2 = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + if (parts[0].toLowerCase().indexOf('hw.l3cachesize') != -1) { + result.l3 = parseInt(parts[1].trim()) * (parts[1].indexOf('K') != -1 ? 1024 : 1); + } + }); + } + if (callback) { callback(result) } + resolve(result); + }); + } + }); + }); +} + +exports.cpuCache = cpuCache; + +// -------------------------- +// CPU - current load - in % + +function getLoad() { + + return new Promise((resolve) => { + process.nextTick(() => { + let loads = os.loadavg().map(function (x) { return x / util.cores() }); + let avgload = parseFloat((Math.max.apply(Math, loads)).toFixed(2)); + let result = {}; + + let now = Date.now() - _current_cpu.ms; + if (now >= 200) { + _current_cpu.ms = Date.now(); + const cpus = os.cpus(); + let totalUser = 0; + let totalSystem = 0; + let totalNice = 0; + let totalIrq = 0; + let totalIdle = 0; + let cores = []; + _corecount = cpus.length; + + for (let i = 0; i < _corecount; i++) { + const cpu = cpus[i].times; + totalUser += cpu.user; + totalSystem += cpu.sys; + totalNice += cpu.nice; + totalIrq += cpu.irq; + totalIdle += cpu.idle; + let tmp_tick = (_cpus && _cpus[i] && _cpus[i].totalTick ? _cpus[i].totalTick : 0); + let tmp_load = (_cpus && _cpus[i] && _cpus[i].totalLoad ? _cpus[i].totalLoad : 0); + let tmp_user = (_cpus && _cpus[i] && _cpus[i].user ? _cpus[i].user : 0); + let tmp_system = (_cpus && _cpus[i] && _cpus[i].sys ? _cpus[i].sys : 0); + let tmp_nice = (_cpus && _cpus[i] && _cpus[i].nice ? _cpus[i].nice : 0); + let tmp_irq = (_cpus && _cpus[i] && _cpus[i].irq ? _cpus[i].irq : 0); + _cpus[i] = cpu; + _cpus[i].totalTick = _cpus[i].user + _cpus[i].sys + _cpus[i].nice + _cpus[i].irq + _cpus[i].idle; + _cpus[i].totalLoad = _cpus[i].user + _cpus[i].sys + _cpus[i].nice + _cpus[i].irq; + _cpus[i].currentTick = _cpus[i].totalTick - tmp_tick; + _cpus[i].load = (_cpus[i].totalLoad - tmp_load) / _cpus[i].currentTick * 100; + _cpus[i].load_user = (_cpus[i].user - tmp_user) / _cpus[i].currentTick * 100; + _cpus[i].load_system = (_cpus[i].sys - tmp_system) / _cpus[i].currentTick * 100; + _cpus[i].load_nice = (_cpus[i].nice - tmp_nice) / _cpus[i].currentTick * 100; + _cpus[i].load_irq = (_cpus[i].irq - tmp_irq) / _cpus[i].currentTick * 100; + cores[i] = {}; + cores[i].load = _cpus[i].load; + cores[i].load_user = _cpus[i].load_user; + cores[i].load_system = _cpus[i].load_system; + cores[i].load_nice = _cpus[i].load_nice; + cores[i].load_irq = _cpus[i].load_irq; + } + let totalTick = totalUser + totalSystem + totalNice + totalIrq + totalIdle; + let totalLoad = totalUser + totalSystem + totalNice + totalIrq; + let currentTick = totalTick - _current_cpu.tick; + result = { + avgload: avgload, + currentload: (totalLoad - _current_cpu.load) / currentTick * 100, + currentload_user: (totalUser - _current_cpu.user) / currentTick * 100, + currentload_system: (totalSystem - _current_cpu.system) / currentTick * 100, + currentload_nice: (totalNice - _current_cpu.nice) / currentTick * 100, + currentload_irq: (totalIrq - _current_cpu.irq) / currentTick * 100, + cpus: cores + }; + _current_cpu = { + user: totalUser, + nice: totalNice, + system: totalSystem, + idle: totalIdle, + irq: totalIrq, + tick: totalTick, + load: totalLoad, + ms: _current_cpu.ms, + currentload: result.currentload, + currentload_user: result.currentload_user, + currentload_system: result.currentload_system, + currentload_nice: result.currentload_nice, + currentload_irq: result.currentload_irq, + }; + } else { + let cores = []; + for (let i = 0; i < _corecount; i++) { + cores[i] = {}; + cores[i].load = _cpus[i].load; + cores[i].load_user = _cpus[i].load_user; + cores[i].load_system = _cpus[i].load_system; + cores[i].load_nice = _cpus[i].load_nice; + cores[i].load_irq = _cpus[i].load_irq; + } + result = { + avgload: avgload, + currentload: _current_cpu.currentload, + currentload_user: _current_cpu.currentload_user, + currentload_system: _current_cpu.currentload_system, + currentload_nice: _current_cpu.currentload_nice, + currentload_irq: _current_cpu.currentload_irq, + cpus: cores + }; + } + resolve(result); + }); + }); +} + +function currentLoad(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + getLoad().then(result => { + if (callback) { callback(result) } + resolve(result); + }) + }); + }); +} + +exports.currentLoad = currentLoad; + +// -------------------------- +// PS - full load +// since bootup + +function getFullLoad() { + + return new Promise((resolve) => { + process.nextTick(() => { + + const cpus = os.cpus(); + let totalUser = 0; + let totalSystem = 0; + let totalNice = 0; + let totalIrq = 0; + let totalIdle = 0; + + for (let i = 0, len = cpus.length; i < len; i++) { + const cpu = cpus[i].times; + totalUser += cpu.user; + totalSystem += cpu.sys; + totalNice += cpu.nice; + totalIrq += cpu.irq; + totalIdle += cpu.idle; + } + let totalTicks = totalIdle + totalIrq + totalNice + totalSystem + totalUser; + let result = (totalTicks - totalIdle) / totalTicks * 100.0; + + resolve(result); + }); + }); +} + +function fullLoad(callback) { + + return new Promise((resolve) => { + process.nextTick(() => { + getFullLoad().then(result => { + if (callback) { callback(result) } + resolve(result); + }) + }); + }); +} + +exports.fullLoad = fullLoad; diff --git a/lib/index.js b/lib/index.js index a71dd1a..dd964a8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -81,6 +81,7 @@ // -------------------------------- // // version date comment +// 3.14.0 2017-01-14 added currentLoad per cpu/core, cpu cache (L1, L2, L3) and cpu flags // 3.13.0 2016-11-23 added shell (determines standard shell) // 3.12.0 2016-11-17 refactoring and extended currentLoad (better OSX coverage and added irq load) // 3.11.2 2016-11-16 blockDevices: improved for older lsblk versions @@ -288,7 +289,7 @@ function getDynamicData(srv, iface, callback) { functionProcessed(); }); - processes.currentLoad().then(res => { + cpu.currentLoad().then(res => { data.currentLoad = res; functionProcessed(); }); @@ -394,8 +395,12 @@ exports.versions = osInfo.versions; exports.shell = osInfo.shell; exports.cpu = cpu.cpu; +exports.cpuFlags = cpu.cpuFlags; +exports.cpuCache = cpu.cpuCache; exports.cpuCurrentspeed = cpu.cpuCurrentspeed; exports.cpuTemperature = cpu.cpuTemperature; +exports.currentLoad = cpu.currentLoad; +exports.fullLoad = cpu.fullLoad; exports.mem = mem; @@ -413,8 +418,6 @@ exports.networkInterfaces = network.networkInterfaces; exports.networkStats = network.networkStats; exports.networkConnections = network.networkConnections; -exports.currentLoad = processes.currentLoad; -exports.fullLoad = processes.fullLoad; exports.services = processes.services; exports.processes = processes.processes; exports.processLoad = processes.processLoad; diff --git a/lib/processes.js b/lib/processes.js index 99b619b..6e63eb6 100644 --- a/lib/processes.js +++ b/lib/processes.js @@ -29,153 +29,6 @@ let _process_cpu = { list: {}, ms: 0 }; -let _current_cpu = { - user: 0, - nice: 0, - system: 0, - idle: 0, - irq: 0, - load: 0, - tick: 0, - ms: 0, - currentload: 0, - currentload_user: 0, - currentload_nice: 0, - currentload_system: 0, - currentload_irq: 0 -}; - -// -------------------------- -// PS - current load - in % - -function getLoad() { - - return new Promise((resolve) => { - process.nextTick(() => { - let loads = os.loadavg().map(function (x) { return x / util.cores() }); - let avgload = parseFloat((Math.max.apply(Math, loads)).toFixed(2)); - let result = {}; - - let now = Date.now() - _current_cpu.ms; - if (now >= 200) { - _current_cpu.ms = Date.now(); - const cpus = os.cpus(); - let totalUser = 0; - let totalSystem = 0; - let totalNice = 0; - let totalIrq = 0; - let totalIdle = 0; - - for (let i = 0, len = cpus.length; i < len; i++) { - const cpu = cpus[i]; - totalUser += cpu.times.user; - totalSystem += cpu.times.sys; - totalNice += cpu.times.nice; - totalIrq += cpu.times.irq; - totalIdle += cpu.times.idle; - } - let totalTick = totalUser + totalSystem + totalNice + totalIrq + totalIdle; - let totalLoad = totalUser + totalSystem + totalNice + totalIrq; - let currentTick = totalTick - _current_cpu.tick; - result = { - avgload: avgload, - currentload: (totalLoad - _current_cpu.load) / currentTick * 100, - currentload_user: (totalUser - _current_cpu.user) / currentTick * 100, - currentload_system: (totalSystem - _current_cpu.system) / currentTick * 100, - currentload_nice: (totalNice - _current_cpu.nice) / currentTick * 100, - currentload_irq: (totalIrq - _current_cpu.irq) / currentTick * 100 - }; - _current_cpu = { - user: totalUser, - nice: totalNice, - system: totalSystem, - idle: totalIdle, - irq: totalIrq, - tick: totalTick, - load: totalLoad, - ms: _current_cpu.ms, - currentload: result.currentload, - currentload_user: result.currentload_user, - currentload_nice: result.currentload_nice, - currentload_system: result.currentload_system, - currentload_irq: result.currentload_irq, - }; - } else { - result = { - avgload: avgload, - currentload: _current_cpu.currentload, - currentload_user: _current_cpu.currentload_user, - currentload_nice: _current_cpu.currentload_nice, - currentload_system: _current_cpu.currentload_system, - currentload_irq: _current_cpu.currentload_irq, - }; - } - resolve(result); - }); - }); -} - -function currentLoad(callback) { - - return new Promise((resolve) => { - process.nextTick(() => { - getLoad().then(result => { - if (callback) { callback(result) } - resolve(result); - }) - }); - }); -} - -exports.currentLoad = currentLoad; - -// -------------------------- -// PS - full load -// since bootup - -function getFullLoad() { - - return new Promise((resolve) => { - process.nextTick(() => { - - let result = {}; - if (_linux) { - if (fs.existsSync('/proc/uptime')) { - let output = fs.readFileSync('/proc/uptime').toString(); - output = output.replace(/ +/g, " ").split(' '); - let uptime = parseFloat(output[0]); - let idletime = parseFloat(output[1]) / util.cores(); - result.fullload = (uptime - idletime) / uptime * 100.0; - resolve(result); - } - } - if (_darwin) { - result.fullload = 0; - resolve(result); - } - }); - }); -} - -function fullLoad(callback) { - - return new Promise((resolve, reject) => { - process.nextTick(() => { - if (_windows) { - let error = new Error(NOT_SUPPORTED); - if (callback) { callback(NOT_SUPPORTED) } - reject(error); - } - - getFullLoad().then(result => { - if (callback) { callback(result) } - resolve(result); - }) - }); - }); -} - -exports.fullLoad = fullLoad; // -------------------------- // PS - services