magic-wormhole/misc/dump-timing.py

149 lines
5.4 KiB
Python
Raw Normal View History

# To use the web() option, you should do:
# * cd misc
2016-04-29 21:28:04 +00:00
# * npm install d3 zepto
from __future__ import print_function
2016-04-29 21:28:04 +00:00
import os, sys, time, json, random
streams = sys.argv[1:]
2016-04-29 21:28:04 +00:00
if not streams:
print("run like: python dump-timing.py tx.json rx.json")
sys.exit(1)
num_streams = len(streams)
labels = dict([(num, " "*num + "[%d]" % (num+1) + " "*(num_streams-1-num))
for num in range(num_streams)])
2016-04-29 21:28:04 +00:00
abs_timeline = []
sides = []
for side,fn in enumerate(streams):
with open(fn, "rb") as f:
2016-04-29 21:28:04 +00:00
for (start, sent, finish, what, details) in json.load(f):
abs_timeline.append( (start, sent, finish, side, what, details) )
print("%s is %s" % (labels[side], fn))
sides.append(os.path.basename(fn))
# relativize all timestamps
all_times = [e[0] for e in abs_timeline] + [e[2] for e in abs_timeline if e[2]]
all_times.sort()
earliest = all_times[0]
def rel(t):
if t is None: return None
return t - earliest
timeline = [ (rel(start), rel(sent), rel(finish), side, what, details)
for (start, sent, finish, side, what, details)
in abs_timeline ]
data = {}
# we pre-calculate the "lane" that each item uses, here in python, rather
# than leaving that up to the javascript.
data["lanes"] = ["proc", # 0 gets high-level events and spans: process start,
# imports, command dispatch, code established, key
# established, transit connected, process exit
"API", # 1 gets API call spans (apps usually only wait for
# one at a time, so they won't overlap): get_code,
# input_code, get_verifier, get_data, send_data,
# close
"wait", # 2 shows waiting-for-human: input code, get
# permission
"app", # 3: file-xfer events
"skt", # 4: websocket message send/receives
"misc", # 5: anything else
]
data["bounds"] = {"min": rel(all_times[0]), "max": rel(all_times[-1]),
}
data["sides"] = sides
print("started at %s" % time.ctime(earliest))
print("duration %s seconds" % data["bounds"]["max"])
items = data["items"] = []
for num, (start, sent, finish, side, what, details) in enumerate(timeline):
background = False
if what in ["wormhole",]:
# background region for wormhole lifetime
lane = 0
background = True
elif what in ["process start", "import", "command dispatch",
"code established", "key established", "transit connected",
"exit"]:
lane = 0
elif what in ["API get_code", "API input_code", "API set_code",
"API get_verifier", "API get_data", "API send_data",
#"API get data", "API send data",
"API close"]:
lane = 1
elif details.get("waiting") in ["user", "crypto"]: # permission or math
lane = 2
elif what in ["tx file", "get ack", "rx file", "unpack zip", "send ack"]:
lane = 3
elif what in ["websocket"]:
# connection establishment
lane = 4
background = True
elif (what in ["welcome", "error"] # rendezvous message receives
or what in ["allocate", "list", "get", "add", "deallocate"]
# rendezvous message sends
):
lane = 4
else:
lane = 5 # unknown
if background:
continue # disable until I figure out how to draw these better
details_str = ", ".join(["%s=%s" % (name, details[name])
for name in sorted(details)])
2016-04-29 21:28:04 +00:00
items.append({"side": side,
"lane": lane,
"start_time": start,
"server_sent": sent,
"finish_time": finish, # maybe None
"what": what,
"details": details,
"details_str": details_str,
"wiggle": random.randint(0,4),
})
2016-04-29 21:28:04 +00:00
#if "waiting" in details:
# viz_className += " wait-%s" % details["waiting"]
2016-04-29 21:28:04 +00:00
from pprint import pprint
pprint(data)
here = os.path.dirname(__file__)
web_root = os.path.join(here, "web")
2016-04-29 21:28:04 +00:00
lib_root = os.path.join(here, "node_modules")
if not os.path.isdir(lib_root):
print("Cannot find 'd3' and 'd3-tip' in misc/node_modules/")
print("Please run 'npm install d3 d3-tip 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)
2016-04-29 21:28:04 +00:00
root.putChild("data.json", static.Data(json.dumps(data).encode("utf-8"),
"application/json"))
root.putChild("lib", static.File(lib_root))
class Shutdown(resource.Resource):
def render_GET(self, request):
2016-04-29 21:28:04 +00:00
if 0:
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()