Skip to content

Commit 0c8b7cd

Browse files
committed
[ui] Add a primitive trace UI.
- This is just an initial sketch of loading a trace file and showing all of the tasks which ran (in the order that they were readied).
1 parent c596636 commit 0c8b7cd

File tree

8 files changed

+286
-1
lines changed

8 files changed

+286
-1
lines changed

products/ui/llbuildui/app.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import flask
44

55
import database
6+
import trace
67
import views
78

89
class LLBuildApp(flask.Flask):
@@ -20,6 +21,14 @@ def _teardown(exception):
2021

2122
self.register_blueprint(views.main)
2223

24+
@property
25+
def trace(self):
26+
d = flask.g.get('_trace', None)
27+
if d is None:
28+
result = trace.Trace.frompath(flask.session["trace"])
29+
d = flask.g._trace = result
30+
return d
31+
2332
@property
2433
def database_session(self):
2534
s = flask.g.get('_database_session', None)

products/ui/llbuildui/templates/db_config.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
Select the database:
77
<form action="" method="post">
8-
<p><input type=text name=db_path>
8+
<p><input type=text name=db_path value="{{db_path}}">
99
<p><input type=submit value="Set Path">
1010
</form>
1111

products/ui/llbuildui/templates/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{% block title %}llbuild Debugging UI{% endblock %}
33
{% block body %}
44

5+
<p><a href="{{ url_for('main.trace_root') }}">Tracer Viewer</a></p>
56
<p><a href="{{ url_for('main.db_root') }}">Database Browser</a></p>
67

78
{% endblock %}

products/ui/llbuildui/templates/layout.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
{% if db_path %}
2727
<li><a href="{{ url_for('main.db_config') }}"><i>Database:</i> {{ db_path }}</a></li>
2828
{% endif %}
29+
{% if trace_path %}
30+
<li><a href="{{ url_for('main.trace_config') }}"><i>Trace:</i> {{ trace_path }}</a></li>
31+
{% endif %}
2932
</ul>
3033
</div><!--/.nav-collapse -->
3134
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{% extends "layout.html" %}
2+
{% block title %}Configuration{% endblock %}
3+
{% block body %}
4+
5+
6+
Select the trace path:
7+
<form action="" method="post">
8+
<p><input type=text name=trace_path value="{{trace_path}}">
9+
<p><input type=submit value="Set Path">
10+
</form>
11+
12+
{% endblock %}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{% extends "layout.html" %}
2+
{% block title %}Build Trace{% endblock %}
3+
{% block body %}
4+
5+
<p>
6+
Events:<br>
7+
{% for event in trace.events%}
8+
{% if event.isReadiedTask %}
9+
{{event.task.rule.key}}<br><br>
10+
{% endif %}
11+
{% endfor %}
12+
</p>
13+
14+
{% endblock %}

