Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

windows implementation based on typeperf tool #7

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -23,3 +23,6 @@ build/Release
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules

# IDE files
.idea
20 changes: 19 additions & 1 deletion README.md
Expand Up @@ -44,7 +44,25 @@ AIX is tricky because I have no AIX test environement, at the moment we use: `ps
[#4](https://github.com/soyuka/pidusage/issues/4)

### Windows
Windows is really tricky, atm it uses the `wmic.exe`, feel free to share ideas on how to improve this.
We use `typeperf` tool in Windows.
First `typeperf "\Process(*)\ID Process" -sc 1` command is run to resolve `process name` from `PID`,
afterwards information from `typeperf -sc 1 "\Process(<process name>)\% Processor Time" "\Process(<process name>)\Working Set"` commands output is parsed for stats report.
To get reports from other(extra) counters like `Working Set - Private` (which is not available in old `Windows`-es) you can provide their name in options as follows:

```
var pusage = require('pidusage')

pusage.stat(process.pid, {extra : ['Virtual Bytes', 'Working Set - Private']}, function(err, stat) {

console.log('Pcpu: %s', stat.cpu) //same as parseFloat(stat.all['% Processor Time'])
console.log('Mem: %s', stat.memory) //same as parseFloat(stat.all['Working Set'])

console.log('Virtual Bytes: %s', stat.all['Virtual Bytes'])
console.log('Working Set - Private: %s', stat.all['Working Set - Private'])

})

```

# Licence

Expand Down
85 changes: 61 additions & 24 deletions lib/stats.js
@@ -1,3 +1,4 @@
"use strict";
var os = require('os')
, fs = require('fs')
, p = require('path')
Expand Down Expand Up @@ -113,39 +114,75 @@ var stats = {
})
})
},
/**
* This is really in a beta stage
*/
win: function(pid, options, done) {

// var history = this.history[pid] ? this.history[pid] : {}
// , uptime = os.uptime()
// , self = this

//http://social.msdn.microsoft.com/Forums/en-US/469ec6b7-4727-4773-9dc7-6e3de40e87b8/cpu-usage-in-for-each-active-process-how-is-this-best-determined-and-implemented-in-an?forum=csharplanguage
exec('wmic PROCESS '+pid+' get workingsetsize,usermodetime,kernelmodetime', function(error, stdout, stderr) {
if(error) {
console.log(error)
return done(error)
if (options.extra) {
if (!Array.isArray(options.extra)) {
return done('options.extra must be array') ;
}
}

stdout = stdout.split(os.EOL)[1]
stdout = stdout.replace(/\s\s+/g, ' ').split(' ')
function collectStat(history) {

var stats = {
kernelmodetime: parseFloat(stdout[0]),
usermodetime: parseFloat(stdout[1]),
workingsetsize: parseFloat(stdout[2])
}
var command = ['% Processor Time', 'Working Set']
.concat(options.extra)
.reduce(function(val, el){
return val + ' '+ history._counterPrefix + '\\' + el + '"' ;
}, 'typeperf -sc 1');


exec(command, function (error, stdout, stderr) {
if (error) {
console.log(error)
return done(error)
}

//according to http://technet.microsoft.com/en-us/library/ee176718.aspx
var total = (stats.usermodetime + stats.kernelmodetime) / 10000000 //seconds
var l = stdout.replace(/[\'\"]/g,'').split(os.EOL)
, titles = l[1].split(',')
, values = l[2].split(',');

var allStats = titles.reduce(function(obj, el, index){
el = el.substring(el.lastIndexOf('\\')+1);
obj[el] = values[index];
return obj;
}, {});

return done(null, {
cpu: total,
memory: stats.workingsetsize
cpu: parseFloat(values[1]),
memory: parseFloat(values[2]),
all: allStats
})
});
}

var history = this.history[pid]
, self = this;

if (history) { // if we already have in cache the process name as used in typeperf tool
collectStat(history);
} else {
exec('typeperf "\\Process(*)\\ID Process" -sc 1', function (error, stdout, stderr) {
if (error) {
console.log(error);
return done(error)
}
var lines = stdout.split(os.EOL)
, nameList = lines[1].split(',')
, pidList = lines[2].split(',');

var p = '"' + pid + '.';
for (var i = 0; i < pidList.length; i++) {
if (pidList[i].indexOf(p) === 0) {
history = self.history[pid] = {
_counterPrefix: nameList[i].substr(0, nameList[i].lastIndexOf('\\'))
};
break;
}
}
collectStat(history);
})
})
}

}
}

Expand Down
26 changes: 25 additions & 1 deletion test/test.js
@@ -1,4 +1,5 @@
var pusage = require('../').stat
, platform = require('os').platform()
, expect = require('chai').expect

//classic "drop somewhere"... yeah I'm a lazy guy
Expand Down Expand Up @@ -57,4 +58,27 @@ describe('pid usage', function() {
})
}, 2000)
})
})


var isWindows = platform.match(/^win/);

it('should get extra process information under WINDOWS os', isWindows ? function(cb) {
pusage(process.pid, {extra : ['Virtual Bytes']}, function(err, stat) {

expect(err).to.be.null
expect(stat).to.be.an('object')
expect(stat).to.have.property('cpu')
expect(stat).to.have.property('memory')
expect(stat).to.have.property('all')
expect(stat.all).to.be.an('object')
expect(stat.all).to.have.property('Virtual Bytes')

console.log('Pcpu: %s', stat.cpu)
console.log('Mem: %s', formatBytes(stat.memory))
console.log('Extra: ', stat.all)

cb()
})
} : undefined)
});