var readable = new Readability();
readable.setSkipLevel(3);
saxParser(document.childNodes[document.childNodes.length-1], readable);
article = readable.getArticle();

document.body.innerHTML = "<h1>" + article.title + "<\/h1><article>" + article.html + "<\/article><hr/><p>" + article.score + "<\/p>";

Following resources are loaded into result:

  1. readabilitySAX.js
  2. DOMasSAX.js
<div id="container"><div id="header"><a href="/"><h1>How To Node</h1><h2 class="tagline">The zen of coding in node.JS</h2></a></div><div class="clearfix" id="main">
    <h1>Realtime Performance Visualizations using Node.js</h1>
    <p>This article outlines how to create a realtime heatmap of your syscall latency using HTML5, some great node modules, and DTrace. It was inspired by talk that Bryan Cantrill and Brendan Greg gave on Joyent's cool cloud analytics tools. While specific, the code provided could easily be adapted to provide a heatmap of any type of aggregation Dtrace is capable of providing. </p>
    
    <h2>System Requirements</h2>
    
    <p>First thing's first, you're going to need a system with DTrace. This likely means Solaris (or one of its decedents), OS X, or a BSD variant.  There doesn't appear to be Dtrace available for Linux. </p>
    
    <h2>Security</h2>
    
    <p>Secondly, please be aware that at the time of writing the demo code contains a fairly substantial secruity vulnerabilty. Namely the d script is sent from the client with no authentication what so ever. If you bind to localhost this shouldn't be a big deal for a demo. Time permitting I intend to clean up the code.  </p>
    
    <h2>Dependencies</h2>
    
    <p>For this tutorial you'll also need:</p>
    
    <pre><code><span class="pln">node </span><span class="pun">-</span><span class="pln"> http</span><span class="pun">:</span><span class="com">//nodejs.org/#download (duh)</span><span class="pln"><br />npm </span><span class="pun">-</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//github.com/isaacs/npm (makes installing modules a breeze)</span><span class="pln"><br />node</span><span class="pun">-</span><span class="pln">libdtrace </span><span class="pun">-</span><span class="pln"> https</span><span class="pun">:</span><span class="com">//github.com/bcantrill/node-libdtrace (provides dtrace functionality)</span><span class="pln"><br /></span><span class="typ">Socket</span><span class="pun">.</span><span class="pln">IO </span><span class="pun">-</span><span class="pln"> </span><span class="str">'npm install socket.io'</span><span class="pln"> </span><span class="pun">(</span><span class="pln">web sockets made easy</span><span class="pun">)</span><span class="pln"><br /></span></code></pre>
    
    <h2>Server</h2>
    
    <p>Now we're ready to start writing our web server: </p>
    
    <div class="snippet"><a href="heat-tracer/heat_tracer.js" class="code-link">heat_tracer.js</a><pre><code><span class="kwd">var</span><span class="pln"> http </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'http'</span><span class="pun">);</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> libdtrace </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'libdtrace'</span><span class="pun">);</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> io </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'socket.io'</span><span class="pun">);</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> express </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'express'</span><span class="pun">);</span><span class="pln"><br /><br /></span><span class="com">/* create our express server and prepare to serve javascript files in ./public <br />*/</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> app </span><span class="pun">=</span><span class="pln"> express</span><span class="pun">.</span><span class="pln">createServer</span><span class="pun">();</span><span class="pln"><br />app</span><span class="pun">.</span><span class="pln">configure</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(){</span><span class="pln"><br />&nbsp; &nbsp; app</span><span class="pun">.</span><span class="kwd">use</span><span class="pun">(</span><span class="pln">express</span><span class="pun">.</span><span class="pln">staticProvider</span><span class="pun">(</span><span class="pln">__dirname </span><span class="pun">+</span><span class="pln"> </span><span class="str">'/public'</span><span class="pun">));</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">});</span><span class="pln"><br /><br /><br /></span><span class="com">/* Before we go any further we must realize that each time a user connects we're going to want to <br />&nbsp; &nbsp;them send them dtrace aggregation every second. We can do so using 'setInterval', but we must<br />&nbsp; &nbsp;keep track of both the intervals we set and the dtrace consumers that are created as we'll need <br />&nbsp; &nbsp;them later when the client disconnects. <br />*/</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> interval_id_by_session_id </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> dtp_by_session_id </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln"><br /><br /></span><span class="com">/* In order to effecienctly send packets we're going to use the Socket.IO library which seemlessly <br />&nbsp; &nbsp;integrates with express. &nbsp;<br />*/</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> websocket_server </span><span class="pun">=</span><span class="pln"> io</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">app</span><span class="pun">);</span><span class="pln"> <br /><br /></span><span class="com">/* Now that we have a web socket server, we need to create a handler for connection events. These <br />&nbsp; &nbsp;events represet a client connecting to our server */</span><span class="pln"><br />websocket_server</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connection'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">socket</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> <br /><br />&nbsp; &nbsp; </span><span class="com">/* Like the web server object, we must also define handlers for various socket events that <br />&nbsp; &nbsp; &nbsp; &nbsp;will happen during the lifetime of the connection. These will define how we interact with<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;the client. The first is a message event which occurs when the client sends something to<br />&nbsp; &nbsp; &nbsp; &nbsp;the server. */</span><span class="pln"><br />&nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="pln"> </span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="com">/* The only message the client ever sends will be sent right after connecting. &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;So it will happen only once during the lifetime of a socket. This message also <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;contains a d script which defines an agregation to walk. <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;*/</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> dtp </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> libdtrace</span><span class="pun">.</span><span class="typ">Consumer</span><span class="pun">();</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> dscript </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">[</span><span class="str">'dscript'</span><span class="pun">];</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln"> dscript </span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; dtp</span><span class="pun">.</span><span class="pln">strcompile</span><span class="pun">(</span><span class="pln">dscript</span><span class="pun">);</span><span class="pln"> &nbsp; &nbsp; &nbsp; &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; dtp</span><span class="pun">.</span><span class="pln">go</span><span class="pun">();</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; dtp_by_session_id</span><span class="pun">[</span><span class="pln">socket</span><span class="pun">.</span><span class="pln">sessionId</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> dtp</span><span class="pun">;</span><span class="pln"><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><span class="com">/* All that's left to do is send the aggration data from the dscript. &nbsp;*/</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;interval_id_by_session_id</span><span class="pun">[</span><span class="pln">socket</span><span class="pun">.</span><span class="pln">sessionId</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> setInterval</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><span class="kwd">var</span><span class="pln"> aggdata </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{};</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dtp</span><span class="pun">.</span><span class="pln">aggwalk</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> key</span><span class="pun">,</span><span class="pln"> val</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><span class="kwd">for</span><span class="pun">(</span><span class="pln"> index </span><span class="kwd">in</span><span class="pln"> val </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><span class="com">/* console.log( 'key: ' + key + ', interval: ' + <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; val[index][0][0] + '-' + val[index][0][1], ', count ' + val[index][1] ); */</span><span class="pln"><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aggdata</span><span class="pun">[</span><span class="pln">key</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> val</span><span class="pun">;</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln"> aggdata </span><span class="pun">);</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">catch</span><span class="pun">(</span><span class="pln"> err </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">},</span><span class="pln"> &nbsp;</span><span class="lit">1001</span><span class="pln"> </span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span><span class="pln"><br /><br /><br />&nbsp; &nbsp; </span><span class="com">/* Not so fast. If a client disconnects we don't want their respective dtrace consumer to <br />&nbsp; &nbsp; &nbsp; &nbsp;keep collecting data any more. We also don't want to try to keep sending anything to them<br />&nbsp; &nbsp; &nbsp; &nbsp;period. So clean up. */</span><span class="pln"><br />&nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'disconnect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; clearInterval</span><span class="pun">(</span><span class="pln">clearInterval</span><span class="pun">(</span><span class="pln">interval_id_by_session_id</span><span class="pun">[</span><span class="pln">socket</span><span class="pun">.</span><span class="pln">sessionId</span><span class="pun">]));</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> dtp </span><span class="pun">=</span><span class="pln"> dtp_by_session_id</span><span class="pun">[</span><span class="pln">socket</span><span class="pun">.</span><span class="pln">sessionId</span><span class="pun">];</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">delete</span><span class="pln"> dtp_by_session_id</span><span class="pun">[</span><span class="pln">socket</span><span class="pun">.</span><span class="pln">sessionId</span><span class="pun">];</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; dtp</span><span class="pun">.</span><span class="pln">stop</span><span class="pun">();</span><span class="pln"> &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'disconnected'</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">});</span><span class="pln"><br /><br /><br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span><span class="pln"><br /><br /><br />app</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="lit">80</span><span class="pun">);</span></code></pre></div>
    
    <h2>Client</h2>
    
    <p>In order to display our heatmap, we're going to need some basic HTML with a canvas element:</p>
    
    <div class="snippet"><a href="heat-tracer/public/heat_tracer.html" class="code-link">public/heat_tracer.html</a><pre><code><span class="tag">&lt;html&gt;</span><span class="pln"><br /></span><span class="tag">&lt;head&gt;</span><span class="pln"><br /></span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"http://localhost/socket.io/socket.io.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln"> <br /></span><span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"http://localhost/heat_tracer_client.js"</span><span class="tag">&gt;&lt;/script&gt;</span><span class="pln"> <br /></span><span class="tag">&lt;/head&gt;</span><span class="pln"><br /></span><span class="tag">&lt;body</span><span class="pln"> </span><span class="atn">onLoad</span><span class="pun">=</span><span class="atv">'</span><span class="pln">heat_tracer</span><span class="pun">()</span><span class="atv">'</span><span class="tag">&gt;</span><span class="pln"><br /></span><span class="tag">&lt;canvas</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">'canvas'</span><span class="pln"> </span><span class="atn">width</span><span class="pun">=</span><span class="atv">'1024'</span><span class="pln"> </span><span class="atn">height</span><span class="pun">=</span><span class="atv">'512'</span><span class="tag">&gt;&lt;/canvas&gt;</span><span class="pln"><br /></span><span class="tag">&lt;/body&gt;</span><span class="pln"><br /></span><span class="tag">&lt;/html&gt;</span></code></pre></div>
    
    <p>Finally the JavaScript client which translates the raw  streaming data into pretty picture:</p>
    
    <div class="snippet"><a href="heat-tracer/public/heat_tracer_client.js" class="code-link">public/heat_tracer_client.js</a><pre><code><span class="com">/* On load we create our web socket (or flash socket if your browser doesn't support it ) and<br />&nbsp; &nbsp;send the d script we wish to be tracing. This extremely powerful and *insecure*. */</span><span class="pln"><br /></span><span class="kwd">function</span><span class="pln"> heat_tracer</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> <br /><br />&nbsp; &nbsp; </span><span class="com">//Global vars</span><span class="pln"><br />&nbsp; &nbsp; setup</span><span class="pun">();</span><span class="pln"><br /><br />&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> socket </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> io</span><span class="pun">.</span><span class="typ">Socket</span><span class="pun">(</span><span class="str">'localhost'</span><span class="pun">);</span><span class="pln"> </span><span class="com">//connect to localhost presently</span><span class="pln"><br />&nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">connect</span><span class="pun">();</span><span class="pln"><br /><br />&nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'on connection'</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> dscript </span><span class="pun">=</span><span class="pln"> </span><span class="str">"syscall:::entry\n{\nself-&gt;syscall_entry_ts[probefunc] = vtimestamp;\n}\nsyscall:::return\n/self-&gt;syscall_entry_ts[probefunc]/\n{\n\n@time[probefunc] = lquantize((vtimestamp - self-&gt;syscall_entry_ts[probefunc] ) / 1000, 0, 63, 2);\nself-&gt;syscall_entry_ts[probefunc] = 0;\n}"</span><span class="pun">;</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">send</span><span class="pun">(</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="str">'dscript'</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> dscript </span><span class="pun">}</span><span class="pln"> </span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">});</span><span class="pln"><br /><br /><br />&nbsp; &nbsp; </span><span class="com">/* The only messages we recieve should contain contain the dtrace aggregation data we requested<br />&nbsp; &nbsp; &nbsp; &nbsp;on connection. */</span><span class="pln"><br />&nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'message'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">message</span><span class="pun">){</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="com">//console.log( message );</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; draw</span><span class="pun">(</span><span class="pln">message</span><span class="pun">);</span><span class="pln"><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="com">/* for ( key in message ) {<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;val = message[key];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;console.log( 'key: ' + key + ', interval: ' + val[0][0] + '-' + val[0][1], ', count ' + val[1] );<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; */</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">});</span><span class="pln"><br /><br />&nbsp; &nbsp; socket</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'disconnect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){</span><span class="pln"> <br />&nbsp; &nbsp; </span><span class="pun">});</span><span class="pln"><br /><br /></span><span class="pun">}</span><span class="pln"><br /><br /><br /></span><span class="com">/* Take the aggregation data and update the heatmap */</span><span class="pln"><br /></span><span class="kwd">function</span><span class="pln"> draw</span><span class="pun">(</span><span class="pln">message</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> &nbsp;<br /><br />&nbsp; &nbsp; </span><span class="com">/* Latest data goes in the right most column, initialize it */</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> syscalls_by_latency </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> index </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">32</span><span class="pun">;</span><span class="pln"> index</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; syscalls_by_latency</span><span class="pun">[</span><span class="pln">index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br /><br />&nbsp; &nbsp; </span><span class="com">/* Presently we have the latency for each system call quantized in our message. Merge the data<br />&nbsp; &nbsp; &nbsp; &nbsp;such that we have all the system call latency quantized together. This gives us the number<br />&nbsp; &nbsp; &nbsp; &nbsp;of syscalls made with latencies in each particular band. */</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> syscall </span><span class="kwd">in</span><span class="pln"> message </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> val </span><span class="pun">=</span><span class="pln"> message</span><span class="pun">[</span><span class="pln">syscall</span><span class="pun">];</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> result_index </span><span class="kwd">in</span><span class="pln"> val </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> latency_start </span><span class="pun">=</span><span class="pln"> val</span><span class="pun">[</span><span class="pln">result_index</span><span class="pun">][</span><span class="lit">0</span><span class="pun">][</span><span class="lit">0</span><span class="pun">];</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> count </span><span class="pun">=</span><span class="pln"> &nbsp;val</span><span class="pun">[</span><span class="pln">result_index</span><span class="pun">][</span><span class="lit">1</span><span class="pun">];</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="com">/* The d script we're using lquantizes from 0 to 63 in steps of two. So dividing by 2 <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tells us which row this result belongs in */</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; syscalls_by_latency</span><span class="pun">[</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="pln">latency_start</span><span class="pun">/</span><span class="lit">2</span><span class="pun">)]</span><span class="pln"> </span><span class="pun">+=</span><span class="pln"> count</span><span class="pun">;</span><span class="pln"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br /><br /><br />&nbsp; &nbsp; </span><span class="com">/* We just created a new column, shift the console to the left and add it. */</span><span class="pln"><br />&nbsp; &nbsp; console_columns</span><span class="pun">.</span><span class="pln">shift</span><span class="pun">();</span><span class="pln"><br />&nbsp; &nbsp; console_columns</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">syscalls_by_latency</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; drawArray</span><span class="pun">(</span><span class="pln">console_columns</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">}</span><span class="pln"><br /><br /><br /><br /></span><span class="com">/* Draw the columns and rows that map up the heatmap on to the canvas element */</span><span class="pln"><br /></span><span class="kwd">function</span><span class="pln"> drawArray</span><span class="pun">(</span><span class="pln">console_columns</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> canvas </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">'canvas'</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">canvas</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> ctx </span><span class="pun">=</span><span class="pln"> canvas</span><span class="pun">.</span><span class="pln">getContext</span><span class="pun">(</span><span class="str">'2d'</span><span class="pun">);</span><span class="pln"> &nbsp;<br />&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> column_index </span><span class="kwd">in</span><span class="pln"> console_columns </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> console_columns</span><span class="pun">[</span><span class="pln">column_index</span><span class="pun">];</span><span class="pln"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> entry_index </span><span class="kwd">in</span><span class="pln"> column </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; entry </span><span class="pun">=</span><span class="pln"> column</span><span class="pun">[</span><span class="pln">entry_index</span><span class="pun">];</span><span class="pln"><br /><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="com">/* We're using a logarithmic scale for the brightness. This was all arrived at by<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;trial and error and found to work well on my Mac. &nbsp;In the future this <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;could all be adjustable with controls */</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> red_value </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> entry </span><span class="pun">!=</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; red_value </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Math</span><span class="pun">.</span><span class="pln">floor</span><span class="pun">(</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">entry</span><span class="pun">)/</span><span class="typ">Math</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="lit">2</span><span class="pun">));</span><span class="pln"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="com">//console.log(red_value); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; ctx</span><span class="pun">.</span><span class="pln">fillStyle </span><span class="pun">=</span><span class="pln"> </span><span class="str">'rgb('</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="pun">(</span><span class="pln">red_value </span><span class="pun">*</span><span class="pln"> </span><span class="lit">25</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="str">',0,0)'</span><span class="pun">;</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; ctx</span><span class="pun">.</span><span class="pln">fillRect</span><span class="pun">(</span><span class="pln">column_index</span><span class="pun">*</span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">496</span><span class="pun">-(</span><span class="pln">entry_index</span><span class="pun">*</span><span class="lit">16</span><span class="pun">),</span><span class="pln"> </span><span class="lit">16</span><span class="pun">,</span><span class="pln"> </span><span class="lit">16</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> <br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br /></span><span class="pun">}</span><span class="pln"><br /><br /><br /></span><span class="com">/* The heatmap is is really a 64x32 grid. Initialize the array which contains the grid data. */</span><span class="pln"><br /></span><span class="kwd">function</span><span class="pln"> setup</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; console_columns </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln"><br /><br />&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> column_index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> column_index </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">64</span><span class="pun">;</span><span class="pln"> column_index</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> column </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[];</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln"> </span><span class="kwd">var</span><span class="pln"> entry_index </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> entry_index </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">32</span><span class="pun">;</span><span class="pln"> entry_index</span><span class="pun">++</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> <br />&nbsp; &nbsp; &nbsp; &nbsp; column</span><span class="pun">[</span><span class="pln">entry_index</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br />&nbsp; &nbsp; console_columns</span><span class="pun">.</span><span class="pln">push</span><span class="pun">(</span><span class="pln">column</span><span class="pun">);</span><span class="pln"><br />&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br /><br /></span><span class="pun">}</span></code></pre></div>
    
    <h2>Run It!</h2>
    
    <p>Run Heat Tacer with the following. Note, sudo is required by dtrace as it does kernal magic.</p>
    
    <pre><code><span class="pln">sudo node heat_tracer</span><span class="pun">.</span><span class="pln">js<br /></span></code></pre>
    
    <p>If all goes well you should see something a moving version of something like the image below.</p>
    
    <blockquote>
        <p><img src="http://howtonode.org/heat-tracer/heat_tracer.png" alt="Alt value of image" title="" /> </p>
    </blockquote>
    
    <h2>Contribute</h2>
    
    <p>You can find the latest version of Heat Tracer <a href="https://github.com/gflarity/Heat-Tracer">here</a>. It is my hope that this article will provide the ground work for a much more abitious performance analytics project. If you're interested in contributing please let me know.</p>
    
    <h2>Further Research</h2>
    
    <p>More information about Bryan and Brendan's demo can be found <a href="http://dtrace.org/blogs/brendan/2011/01/24/cloud-analytics-first-video/">here</a>.</p>
    
    <p>Socket.IO can be found <a href="http://socket.io/">here</a>.</p><hr style="clear:both" /><div class="body" id="disqus_thread"></div>
    </div><div id="sidebar"><div class="aside clearfix"><h4>About the Author</h4><img src="http://www.gravatar.com/avatar/efab0eadca0e9998429621eb25379f37?r=pg&amp;s=200.jpg&amp;d=identicon" class="headshot" /><dl><dt>Name:</dt><dd>Geoff Flarity</dd></dl><dl><dt>Github:</dt><dd><a href="http://github.com/gflarity">gflarity</a></dd></dl><dl><dt>Twitter:</dt><dd><a href="http://twitter.com/gflarity">gflarity</a></dd></dl><dl><dt>Links:</dt><dd><a href="http://ca.linkedin.com/pub/geoff-flarity/a/536/43a">Homepage</a></dd></dl></div><div class="aside"><h4>About this Article</h4><a href="http://twitter.com/share" data-count="horizontal" data-via="creationix" class="twitter-share-button">Tweet</a><script src="http://platform.twitter.com/widgets.js"></script><dl title="Mon, 21 Feb 2011 19:04:30 GMT "><dt>Date Released:</dt><dd>Tuesday February 21, 2011</dd></dl><dl title="Mon Feb 21 11:05:22 2011 -0800"><dt>Last Updated:</dt><dd>Tuesday February 21, 2011</dd></dl><dl><dt>Node Version:</dt><dd><a href="http://github.com/joyent/node/tree/v0.4.0">node v0.4.0</a></dd></dl><dl><dt>Code Samples:</dt><dd><ul><li><a href="heat-tracer/heat_tracer.js">heat_tracer.js</a></li><li><a href="heat-tracer/public/heat_tracer.html">public/heat_tracer.html</a></li><li><a href="heat-tracer/public/heat_tracer_client.js">public/heat_tracer_client.js</a></li></ul></dd></dl><dl><dt>Revisions:</dt><dd><ul><li title="Update timestamp and node version."><a href="/9a7310be5d1d8da6f9043bef07bfb5f100c673bf/heat-tracer">Mon, 21 Feb 2011 19:05:22 GMT</a></li><li title="typo"><a href="/bdf9b1cbb363cef7a2dfc772995755ab6feff13c/heat-tracer">Tue, 08 Feb 2011 01:24:21 GMT</a></li><li title="added a picture of Heat Tracer in action and polished up the article"><a href="/622a639b003a3a3e22c316fbc74b4837475ba40e/heat-tracer">Tue, 08 Feb 2011 01:13:53 GMT</a></li><li title="initial revision of the Heat Tracer article
    
    create mode 100644 articles/heat-tracer.markdown"><a href="/50aa675dedefc5b39f0be62e779fac71eec99858/heat-tracer">Mon, 07 Feb 2011 02:16:56 GMT</a></li></ul></dd></dl></div><div class="bubble"><h3>About HowToNode.org</h3>
    
    <p>HowToNode.org is a community supported blog created by <a href="http://creationix.com/">Tim Caswell</a>. The purpose of the blog is to teach how to do various tasks in <a href="http://nodejs.org/">node.js</a> as well as teach fundamental concepts that are needed to write effective code.</p>
    
    <p>This site is powered by <a href="http://github.com/creationix/wheat">Wheat</a>, a git based blogging engine written in <a href="http://nodejs.org/">node.JS</a>.</p>
    
    <p>The content for this site is stored in a <a href="http://github.com/creationix/howtonode.org">git repository</a> that anyone can fork, write an article, and send a pull request. If your article passes the quality standards it will be published and help support the greater node community.</p></div></div><div class="clearfix" id="footer"><p>Site Design and code is &copy; 2010 to Tim Caswell under the MIT license. Content and articles are copyrighted to the individual authors. All code snippets used in the examples are in the public domain.</p><p>Wheat running on node v0.4.7</p></div></div>
/*
* Gridless version 2.0
*/

/* HTML5 display definitions
---------------------------------------- */

article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section, dialog {
    display: block;
}

/* Corrects inline-block not defined in IE6/7/8 and FF3 */
audio, canvas, video {
    display: inline-block;
    *display: inline;
    *zoom: 1;
}

audio:not([controls]) {
    display: none;
}

/* Ensures content is hidden from all presentations, including screenreaders */
[hidden] {
    display: none;
    visibility: hidden;
}

/* Base structure
---------------------------------------- */

/*
* The body will work like a 'div#wrapper' (for this to work, the 'body' needs to have a set width)

* To add a background to the PAGE, set it in the 'html' element
* To add a background to the WRAPPER, set it in the 'body' element
*/
html {
    height: 100%;
    font-size: 100%;
    overflow-y: scroll; /* Force a scrollbar in non-IE */
    -webkit-text-size-adjust: 100%; /* Prevent iOS text size adjust on orientation change without disabling user zoom */
    -ms-text-size-adjust: 100%;
}

body {
    margin: 0 auto;
    min-height: 100%;
}


/* body styling from http://kevinburke.bitbucket.org/markdowncss/markdown.css */
body{
    color: #444444;
    max-width: 960px;
    padding: 30px;
}
/* additions */
body{
    -moz-hyphens: auto;
    -webkit-hyphens: auto;
}
img{
    margin: 0 auto;
}

/* Fonts settings based on the 100E2R standard: http://www.informationarchitects.jp/en/100e2r/ */
body, button, input, select, textarea {
    font: 1em/1.625 Georgia, serif;
    color: #333; /* Black on white is too much contrast, #333 is a lot better */
}

/* Add and/or remove tags as your baseline grid needs */
p, blockquote, q, pre, address, hr, code, samp, dl, ol, ul, form, table, fieldset, menu, h4, h5, h6, img, figure, figcaption, button, hr {
    margin: 0 0 1.625em;
}

/* Headings/small
---------------------------------------- */

/*
* Font sizes are based on the golden ratio of 16
* See this for the modular scale: ow.ly/5jGl6
* Line-heights and margins are adjusted to keep a 26px (1.625em) vertical rhythm across elements 
*/

h1, h2, h3, h4, h5, h6 {
    font-family: Palatino, 'Palatino Linotype', 'Book Antiqua', FreeSerif, Georgia, serif;
    font-size: 1em;
    font-weight: bold;
}

h1 {
    font-size: 4.25em; /* 68px */
    line-height: 1.1471em;
    margin: 0 0 0.3824em;
}

h2 {
    font-size: 2.625em; /* 42px */
    line-height: 1.2381em;
    margin: 0 0 0.619em;
}

h3 {
    font-size: 1.625em; /* 26px */
    line-height: 1em;
    margin: 0 0 1em;
}

small {
    font-size: 0.625em; /* 10px */
    margin: 0 0 2.6em;
}

/* Preformatted text and code
---------------------------------------- */

/* Allows line wrapping of 'pre' */
pre {
    white-space: pre;
    white-space: pre-wrap;
    word-wrap: break-word;
}

pre, code, kbd, samp {
    font: 1em/1.625em Menlo, Consolas, 'DejaVu Sans Mono', Monaco, 'Courier New', Courier, monospace;
}

/* Tables
---------------------------------------- */

table {
    border-collapse: collapse;
    border-spacing: 0;
}

th {
    text-align: left;
}

tr, th, td {
    padding-right: 1.625em;
}

/* Forms
---------------------------------------- */

form {
    margin: 0;
}

fieldset {
    border: 0;
    padding: 0;
}

textarea {
    overflow: auto;
    vertical-align: top;
}

legend {
    border: 0;
    *margin-left: -7px;
}

button, input, select, textarea {
    vertical-align: baseline;
    *vertical-align: middle;
}

button, input {
    line-height: normal;
    *overflow: visible;
}

button, input[type="button"], input[type="reset"], input[type="submit"] {
    cursor: pointer;
    -webkit-appearance: button;
}

input[type="checkbox"], input[type="radio"] {
    box-sizing: border-box;
}

input[type="search"] {
    -webkit-appearance: textfield;
    -moz-box-sizing: content-box;
    -webkit-box-sizing: content-box;
    box-sizing: content-box;
}

input[type="search"]::-webkit-search-decoration {
    -webkit-appearance: none;
}

button::-moz-focus-inner, input::-moz-focus-inner {
    border: 0;
    padding: 0;
}

/* Reintroduce inner spacing in 'table' to avoid overlap and whitespace issues in IE6/7 */
table button, table input {
    *overflow: auto;
}

/* Quotes
---------------------------------------- */

blockquote, q {
    quotes: none;
}

blockquote:before, blockquote:after, q:before, q:after {
    content: '';
    content: none;
}

blockquote, q, cite {
    font-style: italic;
}

blockquote {
    padding-left: 1.625em;
    border-left: 1px solid #ddd;
}

blockquote > p {
    padding: 0;
}

/* Lists
---------------------------------------- */

ul, ol {
    list-style-position: inside;
    padding: 0;
}

li ul, li ol {
    margin: 0 1.625em;
}

dl dd {
    margin-left: 1.625em;
}

/* Links
---------------------------------------- */

a, a:visited {
    text-decoration: none;
    color: #06c;
}

a:hover {
    border-bottom: 1px solid;
}

a:focus {
    outline: thin dotted;
}

/* Better CSS outline suppression: people.opera.com/patrickl/experiments/keyboard/test */
a:hover, a:active {
    outline: none;
}

/* Figures
---------------------------------------- */

figure {
    margin: 0;
}

/* Embedded content
---------------------------------------- */

img, object, video {
    max-width: 100%; /* Automatically scales images larger than the container. Consider this first: http://unstoppablerobotninja.com/entry/fluid-images/ */
    /* _width: 100%; /* IE6 doesn't support max-width, so we just use width. If the image is larger than the container, just uncomment this. If it is smaller than the container, uncomment and change the 100% value to an absolute one */
}

img {
    border: 0;
    -ms-interpolation-mode: bicubic; /* Improve IE's resizing of images: css-tricks.com/ie-fix-bicubic-scaling-for-images */
}

/* Corrects overflow displayed oddly in IE9 */
svg:not(:root) {
    overflow: hidden;
}

/* Abbreviations
---------------------------------------- */

abbr[title], dfn[title] {
    border-bottom: 1px dotted;
    cursor: help;
}

/* Marked/inserted/deleted text
---------------------------------------- */

mark {
    background: #ff0;
}

ins {
    text-decoration: none;
    background: #ff9;
}

del {
    text-decoration: line-through;
}

/* Others
---------------------------------------- */

hr {
    display: block;
    height: 1px;
    border: 0;
    border-bottom: 1px solid #ddd;
}

strong, b, dt {
    font-weight: bold;
}

dfn {
    font-style: italic;
}

var, address {
    font-style: normal;
}

/* Position 'sub' and 'sup' without affecting line-height: gist.github.com/413930 */
sub, sup {
    font-size: 0.625em;
    line-height: 0;
    position: relative;
    vertical-align: baseline;
}

sup {
    top: -0.5em;
}

sub {
    bottom: -0.25em;
}

/* Helper classes
---------------------------------------- */

/*
* Micro clearfix hack, more semantically titled with 'group'
* Source: nicolasgallagher.com/micro-clearfix-hack/
*/
.group:before, .group:after {
    content: "";
    display: table;
}

.group:after {
    clear: both;
}

.group {
    *zoom: 1;
}




/* Print styles
---------------------------------------- */

/* Print styles inlined to avoid extra HTTP connection */

@media print {
* {
    background: transparent !important;
    color: black !important; /* Black prints faster: sanbeiji.com/archives/953 */
    text-shadow: none !important;
    filter: none !important;
    -ms-filter: none !important;
}

a, a:visited {
    text-decoration: underline;
}

a[href]:after {
    content: " (" attr(href) ")";
}

abbr[title]:after {
    content: " (" attr(title) ")";
}

/* Do not show javascript and internal links */
a[href^="javascript:"]:after, a[href^="#"]:after {
    content: ""; 
}

/* Printing Tables: css-discuss.incutio.com/wiki/Printing_Tables */
thead {
    display: table-header-group;
}

tr, img {
    page-break-inside: avoid;
}

@page {
    margin: 2cm;
}

p, h2, h3 {
    orphans: 3;
    widows: 3;
}

h2, h3, h4 {
    page-break-after: avoid;
}
}