products/ui/llbuildui/trace.py

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import json
2+
3+
class Trace(object):
4+
"""
5+
An llbuild build system trace
6+
"""
7+
8+
# Get a cached trace.
9+
@classmethod
10+
def frompath(cls, path):
11+
db = cls._traces.get(path)
12+
if db is None:
13+
cls._traces[path] = db = Trace(path)
14+
return db
15+
_traces = {}
16+
17+
def __init__(self, path):
18+
self.events = []
19+
self.tasks = {}
20+
self.rules = {}
21+
22+
# FIXME: Move this format to just JSON for ease of loading.
23+
with open(path) as f:
24+
lines = list(f)
25+
print((lines[0], lines[-1]))
26+
assert(lines.pop(0) == '[\n')
27+
assert(lines.pop(-1) == ']\n')
28+
for line in lines:
29+
assert(line.startswith('{ '))
30+
assert(line.endswith('},\n'))
31+
line = line[2:-3]
32+
event_data = [eval(s) for s in line.split(', ')]
33+
handler = _event_handlers.get(event_data[0])
34+
if handler is None:
35+
raise NotImplementedError(
36+
"unknown event: {}".format(event_data[0]))
37+
event = handler(self, event_data[1:])
38+
if event:
39+
self.events.append(event)
40+
41+
# MARK: Event Parsing
42+
43+
class Rule(object):
44+
def __init__(self, data):
45+
(name, key) = data
46+
self.name = name
47+
self.key = key
48+
49+
class Task(object):
50+
def __init__(self, data):
51+
(name,) = data
52+
self.name = name
53+
self.rule = None
54+
55+
###
56+
57+
class Event(object):
58+
@property
59+
def isReadiedTask(self):
60+
return isinstance(self, ReadiedTask)
61+
62+
class BuildStarted(Event):
63+
def __init__(self, trace, data):
64+
pass
65+
66+
class BuildEnded(Event):
67+
def __init__(self, trace, data):
68+
pass
69+
70+
class HandlingBuildInputRequest(Event):
71+
def __init__(self, trace, data):
72+
(rule,) = data
73+
self.rule = trace.rules[rule]
74+
75+
class CheckingRuleNeedsToRun(Event):
76+
def __init__(self, trace, data):
77+
(rule,) = data
78+
self.rule = trace.rules[rule]
79+
80+
class RuleNeedsToRun(Event):
81+
class NeverBuilt(Event):
82+
pass
83+
class InvalidValue(Event):
84+
pass
85+
class InputRebuilt(Event):
86+
def __init__(self, inputRule):
87+
self.inputRule = inputRule
88+
def __init__(self, trace, data):
89+
self.rule = trace.rules[data[0]]
90+
if data[1] == 'invalid-value':
91+
(_, _) = data
92+
self.reason = RuleNeedsToRun.InvalidValue()
93+
elif data[1] == 'never-built':
94+
(_, _) = data
95+
self.reason = RuleNeedsToRun.NeverBuilt()
96+
elif data[1] == 'input-rebuilt':
97+
(_, _, inputRule) = data
98+
self.reason = RuleNeedsToRun.InputRebuilt(
99+
trace.rules[inputRule])
100+
else:
101+
raise NotImplementedError("unknown reason: {}".format(data))
102+
103+
class RuleDoesNotNeedToRun(Event):
104+
def __init__(self, trace, data):
105+
(rule,) = data
106+
self.rule = trace.rules[rule]
107+
108+
class CreatedTaskForRule(Event):
109+
def __init__(self, trace, data):
110+
(task, rule) = data
111+
self.task = trace.tasks[task]
112+
self.rule = trace.rules[rule]
113+
self.task.rule = self.rule
114+
115+
class HandlingTaskInputRequest(Event):
116+
def __init__(self, trace, data):
117+
(task, rule) = data
118+
self.task = trace.tasks[task]
119+
self.rule = trace.rules[rule]
120+
121+
class AddedRulePendingTask(Event):
122+
def __init__(self, trace, data):
123+
(rule, task) = data
124+
self.rule = trace.rules[rule]
125+
self.task = trace.tasks[task]
126+
127+
class RuleScheduledForScanning(Event):
128+
def __init__(self, trace, data):
129+
(rule,) = data
130+
self.rule = trace.rules[rule]
131+
132+
class PausedInputRequestForRuleScan(Event):
133+
def __init__(self, trace, data):
134+
(rule,) = data
135+
self.rule = trace.rules[rule]
136+
137+
class ReadyingTaskInputRequest(Event):
138+
def __init__(self, trace, data):
139+
(task, rule) = data
140+
self.task = trace.tasks[task]
141+
self.rule = trace.rules[rule]
142+
143+
class CompletedTaskInputRequest(Event):
144+
def __init__(self, trace, data):
145+
(task, rule) = data
146+
self.task = trace.tasks[task]
147+
self.rule = trace.rules[rule]
148+
149+
class UpdatedTaskWaitCount(Event):
150+
def __init__(self, trace, data):
151+
(task, count) = data
152+
self.task = trace.tasks[task]
153+
self.count = count
154+
155+
class RuleScanningDeferredOnInput(Event):
156+
def __init__(self, trace, data):
157+
(rule, inputRule) = data
158+
self.rule = trace.rules[rule]
159+
self.inputRule = trace.rules[inputRule]
160+
161+
class RuleScanningDeferredOnTask(Event):
162+
def __init__(self, trace, data):
163+
(rule, inputTask) = data
164+
self.rule = trace.rules[rule]
165+
self.inputTask = trace.tasks[inputTask]
166+
167+
class RuleScanningNextInput(Event):
168+
def __init__(self, trace, data):
169+
(rule, inputRule) = data
170+
self.rule = trace.rules[rule]
171+
self.inputRule = trace.rules[inputRule]
172+
173+
class UnblockedTask(Event):
174+
def __init__(self, trace, data):
175+
(task,) = data
176+
self.task = trace.tasks[task]
177+
178+
class ReadiedTask(Event):
179+
def __init__(self, trace, data):
180+
(task, rule) = data
181+
self.task = trace.tasks[task]
182+
assert(self.task.rule is trace.rules[rule])
183+
184+
class FinishedTask(Event):
185+
def __init__(self, trace, data):
186+
(task, rule, effect) = data
187+
self.task = trace.tasks[task]
188+
assert(self.task.rule is trace.rules[rule])
189+
self.effect = effect
190+
191+
def _create_rule(trace, data):
192+
rule = Rule(data)
193+
trace.rules[rule.name] = rule
194+
195+
def _create_task(trace, data):
196+
task = Task(data)
197+
trace.tasks[task.name] = task
198+
199+
_event_handlers = {
200+
"new-rule": _create_rule,
201+
"new-task": _create_task,
202+
203+
"build-started": BuildStarted,
204+
"build-ended": BuildEnded,
205+
"handling-build-input-request": HandlingBuildInputRequest,
206+
"checking-rule-needs-to-run": CheckingRuleNeedsToRun,
207+
"rule-needs-to-run": RuleNeedsToRun,
208+
"rule-does-not-need-to-run": RuleDoesNotNeedToRun,
209+
"created-task-for-rule": CreatedTaskForRule,
210+
"handling-task-input-request": HandlingTaskInputRequest,
211+
"added-rule-pending-task": AddedRulePendingTask,
212+
"rule-scheduled-for-scanning": RuleScheduledForScanning,
213+
"paused-input-request-for-rule-scan": RuleScheduledForScanning,
214+
"readying-task-input-request": ReadyingTaskInputRequest,
215+
"completed-task-input-request": CompletedTaskInputRequest,
216+
"updated-task-wait-count": UpdatedTaskWaitCount,
217+
"rule-scanning-deferred-on-input": RuleScanningDeferredOnInput,
218+
"rule-scanning-deferred-on-task": RuleScanningDeferredOnTask,
219+
"rule-scanning-next-input": RuleScanningNextInput,
220+
"unblocked-task": UnblockedTask,
221+
"readied-task": ReadiedTask,
222+
"finished-task": FinishedTask,
223+
}

products/ui/llbuildui/views.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@
1212
def index():
1313
return flask.render_template("index.html")
1414

15+
# MARK: Trace Viewer
16+
17+
@main.route('/trace')
18+
def trace_root():
19+
# If no database has been selected, prompt for one.
20+
trace_path = session.get("trace")
21+
if trace_path is None:
22+
return redirect(url_for('main.trace_config'))
23+
24+
return flask.render_template(
25+
"trace_root.html", trace=current_app.trace,
26+
trace_path=session.get("trace"))
27+
28+
@main.route('/trace/config', methods=['GET', 'POST'])
29+
def trace_config():
30+
if request.method == 'POST':
31+
session['trace'] = request.form['trace_path']
32+
return redirect(url_for('main.trace_root'))
33+
return flask.render_template("trace_config.html",
34+
trace_path=session.get("trace"))
35+
36+
# MARK: Database Browser
37+
1538
@main.route('/db')
1639
def db_root():
1740
# If no database has been selected, prompt for one.

0 commit comments

Comments
 (0)