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

[refactor] initial rewrite to use native async_hooks #19

Merged
merged 6 commits into from
May 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 158 additions & 110 deletions docs/example/dprof.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions docs/visualizer/visualizer.build.js
Original file line number Diff line number Diff line change
Expand Up @@ -18531,7 +18531,7 @@ function Flatten(data) {
var root = new Node(data.root);

// Construct map of all nodes
var nodes = new Map([[0, root]]);
var nodes = new Map([[1, root]]);
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
Expand Down Expand Up @@ -18694,6 +18694,7 @@ function Node(node) {

// Info
this.name = node.name;
this.uid = node.uid;
this.stack = node.stack;
this.initRef = node.initRef;

Expand Down Expand Up @@ -18942,7 +18943,9 @@ StatsLayout.prototype.draw = function () {
}
wait += this._node.destroy - prevSyncTime;

stats += '\n' + ('handle: ' + this._node.name + '\n') + ('uid: ' + this._node.uid + '\n') + ('start: ' + this._node.init.toFixed(8) + ' sec\n') + ('wait: ' + toms(wait, 11) + ' ms\n') + ('callback: ' + toms(callback, 7) + ' ms');
stats += '\n' + ('handle: ' + this._node.name + '\n') + ('uid: ' + this._node.uid + '\n') + (
// `weak (unrefed): ${this._node.unrefed}\n` +
'start: ' + this._node.init.toFixed(8) + ' sec\n') + ('wait: ' + toms(wait, 11) + ' ms\n') + ('callback: ' + toms(callback, 7) + ' ms');

trace += this._node.stack.map(function (site) {
return ' at ' + site.description;
Expand Down
93 changes: 56 additions & 37 deletions dprof.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const asyncHook = require('async-hook');
const asyncHook = require('async_hooks');

const chain = require('stack-chain');
const zlib = require('zlib');
const fs = require('fs');
Expand All @@ -14,9 +15,18 @@ if (process.execArgv.indexOf('--stack_trace_limit') === -1 && Error.stackTraceLi
}

//
// Define node class
// Setup hooks
//
const hooks = asyncHook.createHook({
init: asyncInit,
before: asyncBefore,
after: asyncAfter,
destroy: asyncDestroy
});

//
// Define node class
//
function Site(site) {
this.description = site.toString();
this.filename = site.getFileName();
Expand All @@ -29,12 +39,11 @@ function timestamp() {
return t[0] * 1e9 + t[1];
}

function Node(uid, handle, stack, parent) {
function Node(uid, handle, name, stack, parent) {
const self = this;

this.parent = parent === null ? null : parent.uid;
this.name = name;
this.uid = uid;
this.name = handle.constructor.name;
this._init = timestamp();
this._destroy = Infinity;
this._before = [];
Expand Down Expand Up @@ -81,8 +90,8 @@ function getCallSites(skip) {
return stack;
}

Node.prototype.add = function (uid, handle) {
const node = new Node(uid, handle, getCallSites(3), this);
Node.prototype.add = function (uid, handle, type) {
const node = new Node(uid, handle, type, getCallSites(3), this);
this.children.push(uid);
return node;
};
Expand Down Expand Up @@ -121,63 +130,73 @@ Node.prototype.rootIntialize = function () {
this._before.push(0);
};

//
// Setup hooks
//
asyncHook.addHooks({
init: asyncInit,
pre: asyncBefore,
post: asyncAfter,
destroy: asyncDestroy
});

const root = new Node(
0, { 'constructor': { name: 'root' } },
1,
{},
'root',
getCallSites(2),
null
);
root.rootIntialize();

const nodes = new Map();
const stateStack = [root];

function asyncInit(uid, handle, provider, parentUid) {
// get parent state
const topState = stateStack[stateStack.length - 1];
const state = (parentUid === null ? topState : nodes.get(parentUid));
// Setup the root: fake hook events
hooks.disable();
process.nextTick(function () {
root.after();
root.destroy();
});
hooks.enable();


function asyncInit(uid, type, triggerId, handle) {
process._rawDebug('init', {uid, type, triggerId});

// get initializing state
let state;
if (triggerId === 0 || triggerId === 1) {
// 1 is always root
// 0 is not root, but unknown. Use root for now.
state = root;
} else {
state = nodes.get(triggerId);
}

// add new state node
nodes.set(uid, state.add(uid, handle));
nodes.set(uid, state.add(uid, handle, type));
}

function asyncBefore(uid) {
// Ignore our nextTick for the root duration
if (!nodes.has(uid)) return;
process._rawDebug('before', {uid});

const state = nodes.get(uid);

state.before();
stateStack.push(state);
}

function asyncAfter(uid) {
// Ignore our nextTick for the root duration
if (!nodes.has(uid)) return;
process._rawDebug('after', {uid});

const state = nodes.get(uid);

state.after();
stateStack.pop();
}

function asyncDestroy(uid) {
// Ignore our nextTick for the root duration
if (!nodes.has(uid)) return;
process._rawDebug('destroy', {uid});

const state = nodes.get(uid);

state.destroy();
}

// The root job is done when process.nextTick is called
asyncHook.disable();
process.nextTick(function () {
root.after();
root.destroy();
});
asyncHook.enable();

//
// Print result
//
Expand All @@ -196,9 +215,9 @@ if (process.argv.indexOf('--dprof-no-sigint') === -1 &&
process.on('exit', writeDataFile);

function writeDataFile() {
// even though zlib is sync, it still fires async_wrap events,
// so disable asyncWrap just to be sure.
asyncHook.disable();
// even though zlib is sync, it still fires async_hook events,
// so disable the hooks just to be sure.
hooks.disable();

const data = {
'total': timestamp(),
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "dprof",
"description": "Dynamic/structured profiling & visualization for sync and async operations",
"version": "0.18.2",
"version": "1.0.0",
"author": "Andreas Madsen <[email protected]>",
"main": "./dprof.js",
"bin": {
Expand Down Expand Up @@ -29,13 +29,15 @@
"babelify": "^7.3.0",
"babel-preset-es2015": "^6.13.2",
"startpoint": "0.3.x",
"async-hook": "^1.6.0",
"stack-chain": "^1.3.7"
},
"devDependencies": {
"tap": "^6.3.2",
"pako": "^1.0.3",
"interpreted": "0.7.x"
},
"license": "MIT"
"license": "MIT",
"engines": {
"node": "^8.0"
}
}
Loading