Skip to content

Instantly share code, notes, and snippets.

@mike-kaufman
Last active June 22, 2016 18:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mike-kaufman/eac110b376a8b8846bcd2a65cac1518c to your computer and use it in GitHub Desktop.
Save mike-kaufman/eac110b376a8b8846bcd2a65cac1518c to your computer and use it in GitHub Desktop.
'use strict';
var fs = require('fs');
var os = require('os');
var asyncWrap = process.binding('async_wrap');
const deferment = 'deferment';
const invocation = 'invocation';
const providerIDToName = {};
// build table to map async provider ID to name
for (var p in asyncWrap.Providers) {
if (asyncWrap.Providers.hasOwnProperty(p)) {
providerIDToName[asyncWrap.Providers[p]] = p;
}
}
function safeWrite(s) {
process._rawDebug(s);
}
const nodes = [];
const edges = [];
const invocationCounts = {};
let currentId = '0';
function addNode(id, type, properties) {
const node = { name: id, type, properties};
nodes.push(node);
}
function addEdge(source, target) {
edges.push({ source, target });
}
function getDefermentNodeId(id) {
return deferment + '_' + id;
}
function getInvocationNodeId(id, invocationCount) {
return `${invocation}_${id}_${invocationCount}`;
}
function getProviderName(type) {
if (providerIDToName.hasOwnProperty(type)) {
return providerIDToName[type];
}
return type;
}
function initialize() {
addNode('0', 'root');
asyncWrap.disable();
asyncWrap.setupHooks(
{
init: function init(id, type, parentId) {
const id2 = deferment + '_' + id;
addNode(id2, deferment, {id, parentId, providerType: type, providerName: getProviderName(type)});
addEdge(currentId, id2);
},
pre: function pre(id) {
var ic = 0;
if (!invocationCounts.hasOwnProperty(id)) {
invocationCounts[id] = 0;
}
else {
ic = ++invocationCounts[id];
}
currentId = getInvocationNodeId(id, ic);
addNode(currentId, invocation, {id});
addEdge(getDefermentNodeId(id), currentId);
},
post: function post(id) {
currentId = '0';
},
destroy: function destroy(id) {
// no more invocations possible after this point.
delete invocationCounts[id];
}
});
asyncWrap.enable();
}
function writeGraph(outputFileName) {
asyncWrap.disable();
outputFileName = outputFileName || './graph';
//writeDGML(outputFileName);
writeDOT(outputFileName);
asyncWrap.enable()
}
// function getDGMLColor(type) {
// if (type == 'root') {
// return '#ffffff'; // white
// }
// else if (type === deferment) {
// return '#ff0000'; // red
// }
// else if (type === invocation) {
// return '#0000ff'; // blue
// }
// else {
// return '##ffffff'; // white
// }
// }
// function writeDGML(outputFileName) {
// var xml = `<?xml version='1.0' encoding='utf-8'?>
// <DirectedGraph xmlns="http://schemas.microsoft.com/vs/2009/dgml">
// <Nodes>
// `;
// for (let i = 0; i < nodes.length; i++) {
// let color = getDGMLColor(nodes[i].type)
// xml += ` <Node Id="${nodes[i].name}" Label="${nodes[i].name}" Size="10" Background="${color}" `;
// if (nodes[i].provider) {
// xml = xml + `Provider="${nodes[i].provider}" `
// }
// xml += '/>';
// xml += os.EOL;
// }
// xml += `
// </Nodes>
// <Links>
// `;
// for (let i = 0; i < edges.length; i++) {
// xml = xml + ` <Link Source="${edges[i].source}" Target="${edges[i].target}" />`;
// xml = xml + os.EOL;
// }
// xml = xml + `
// </Links>
// <Properties>
// <Property Id="Background" Label="Background" DataType="Brush" />
// <Property Id="Label" Label="Label" DataType="String" />
// <Property Id="Size" DataType="String" />
// <Property Id="Start" DataType="DateTime" />
// </Properties>
// </DirectedGraph>
// `;
// fs.writeFileSync(outputFileName + '.dgml', xml, 'utf8');
// }
function getShapeForDOT(node) {
if (node.type === 'root') {
return 'square';
}
else if (node.type === deferment) {
return 'record';
}
else if (node.type === invocation) {
return 'record';
}
else {
return 'circle';
}
}
function getLabelForDOT(node) {
if (node.type === deferment) {
let label = `{{${deferment} ${node.properties.id}}`;
label += `|{parentID|${node.properties.parentId}}`;
label += `|{providerType|${node.properties.providerType}}`;
label += `|{providerName|${node.properties.providerName}}`;
label += '}';
return label;
}
else if (node.type === invocation) {
let label = `{{${node.name}}`;
label += `|{${deferment} ID|${node.properties.id}}`;
label += '}';
return label;
}
else {
return node.name;
}
}
function getColorForDOT(node) {
if (node.type === 'root') {
return 'black';
}
else if (node.type === invocation) {
return 'red';
}
else if (node.type === deferment) {
return 'blue';
}
else {
return 'black';
}
}
function writeDOT(outputFileName) {
// See DOT format here https://en.wikipedia.org/wiki/DOT_(graph_description_language)
// See online visualizer here: http://sandbox.kidstrythisathome.com/erdos/
let contents = 'digraph asyncCallTree {';
for (let i = 0; i < nodes.length; i++) {
contents += os.EOL;
const shape = getShapeForDOT(nodes[i]);
const color = getColorForDOT(nodes[i]);
const label = getLabelForDOT(nodes[i]);
contents += `${nodes[i].name} [shape=${shape} label="${label}" color=${color}];`;
contents += os.EOL;
}
for (let i = 0; i < edges.length; i++) {
contents += os.EOL;
contents += `${edges[i].source} -> ${edges[i].target};`;
contents += os.EOL;
}
contents += '}';
fs.writeFileSync(outputFileName + '.dot', contents, 'utf8');
}
module.exports = {
initialize,
writeGraph,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment