Skip to content

Commit

Permalink
Merge pull request hapijs#959 from wpreul/dtrace
Browse files Browse the repository at this point in the history
Adding foundation for dtrace probe support
  • Loading branch information
Eran Hammer committed Jul 18, 2013
2 parents 52d3686 + 6515b8d commit 7891702
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 8 deletions.
81 changes: 81 additions & 0 deletions lib/dtrace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Load modules

var Utils = require('./utils');
// dtrace-provider loaded inline when installed


// Declare internals

var internals = {};


module.exports = internals.DTrace = function (name) {

Utils.assert(this.constructor === internals.DTrace, 'DTrace must be instantiated using new');

if (!internals.DTrace.isInstalled()) {
this.report = function () {};
return;
}

this._probes = {};
this._dtrace = new internals.DTrace.Provider(name);
};


internals.DTrace.prototype.report = function (key/* arg1, arg2 */) {

var args = Array.prototype.slice.call(arguments, 1);
var probe = this._probes[key] || this._addProbe(key, args); // If probe not found create and add it

probe.fire(function () {

return args;
});
};


internals.DTrace.prototype._addProbe = function (key, values) {

var paramTypes = [];
for (var i = 0, il = values.length; i < il; ++i) {
var value = values[i];
if (typeof value === 'number') {
paramTypes.push('int');
}
else if (value !== null && typeof value === 'object') {
paramTypes.push('json');
}
else {
paramTypes.push('char *');
}
}

var probe = this._dtrace.addProbe.apply(this._dtrace, [key].concat(paramTypes));
this._probes[key] = probe;

this._dtrace.disable(); // Provider must be disabled/enabled for probe to be visible
this._dtrace.enable();

return probe;
};


internals.DTrace.isInstalled = function () {

var result = false;
try {
result = !!require.resolve('dtrace-provider');
}
catch (err) {}

internals.DTrace.isInstalled = function () {

return result;
};

return result;
};


internals.DTrace.Provider = internals.DTrace.isInstalled() && require('dtrace-provider').DTraceProvider;
2 changes: 2 additions & 0 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,11 +436,13 @@ internals.Request.bindPre = function (pre) {
request.pre[pre.assign] = result;
}

request.server._dtrace.report('pre.end', pre.assign, result);
return exit();
};

enter(function () {

request.server._dtrace.report('pre.start', pre.assign);
pre.method(request, finalize);
});
});
Expand Down
4 changes: 3 additions & 1 deletion lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var Boom = require('boom');
var Catbox = require('catbox');
var Auth = require('./auth');
var Defaults = require('./defaults');
var DTrace = require('./dtrace');
var Request = require('./request');
var Router = require('./router');
var Schema = require('./schema');
Expand Down Expand Up @@ -83,6 +84,7 @@ module.exports = internals.Server = function (/* host, port, options */) {
this._router = new Router(this);
this._ext = new Ext();
this._stateDefinitions = {};
this._dtrace = new DTrace('' + this._port);

if (args.pack) {
this.pack = args.pack;
Expand Down Expand Up @@ -246,7 +248,7 @@ internals.Server.prototype._stop = function (options, callback) {
}, options.timeout);

self.listener.close(function () {

self.listener.removeAllListeners();
clearTimeout(timeoutId);
callback();
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
"request": "2.22.x",
"handlebars": "1.0.x",
"jade": "0.33.x",
"hapi-plugin-test": "1.x.x"
"hapi-plugin-test": "1.x.x",
"dtrace-provider": "0.2.x"
},
"bin": {
"hapi": "./bin/hapi"
Expand Down
212 changes: 212 additions & 0 deletions test/integration/dtrace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
// Load modules

var Lab = require('lab');
var Hapi = require('../..');
var DTrace = require('../../lib/dtrace');


// Declare internals

var internals = {};


// Test shortcuts

var expect = Lab.expect;
var before = Lab.before;
var after = Lab.after;
var describe = Lab.experiment;
var it = Lab.test;


describe('DTrace', function () {

it('doesn\'t fire probes when dtrace provider isn\'t installed', function (done) {

var provider = DTrace.Provider;
var isInstalled = DTrace.isInstalled;
DTrace.Provider = function () {

return {
enable: function () {},
disable: function () {},
addProbe: function () {

return {
fire: function (fn) {

expect(fn).to.not.exist;
}
};
}
};
};
DTrace.isInstalled = function () {

return false;
};

var server = new Hapi.Server();


var pre1 = function (request, next) {

next('Hello');
};

server.route({ method: '*', path: '/', config: {
handler: function () {

this.reply('OK');
},
pre: [
{ method: pre1, assign: 'm1' }
]
}});

server.inject({ url: '/' }, function () {

DTrace.Provider = provider;
DTrace.isInstalled = isInstalled;
done();
});
});

it('fires correct probe on prerequisites when dtrace-provider is installed', function (done) {

var provider = DTrace.Provider;
var isInstalled = DTrace.isInstalled;
DTrace.Provider = function () {

return {
enable: function () {},
disable: function () {},
addProbe: function () {

return {
fire: function (fn) {

expect(fn()).to.contain('m1');
DTrace.Provider = provider;
DTrace.isInstalled = isInstalled;
done();
}
};
}
};
};
DTrace.isInstalled = function () {

return true;
};

var server = new Hapi.Server();
var pre1 = function (request, next) {

next('Hello');
};

server.route({ method: '*', path: '/', config: {
handler: function () {

this.reply('OK');
},
pre: [
{ method: pre1, assign: 'm1' }
]
}});

server.inject({ url: '/' }, function () {});
});

it('allows probes to be added dynamically', function (done) {

var runNum = 0;
var provider = DTrace.Provider;
DTrace.Provider = function () {

return {
enable: function () {},
disable: function () {},
addProbe: function () {

return {
fire: function (fn) {

if (runNum++ === 0) {
expect(fn()).to.contain(20);
expect(fn()).to.contain('some value');
}
else {
expect(fn()).to.contain(1);
expect(fn()).to.contain('3');
}
}
};
}
};
};
var server = new Hapi.Server();

server.route({ method: '*', path: '/', config: {
handler: function () {

this.server._dtrace.report('my.handler.start', 20, 'some value');
this.reply('OK');
this.server._dtrace.report('my.handler.end', 1, '3');
}
}});

server.inject({ url: '/' }, function () {
DTrace.Provider = provider;
done();
});
});

it('probes add the correct data types', function (done) {

var provider = DTrace.Provider;
DTrace.Provider = function () {

return {
enable: function () {},
disable: function () {},
addProbe: function (key, val1, val2, val3) {

expect(key).to.equal('my.probe');
expect(val1).to.equal('int');
expect(val2).to.equal('char *');
expect(val3).to.equal('json');
DTrace.Provider = provider;
done();

return {
fire: function () {}
};
}
};
};
var server = new Hapi.Server();
server._dtrace.report('my.probe', 20, 'some value', { some: 'obj' });
});

it('allows probes to be added dynamically with the dtrace-provider installed', function (done) {

var server = new Hapi.Server();

server.route({ method: '*', path: '/', config: {
handler: function () {

this.server._dtrace.report('my.handler.start', 20, ['some value', 1]);
this.reply('OK');
this.server._dtrace.report('my.handler.end', 1, '3');
}
}});

server.inject({ url: '/' }, function (res) {

expect(res.statusCode).to.equal(200);
done();
});
});
});
14 changes: 8 additions & 6 deletions test/integration/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ describe('Server', function () {

it('won\'t stop until all connections are closed', function (done) {

var isDone = false;
var server = Hapi.createServer(0);
server.start(function () {

Expand All @@ -57,7 +58,8 @@ describe('Server', function () {
server.listener.getConnections(function (err, count) {

expect(count).to.equal(0);
done();
!isDone && done();
isDone = true;
});
});

Expand Down Expand Up @@ -154,18 +156,18 @@ describe('Server', function () {
});
});
});

it('removes connection event listeners after it stops', function (done) {

var server = Hapi.createServer(0);
server.start(function () {

server.stop(function () {

server.start(function () {

server.stop(function () {

expect(server.listeners('connection').length).to.be.eql(0);
done();
});
Expand Down

0 comments on commit 7891702

Please sign in to comment.