Last active
June 22, 2016 18:42
-
-
Save mike-kaufman/eac110b376a8b8846bcd2a65cac1518c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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