<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; 
}

</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;
          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_subchart(trace_events, top) {
  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 (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.height = top + row_height;
  subchart.style.width = row_height + last_max_x;
  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_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">&nbsp;</span> URL<br />
<span class="http">&nbsp;</span> HTTP<br />
<span class="socket">&nbsp;</span> Socket<br />
<span class="v8">&nbsp;</span> V8<br />
</div>
<div id="chart">
<div id="outer">
</div>
</div>
<div id="tooltip" ondblclick="this.style.display = 'none';"></div>
</body>
</html>