add misc/dump-timing.py too, to visualize the timeline
This commit is contained in:
parent
8d82726c51
commit
84749dd8b3
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -56,3 +56,4 @@ docs/_build/
|
|||
target/
|
||||
/twistd.pid
|
||||
/relay.sqlite
|
||||
/misc/node_modules/
|
||||
|
|
95
misc/dump-timing.py
Normal file
95
misc/dump-timing.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
# To use the web() option, you should do:
|
||||
# * cd misc
|
||||
# * npm install vis zepto
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, time, json
|
||||
|
||||
|
||||
streams = sys.argv[1:]
|
||||
num_streams = len(streams)
|
||||
labels = dict([(num, " "*num + "[%d]" % (num+1) + " "*(num_streams-1-num))
|
||||
for num in range(num_streams)])
|
||||
timeline = []
|
||||
groups_out = []
|
||||
for which,fn in enumerate(streams):
|
||||
with open(fn, "rb") as f:
|
||||
for (start, finish, what, start_d, finish_d) in json.load(f):
|
||||
timeline.append( (start, finish, which, what, start_d, finish_d) )
|
||||
print("%s is %s" % (labels[which], fn))
|
||||
groups_out.append({"id": which, "content": fn,
|
||||
"className": "group-%d" % which})
|
||||
|
||||
|
||||
timeline.sort(key=lambda row: row[0])
|
||||
first = timeline[0][0]
|
||||
print("started at %s" % time.ctime(start))
|
||||
viz_out = []
|
||||
for num, (start, finish, which, what, start_d, finish_d) in enumerate(timeline):
|
||||
delta = start - first
|
||||
delta_s = "%.6f" % delta
|
||||
start_d_str = ", ".join(["%s=%s" % (name, start_d[name])
|
||||
for name in sorted(start_d)])
|
||||
finish_d_str = ", ".join(["%s=%s" % (name, finish_d[name])
|
||||
for name in sorted(finish_d)])
|
||||
details_str = start_d_str
|
||||
if finish_d_str:
|
||||
details_str += "/" + finish_d_str
|
||||
finish_str = ""
|
||||
if finish is not None:
|
||||
finish_str = " +%.6f" % (finish - start)
|
||||
print("%9s: %s %s %s%s" % (delta_s, labels[which], what, details_str,
|
||||
finish_str))
|
||||
viz_start = start*1000
|
||||
viz_end = None if finish is None else finish*1000
|
||||
viz_type = "range" if finish else "point"
|
||||
if what == "wormhole started" or what == "wormhole":
|
||||
viz_type = "background"
|
||||
viz_content = '<span title="%s">%s</span>' % (details_str or "(No details)",
|
||||
what) # sigh
|
||||
viz_className = "item-group-%d" % which
|
||||
if "waiting" in start_d:
|
||||
viz_className += " wait-%s" % start_d["waiting"]
|
||||
viz_out.append({"id": num, "start": viz_start, "end": viz_end,
|
||||
"group": which, "content": viz_content,
|
||||
"className": viz_className, # or style:
|
||||
"type": viz_type,
|
||||
})
|
||||
|
||||
here = os.path.dirname(__file__)
|
||||
web_root = os.path.join(here, "web")
|
||||
vis_root = os.path.join(here, "node_modules", "vis", "dist")
|
||||
zepto_root = os.path.join(here, "node_modules", "zepto")
|
||||
if not os.path.isdir(vis_root) or not os.path.isdir(zepto_root):
|
||||
print("Cannot find 'vis' and 'zepto' in misc/node_modules/")
|
||||
print("Please run 'npm install vis zepto' from the misc/ directory.")
|
||||
sys.exit(1)
|
||||
|
||||
def web():
|
||||
# set up a server that serves web/ at the root, plus a /data.json built
|
||||
# from {timeline}. Quit when it fetches /done .
|
||||
from twisted.web import resource, static, server
|
||||
from twisted.internet import reactor, endpoints
|
||||
ep = endpoints.serverFromString(reactor, "tcp:8066:interface=127.0.0.1")
|
||||
root = static.File(web_root)
|
||||
data_json = {"items": viz_out, "groups": groups_out}
|
||||
data = json.dumps(data_json).encode("utf-8")
|
||||
root.putChild("data.json", static.Data(data, "application/json"))
|
||||
root.putChild("vis", static.File(vis_root))
|
||||
root.putChild("zepto", static.File(zepto_root))
|
||||
class Shutdown(resource.Resource):
|
||||
def render_GET(self, request):
|
||||
print("timeline ready, server shutting down")
|
||||
reactor.stop()
|
||||
return "shutting down"
|
||||
root.putChild("done", Shutdown())
|
||||
site = server.Site(root)
|
||||
ep.listen(site)
|
||||
import webbrowser
|
||||
def launch_browser():
|
||||
webbrowser.open("http://localhost:%d/timeline.html" % 8066)
|
||||
print("browser opened, waiting for shutdown")
|
||||
reactor.callLater(0, launch_browser)
|
||||
reactor.run()
|
||||
web()
|
||||
|
20
misc/web/timeline.css
Normal file
20
misc/web/timeline.css
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
div.item-group-0 {
|
||||
background-color: #fcc;
|
||||
}
|
||||
|
||||
div.item-group-1 {
|
||||
background-color: #cfc;
|
||||
}
|
||||
|
||||
div.item-group-2 {
|
||||
background-color: #ccf;
|
||||
}
|
||||
|
||||
div.wait-user {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.vis-item .vis-item-overflow {
|
||||
overflow: visible;
|
||||
}
|
21
misc/web/timeline.html
Normal file
21
misc/web/timeline.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Timeline Visualizer</title>
|
||||
<script src="vis/vis.min.js"></script>
|
||||
<script src="zepto/zepto.min.js"></script>
|
||||
<link href="vis/vis.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="timeline.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Wormhole Timeline</h1>
|
||||
|
||||
<div id="viz"></div>
|
||||
<br>
|
||||
<div id="cursor_date"></div>
|
||||
<div id="cursor_time"></div>
|
||||
|
||||
<script src="timeline.js"></script>
|
||||
|
||||
</body> </html>
|
45
misc/web/timeline.js
Normal file
45
misc/web/timeline.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
var vis, $; // hush
|
||||
|
||||
var container = document.getElementById("viz");
|
||||
var options = {editable: false,
|
||||
showCurrentTime: false,
|
||||
snap: null,
|
||||
order: function(a,b) { return a.id - b.id; }
|
||||
};
|
||||
var timeline = new vis.Timeline(container, options);
|
||||
|
||||
$.getJSON("data.json", function(data) {
|
||||
timeline.setData({groups: new vis.DataSet(data.groups),
|
||||
items: new vis.DataSet(data.items)});
|
||||
var start = data.items[0].start;
|
||||
var end = data.items[data.items.length-1].start;
|
||||
var span = end - start;
|
||||
timeline.setWindow(start - (span/10), end + (span/10));
|
||||
//timeline.fit(); // doesn't leave space on the ends
|
||||
timeline.setOptions({min: start - (span/10),
|
||||
max: end + (span/10),
|
||||
zoomMin: 50,
|
||||
zoomMax: 1.2*span});
|
||||
var bar = timeline.addCustomTime(start, "cursor");
|
||||
timeline.on("timechange", update_cursor);
|
||||
update_cursor({time: new Date(start)});
|
||||
timeline.on("doubleClick", zoom);
|
||||
$.get("done", function(_) {});
|
||||
});
|
||||
|
||||
function zoom(properties) {
|
||||
var target = properties.time.valueOf();
|
||||
var w = timeline.getWindow();
|
||||
var span = w.end - w.start;
|
||||
var new_span = span / 2;
|
||||
var new_start = target - new_span/2;
|
||||
var new_end = target + new_span/2;
|
||||
timeline.setWindow(new_start, new_end, {animation: true});
|
||||
}
|
||||
|
||||
function update_cursor(properties) {
|
||||
var t = properties.time;
|
||||
document.getElementById("cursor_date").innerText = t;
|
||||
var m = vis.moment(t);
|
||||
document.getElementById("cursor_time").innerText = m.format("ss.SSSSSS");
|
||||
}
|
Loading…
Reference in New Issue
Block a user