<!-- | |
Tracing template | |
---------------- | |
Test_shell has an optional tracing feature. It can be enabled by running | |
with the --enable-tracing option. Tracing causes events to be dropped into | |
a trace file during runtime. | |
This HTML file can be used to render the output from the trace. You'll need | |
to rename your trace data to "trace_data.js" in the same directory with this | |
HTML file, and then you can visualize the trace. | |
Lots of work remains on this tracing tool; currently development is on hold. | |
--> | |
<html> | |
<head> | |
<title> | |
Trace Events | |
</title> | |
<style> | |
body { | |
font-family: "Courier New"; | |
font-size: 9pt; | |
} | |
#header { | |
position: absolute; | |
top: 0px; | |
left: 0px; | |
border-bottom: 1px dashed black; | |
background-color: #F0F0F0; | |
z-index: 3; | |
} | |
#outer { | |
position: relative; | |
height: 200px; | |
} | |
#time_scale { | |
height: 15px; | |
width: 100%; | |
} | |
#tooltip { | |
position: absolute; | |
background-color: #FFFFCC; | |
display: none; | |
font-family: "Courier New"; | |
font-size: 9pt; | |
padding: 5px; | |
border: 1px solid #CCCC88; | |
z-index: 3; | |
} | |
#legend { | |
position: fixed; | |
left: 10px; | |
bottom: 10px; | |
padding: 5px; | |
border: 1px solid silver; | |
z-index: 10; | |
background-color: #f0f0f0; | |
} | |
h2 { | |
margin: 5px; | |
} | |
#instructions { | |
position: absolute; | |
top: | |
float: right; | |
display: none; | |
} | |
li.time_tick { | |
background-color: #FFFFCC; | |
height: 15px; | |
} | |
li { | |
background: pink; | |
position: absolute; | |
height: 10px; | |
list-style: none; | |
margin: 0px; | |
padding: 0px; | |
z-index: 2; | |
} | |
li:hover { | |
border: 1px solid red; | |
} | |
.url { | |
background-color: green; | |
} | |
.http { | |
background-color: blue; | |
} | |
.socket { | |
background-color: black; | |
} | |
.v8 { | |
background-color: orange; | |
} | |
.loop { | |
background-color: gray; | |
} | |
.io { | |
background-color: blue; | |
} | |
</style> | |
<script src='trace_data.js'></script> | |
<script> | |
var scale = 100000; | |
var row_height = 15; | |
var trace_initial_time = 0; | |
var trace_threads = {}; | |
var heartbeats = []; | |
var trace_total_time = 0; | |
function process_raw_events() { | |
trace_initial_time = raw_trace_events[0].usec_begin; | |
var stack = []; | |
var e; | |
for (var i in raw_trace_events) { | |
e = raw_trace_events[i]; | |
var trace_events = trace_threads["e.tid"]; | |
if (!trace_events) { | |
trace_events = []; | |
trace_threads["e.tid"] = trace_events; | |
} | |
if (e.name.indexOf("heartbeat.") == 0) { | |
heartbeats.push(e); | |
} else if (e.type == "BEGIN") { | |
trace_events.push(e); | |
stack.unshift(e); | |
} else if (e.type == "END") { | |
for (var s in stack) { | |
var begin = stack[s]; | |
if ((begin.id == e.id) && (begin.name == e.name) && | |
(begin.pid == e.pid) && (begin.tid == e.tid)) { | |
begin.usec_end = e.usec_begin; | |
begin.duration = begin.usec_end - begin.usec_begin; | |
begin.extra += " " + e.extra; | |
stack.splice(s, 1); | |
break; | |
} | |
} | |
} else if (e.type == "INSTANT") { | |
trace_events.push(e); | |
e.duration = 0; | |
} | |
} | |
if (e.usec_end) | |
trace_total_time = e.usec_end - trace_initial_time; | |
else | |
trace_total_time = e.usec_begin - trace_initial_time; | |
} | |
function compute_scale() { | |
var outer = document.getElementById("outer"); | |
scale = Math.floor(trace_total_time / (outer.offsetWidth - (row_height * 2))); | |
}; | |
function show_details(tid, i, event) { | |
var trace_events = trace_threads["e.tid"]; | |
var inner = trace_events[i].name + " " + | |
trace_events[i].duration / 1000 + "ms<br />" + | |
trace_events[i].id + "<br />" + | |
trace_events[i].extra + "<br />"; | |
var tooltip = document.getElementById("tooltip"); | |
tooltip.innerHTML = inner; | |
if (window.event) | |
event = window.event; | |
tooltip.style.top = event.pageY + 3; | |
tooltip.style.left = event.pageX + 3; | |
tooltip.style.display = "block"; | |
}; | |
function generate_time_scale() { | |
var view_size = window.clientWidth; | |
var body_size = document.body.scrollWidth; | |
var inner = ""; | |
var step_ms = Math.floor(scale / 10); // ms per 100px | |
var pow10 = Math.pow(10, Math.floor(Math.log(step_ms) / Math.log(10))); | |
var round = .5 * pow10; | |
step_ms = round * (Math.floor(step_ms / round)); // round to a multiple of round | |
for (var i = step_ms; i < trace_total_time / 1000; i += step_ms) { | |
var x = Math.floor(i * 1000 / scale); | |
inner += "<li class='time_tick' style='left: " + x + "px'>" + i + "</li>"; | |
} | |
var time_scale = document.getElementById("time_scale"); | |
time_scale.innerHTML = inner; | |
time_scale.style.width = document.body.scrollWidth; | |
} | |
function generate_io_graph(trace_events, top) { | |
var max_height = 200; | |
var bucket_size = 50000; // millisecs | |
var inner = ""; | |
var buckets = new Array(); | |
var value = 0; | |
var max_bucket = 0; | |
// Go through events and find all read samples. | |
// Aggregate data into buckets. | |
for (var i in trace_events) { | |
var e = trace_events[i]; | |
if (e.name != "socket.read") { | |
continue; | |
} | |
var bytes = parseInt(e.extra); | |
// bytes = 400; | |
var start_time = e.usec_begin - trace_initial_time; | |
var mybucket = Math.floor(start_time / bucket_size); | |
if (buckets[mybucket] == undefined) { | |
buckets[mybucket] = 0; | |
} | |
buckets[mybucket] += bytes; | |
if (buckets[max_bucket] == undefined || | |
buckets[max_bucket] < buckets[mybucket]) { | |
max_bucket = mybucket; | |
} | |
} | |
for (var index = 0; index < buckets.length; index++) { | |
var left = index * Math.floor(bucket_size / scale); | |
var width = Math.floor(bucket_size / scale); | |
if (width == 0) | |
width = 1; | |
var height; | |
if (buckets[index] == undefined) { | |
height = 0; | |
} else { | |
height = (buckets[index] / buckets[max_bucket]) * max_height; | |
} | |
var my_top = max_height - height; | |
var style = "top: " + my_top + "px; left: " + left + "px; width: " + width + "px; height:" + height + "px;"; | |
var cls = "io"; | |
inner += "<li title='" + buckets[index] + " bytes' class='" + cls + "' id='li-" + i + "' style='" + style + "'></li>\n"; | |
} | |
var subchart = document.createElement('div'); | |
subchart.setAttribute("class", "iograph"); | |
subchart.setAttribute("id", trace_events[0].tid); | |
subchart.innerHTML = inner; | |
subchart.style.height = max_height; | |
subchart.style.top = top; | |
subchart.style.left = 0; | |
subchart.style.position = "absolute"; | |
// subchart.style.width = row_height + last_max_x; | |
var chart = document.getElementById("chart"); | |
chart.appendChild(subchart); | |
return top + max_height; | |
} | |
function generate_subchart(trace_events, top) { | |
var start_top = top; | |
var heights = new Array(); | |
var max_row = 0; | |
var inner = ""; | |
var last_max_time = 0; | |
var last_max_x = 0; | |
for (var i in trace_events) { | |
var e = trace_events[i]; | |
var start_time = e.usec_begin - trace_initial_time; | |
var left = row_height + Math.floor(start_time / scale); | |
var width = Math.floor(e.duration / scale); | |
if (width == 0) | |
width = 1; | |
if (heights[e.id]) { | |
top = heights[e.id]; | |
} else { | |
max_row += row_height; | |
heights[e.id] = max_row; | |
top = heights[e.id]; | |
} | |
//if (start_time < last_max_time) | |
// top += row_height; | |
var style = "top: " + top + "px; left: " + left + "px; width: " + width + "px;"; | |
var js = 'javascript:show_details("' + e.tid + '", ' + i + ', event);'; | |
var cls = e.name.split('.')[0]; | |
inner += "<li class='" + cls + "' onmouseover='" + js + "' id='li-" + i + "' style='" + style + "'></li>\n"; | |
last_max_time = start_time + e.duration; | |
last_max_x = left + width; | |
} | |
var subchart = document.createElement('div'); | |
subchart.setAttribute("class", "subchart"); | |
subchart.setAttribute("id", trace_events[0].tid); | |
subchart.innerHTML = inner; | |
subchart.style.top = start_top + "px"; | |
subchart.style.height = top + row_height; | |
subchart.style.width = row_height + last_max_x; | |
subchart.style.position = "absolute"; | |
var chart = document.getElementById("chart"); | |
chart.appendChild(subchart); | |
return top; | |
}; | |
function generate_chart() { | |
var chart = document.getElementById("chart"); | |
chart.innerHTML = ""; | |
var top = 60; | |
for (var t in trace_threads) { | |
top = generate_io_graph(trace_threads[t], top); | |
top = generate_subchart(trace_threads[t], top); | |
} | |
generate_time_scale(); | |
} | |
function change_scale(event) { | |
if (!event) | |
event = window.event; | |
if (!event.shiftKey) | |
return; | |
var delta = 0; | |
if (event.wheelDelta) { | |
delta = event.wheelDelta / 120; | |
} else if (event.detail) { | |
delta = - event.detail / 3; | |
} | |
if (delta) { | |
var tooltip = document.getElementById("tooltip"); | |
tooltip.style.display = "none"; | |
var factor = 1.1; | |
if (delta < 0) | |
scale = Math.floor(scale * factor); | |
else | |
scale = Math.floor(scale / factor); | |
if (scale > 300000) | |
scale = 300000; | |
generate_chart(); | |
if (event.preventDefault) | |
event.preventDefault(); | |
} | |
event.returnValue = false; | |
}; | |
function initial_load() { | |
if (window.addEventListener) | |
window.addEventListener('DOMMouseScroll', change_scale, false); | |
window.onmousewheel = document.onmousewheel = change_scale; | |
process_raw_events(); | |
compute_scale(); | |
generate_chart(); | |
}; | |
</script> | |
</head> | |
<body onload='initial_load();'> | |
<div id="header"> | |
<h2>Trace Events</h2> | |
<div id="instructions"> | |
Use shift+mouse-wheel to zoom in and out. | |
</div> | |
<div id="time_scale"></div> | |
</div> | |
<div id="legend"> | |
<span class="url"> </span> URL<br /> | |
<span class="http"> </span> HTTP<br /> | |
<span class="socket"> </span> Socket<br /> | |
<span class="v8"> </span> V8<br /> | |
<span class="loop"> </span> TASKS<br /> | |
</div> | |
<div id="chart"> | |
<div id="outer"> | |
</div> | |
</div> | |
<div id="tooltip" ondblclick="this.style.display = 'none';"></div> | |
</body> | |
</html> |