Skip to content
This repository has been archived by the owner on Nov 28, 2023. It is now read-only.

Commit

Permalink
New: support for monitoring CPU used by a process
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartherbert committed Apr 15, 2013
1 parent b6bd586 commit e29c7ef
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 98 deletions.
9 changes: 5 additions & 4 deletions SavageD.sh
Expand Up @@ -140,10 +140,11 @@ function selfMonitor() {
# use CURL to monitor ourselves
#
# setup the alias first
# curl -X PUT http://localhost:8091/process/$proc_alias/pid -d "pid=$pid"
curl -X PUT http://localhost:8091/process/$proc_alias/pid -d "pid=$pid"

# activate all known process plugins
# curl -X PUT http://localhost:8091/process/$proc_alias/memory
curl -X PUT http://localhost:8091/process/$proc_alias/memory
curl -X PUT http://localhost:8091/process/$proc_alias/cpu

# activate all known server plugins
curl -X PUT http://localhost:8091/server/$serv_alias/loadavg
Expand Down Expand Up @@ -177,10 +178,10 @@ function stopSelfMonitor() {
# use CURL to stop monitor ourselves
#
# setup the alias first
# curl -X PUT http://localhost:8091/process/$proc_alias/pid -d "pid=$pid"
curl -X PUT http://localhost:8091/process/$proc_alias/pid -d "pid=$pid"

# activate all known process plugins
# curl -X PUT http://localhost:8091/process/$proc_alias/memory
curl -X PUT http://localhost:8091/process/$proc_alias/memory

# activate all known server plugins
curl -X DELETE http://localhost:8091/server/$serv_alias/loadavg
Expand Down
170 changes: 82 additions & 88 deletions app/features/ProcessCpu.js
Expand Up @@ -9,48 +9,36 @@ var util = require("util");
var _ = require("underscore");
var dsCommon = require("dsCommon");

// our parsers
var ServerStatParser = require("../parsers/ServerStatParser");
var ProcessStatParser = require("../parsers/ProcessStatParser");

function ProcessCpu(appServer) {
// call our parent constructor
ProcessCpu.super_.call(this, appServer, {
name: "ProcessCpu"
});

// our cached stats
this.cpuStats = {};
// our list of stats that we've seen before, for sampling purposes
this.stats = {};

// add ourselves to the list of available plugins
appServer.processCpu.addPlugin("memory", this);
appServer.processMonitor.addPlugin("cpu", this);
}
module.exports = ProcessCpu;
util.inherits(ProcessCpu, dsCommon.dsFeature);

ProcessCpu.prototype.canMonitorPid = function(pid) {
// does the pid refer to an existing process?
if (!fs.existsSync("/proc/" + pid + "/status")) {
return false;
}

// are we currently monitoring the server CPU stats?
//

return true;
};

ProcessCpu.prototype.getCpuStats = function(pid) {
// self-reference
var self = this;

// where are we getting the data from?
var filename = "/proc/" + pid + "/stat";

// this will hold the processed contents of the stat file
var results = {};

// this will hold the raw contents of the status file
var content = fs.readFileSync(filename, ascii);

// let's split up the file
var parsed = content.split(/\s/);
ProcessCpu.prototype.getFilenamesToMonitor = function(pid) {
return [
{
filename: "/proc/stat",
parser: new ServerStatParser()
},
{
filename: "/proc/" + pid + "/stat",
parser: new ProcessStatParser()
}
];
};

ProcessCpu.prototype.reportUsage = function(pid, alias) {
Expand All @@ -60,72 +48,78 @@ ProcessCpu.prototype.reportUsage = function(pid, alias) {
// what are we doing?
// this.logInfo("report cpu usage of PID " + pid + " as alias " + alias);

// get the server CPU data first
var serverCpuStats = self.appServer.getLatestDataFor("/proc/stat");

// console.log(serverCpuStats.diff);

// we can get the information we need from the process's status file
var filename = "/proc/" + pid + "/status";
var filename = "/proc/" + pid + "/stat";

// this will hold the raw contents of the status file
var content = "";
// get the process CPU data now
var processStats = self.appServer.getLatestDataFor(filename);

// this will hold the processed contents of the status file
var status = {};
// we need to sample the data to work out CPU usage
if (this.stats[filename] === undefined) {
// remember the stats for next time
this.stats[filename] = { raw: extractedCpuStats, diff: {}, percentages: {} };

// does the path exist?
if (!fs.existsSync(filename)) {
throw new Error("Cannot find file " + filename + " for process ID " + pid);
// can't do anything else yet
return;
}

content = fs.readFileSync(filename, "ascii");

// extract the values that we want
_.each(content.split("\n"), function(line) {
// peak size of the virtual memory of the process
if (line.match(/^VmPeak/)) {
status.vmTotalPeak = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current total size of the virtual memory of the process
if (line.match(/^VmSize/)) {
status.vmCurrentSize = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// total amount of 'locked' memory
if (line.match(/^VmLck/)) {
status.vmLocked = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// high-water mark for resident set size
if (line.match(/^VmHWM/)) {
status.vmRssPeak = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current resident set size
if (line.match(/^VmRSS/)) {
status.vmCurrentRss = parseInt(line.split(/\s+/)[1], 10) * 1024;
// how many total CPU jiffies have occurred, on a single CPU?
var totalJiffies = 0;
_.each(serverCpuStats.diff, function(cpu, cpuName) {
if (cpuName !== 'cpu') {
if (cpu.total > totalJiffies) {
totalJiffies = cpu.total;
}
}
// current data segment size
if (line.match(/^VmData/)) {
status.vmData = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current stack size
if (line.match(/^VmStk/)) {
status.vmStack = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current code pages size
if (line.match(/^VmExe/)) {
status.vmExe = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current library size
if (line.match(/^VmLib/)) {
status.vmLib = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current page table entries size
if (line.match(/^VmPTE/)) {
status.vmPTE = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
// current swap usage
if (line.match(/^VmSwap/)) {
status.vmSwap = parseInt(line.split(/\s+/)[1], 10) * 1024;
}
}, this);
});

// extract just the CPU data that we want
var extractedCpuStats = {
user: processStats.utime,
system: processStats.stime
};

// work out how much CPU the process has used this interval
var processDiff = this.diffCpuStats(this.stats[filename].raw, extractedCpuStats);

// convert the diff into a percentage
var percentageStats = this.statsToPercent(processDiff, totalJiffies);

// remember the stats for next time
this.stats[filename] = { raw: extractedCpuStats, diff: processDiff, percentages: percentageStats };

// at this point, we have data to send to statsd
_.each(status, function(value, name) {
self.appServer.statsManager.count(alias + "." + name, value);
_.each(percentageStats, function(value, name) {
self.appServer.statsManager.count(alias + ".cpu." + name, value);
});
};

ProcessCpu.prototype.diffCpuStats = function(oldStats, newStats) {
var results = {};

// work out the number of jiffies that have occured
// between our two sample points
_.each(oldStats, function(value, fieldName) {
results[fieldName] = newStats[fieldName] - value;
});

// all done
return results;
};

ProcessCpu.prototype.statsToPercent = function(stats, totalJiffies) {
var results = {};

_.each(stats, function(value, fieldName) {
// calculate the percentage, to 2 decimal places
results[fieldName] = Math.round((parseFloat(value) / parseFloat(totalJiffies)) * 10000.0) / 100.0;
});

// all done
return results;
};
Expand Up @@ -2,20 +2,18 @@
// All rights reserved

var fs = require("fs");
var _ = require("underscore");

function PidStatParser()
{
// does nothing
}
module.exports = PidStatParser;

PidStatParser.prototype.getStats = function(pid) {
PidStatParser.prototype.retrieveStats = function(filename) {
// self-reference
var self = this;

// where are we getting the data from?
var filename = "/proc/" + pid + "/stat";

// does the file exist?
if (!fs.existsSync(filename)) {
throw new Error("cannot find file " + filename + " for parsing");
Expand All @@ -24,8 +22,8 @@ PidStatParser.prototype.getStats = function(pid) {
// this will hold the processed contents of the stat file
var results = {};

// this will hold the raw contents of the status file
var content = fs.readFileSync(filename, ascii);
// this will hold the raw contents of the stat file
var content = fs.readFileSync(filename, "ascii");

// let's split up the file
var parsed = content.split(/\s/);
Expand Down
1 change: 1 addition & 0 deletions config.js
@@ -1,5 +1,6 @@
{
"features": {
"ProcessCpu": {},
"ProcessMemory": {},
"ServerCpu": {},
"ServerLoadavg": {}
Expand Down

0 comments on commit e29c7ef

Please sign in to comment.