Permalink
Browse files

Added metadata to the call timeline page.

  • Loading branch information...
alex committed Mar 28, 2012
1 parent 565c8a7 commit 663b6d82b09c2529f621fc9c1d5d90af6b03b5e6
@@ -73,7 +73,7 @@ def visit_python_abort(self, abort):
def visit_python_call(self, call):
return {
"type": "python",
- "func_name": call.func_name,
+ "name": call.func_name,
"start_time": call.start_time,
"end_time": call.end_time,
"subcalls": [self.visit(subcall) for subcall in call.subcalls],
@@ -0,0 +1,64 @@
+#call-info {
+ width: 100;
+ margin: 0 0 15px 0;
+ background-color: 1px solid #B9DAEC;
+
+ -webkit-border-radius: 6px;
+ -moz-border-radius: 6px;
+ border-radius: 6px;
+
+ -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+
+ position: relative;
+}
+
+#call-info #call-header {
+ border-bottom: 1px solid #C3E3F3;
+ padding: 5px 15px;
+ background: #E5F3FA;
+
+ -webkit-border-top-left-radius: 6px;
+ -webkit-border-top-right-radius: 6px;
+ -moz-border-radius-topleft: 6px;
+ -moz-border-radius-topright: 6px;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+
+ overflow: hidden;
+}
+
+#call-info #call-header #call-name {
+ width: 600px;
+ height: 34px;
+ font-size: 16pt;
+ margin: 0px;
+ padding: 0px;
+}
+
+#call-info #call-body {
+ padding: 5px 15px 6px;
+ border-top: 1px solid #c3e3f3;
+
+ -webkit-border-radius: 0 0 6px 6px;
+ -moz-border-radius: 0 0 6px 6px;
+ border-radius: 0 0 6px 6px;
+
+ -webkit-box-shadow: inset 0 1px 0 #ffffff;
+ -moz-box-shadow: inset 0 1px 0 #ffffff;
+ box-shadow: inset 0 1px 0 #ffffff;
+
+ zoom: 1;
+ margin-bottom: 0;
+}
+
+#call-info #call-body p {
+ width: 600px;
+ margin: 0;
+ padding: 0;
+}
@@ -2,7 +2,8 @@
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/bootstrap.min.css" />
- <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/global.css">
+ <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/global.css" />
+ {% block extra_css %}{% endblock %}
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.min.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/bootstrap-dropdown.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/bootstrap-tabs.js"></script>
@@ -2,6 +2,10 @@
{% load trace_helpers %}
+{% block extra_css %}
+ <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/timeline.css" />
+{% endblock %}
+
{% block page_content %}
<script type="text/javascript" src="{{ STATIC_URL }}js/d3.v2.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
@@ -90,7 +94,7 @@
$(function() {
var h = 150;
var w = 675;
- $.getJSON("{% url trace_call_data log.id %}", function(dataset) {
+ $.getJSON("{% url trace_timeline_call_data log.id %}", function(dataset) {
var x = d3.scale.linear()
.domain([
d3.min(dataset, attrgetter("start_time")),
@@ -124,18 +128,45 @@
})
.attr("fill", colorgenerator(attrgetter("name")))
.on("mouseover", function(d) {
- $("#timeline #current-function").html("<p>Current "
- + "function: " + d.name + "</p>");
+ $("#call-info #call-header #call-name").html(d.name);
+ $.getJSON("{% url trace_call_data log.id %}", {
+ call_id: d.id,
+ }, function(data) {
+ $("#call-time").html(data["call_time"]);
+ $("#call-exclusive-time").html(data["call_exclusive_time"]);
+ $("#func-time").html(data["func_time"]);
+ $("#func-exclusive-time").html(data["func_exclusive_time"]);
+ })
})
.on("mouseout", function(d) {
- return $("#timeline #current-function").html("");
+ $("#call-info #call-header #call-name").html("");
});
});
});
</script>
<div id="timeline">
+ <div id="call-info">
+ <div id="call-header">
+ <code id="call-name"></code>
+ </div>
+ <div id="call-body">
+ <div class="row">
+ <div class="span5">
+ <p>
+ Total time: <strong id="call-time"></strong> seconds<br />
+ Exclusive time: <strong id="call-exclusive-time"></strong> seconds<br />
+ </p>
+ </div>
+ <div class="span5">
+ <p>
+ All invocation's total time: <strong id="func-time"></strong> seconds<br />
+ All invocation's exclusive time: <strong id="func-exclusive-time"></strong> seconds<br />
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
<div id="visualization"></div>
- <div id="current-function"></div>
</div>
{% endblock %}
@@ -173,16 +173,13 @@ class AssemblerChunk(TraceChunk):
class Call(models.Model):
log = models.ForeignKey(Log, related_name="calls")
+ name = models.CharField(max_length=255)
start_time = models.FloatField()
end_time = models.FloatField()
call_depth = models.PositiveIntegerField()
- parent = models.ForeignKey("self", null=True)
+ parent = models.ForeignKey("self", null=True, related_name="subcalls")
objects = InheritanceManager()
class PythonCall(Call):
- func_name = models.CharField(max_length=255)
-
- @property
- def name(self):
- return self.func_name
+ pass
@@ -9,23 +9,23 @@
class BaseTraceTests(TestCase):
- def _request(self, method, url_name, url_kwargs, **kwargs):
- status_code = url_kwargs.pop("status_code", 200)
- url = reverse(url_name, kwargs=url_kwargs)
- response = getattr(self.client, method)(url, **kwargs)
+ def _request(self, method, url_name, **kwargs):
+ status_code = kwargs.pop("status_code", 200)
+ meth_kwargs = {}
+ for key in ["data", "content_type"]:
+ if key in kwargs:
+ meth_kwargs[key] = kwargs.pop(key)
+
+ url = reverse(url_name, kwargs=kwargs)
+ response = getattr(self.client, method)(url, **meth_kwargs)
self.assertEqual(response.status_code, status_code)
return response
def get(self, url_name, **kwargs):
- return self._request("get", url_name, url_kwargs=kwargs)
+ return self._request("get", url_name, **kwargs)
def post(self, url_name, **kwargs):
- url_kwargs = kwargs
- kwargs = {}
- for key in ["data", "content_type"]:
- if key in url_kwargs:
- kwargs[key] = url_kwargs.pop(key)
- return self._request("post", url_name, url_kwargs=url_kwargs, **kwargs)
+ return self._request("post", url_name, **kwargs)
def assert_attributes(self, obj, **kwargs):
@@ -34,7 +34,8 @@ def assert_attributes(self, obj, **kwargs):
def assert_json_response(self, response, expected_data):
self.assertEqual(response["Content-type"], "application/json")
- self.assertEqual(json.loads(response.content), expected_data)
+ actual_data = json.loads(response.content)
+ self.assertEqual(actual_data, expected_data)
def create_log(self):
@@ -52,12 +53,21 @@ def create_env_option(self, log):
def create_timeline_event(self, event_type, start_time, end_time, log):
return log.timeline_events.create(event_type=event_type, start_time=start_time, end_time=end_time)
- def create_call(self, cls, log, parent=None):
+ def create_call(self, cls, log, name="", start_time=0, end_time=0,
+ parent=None):
+
if parent is None:
call_depth = 0
else:
call_depth = parent.call_depth + 1
- return cls.objects.create(log=log, start_time=5, end_time=5.5, call_depth=call_depth)
+ return cls.objects.create(
+ log=log,
+ parent=parent,
+ name=name,
+ start_time=start_time,
+ end_time=end_time,
+ call_depth=call_depth
+ )
class InheritanceManagerTests(BaseTraceTests):
@@ -156,26 +166,26 @@ def test_calls(self):
"calls": [
{
"type": "python",
- "func_name": "time",
+ "name": "time",
"start_time": 0.0,
"end_time": 0.1,
"subcalls": [],
},
{
"type": "python",
- "func_name": "main",
+ "name": "main",
"start_time": 0.11,
"end_time": 2.0,
"subcalls": [
{
"type": "python",
- "func_name": "f",
+ "name": "f",
"start_time": .2,
"end_time": .4,
"subcalls": [
{
"type": "python",
- "func_name": "g",
+ "name": "g",
"start_time": .25,
"end_time": .35,
"subcalls": [],
@@ -184,7 +194,7 @@ def test_calls(self):
},
{
"type": "python",
- "func_name": "h",
+ "name": "h",
"start_time": 1.2,
"end_time": 1.8,
"subcalls": [],
@@ -200,10 +210,10 @@ def test_calls(self):
(PythonCall, log, "f", 0.2, 0.4, 1),
(PythonCall, log, "g", 0.25, 0.35, 2),
(PythonCall, log, "h", 1.2, 1.8, 1),
- ], attrgetter("__class__", "log", "func_name", "start_time", "end_time", "call_depth"))
+ ], attrgetter("__class__", "log", "name", "start_time", "end_time", "call_depth"))
subcall = log.calls.get(call_depth=2)
- self.assertEqual(subcall.func_name, "g")
- self.assertEqual(subcall.parent.func_name, "f")
+ self.assertEqual(subcall.name, "g")
+ self.assertEqual(subcall.parent.name, "f")
def test_trace(self):
response = self.post("trace_upload", data=json.dumps({
@@ -263,30 +273,75 @@ def test_trace(self):
self.assert_attributes(chunk, start_line=87, end_line=90)
class CallDataTests(BaseTraceTests):
- def test_basic_data(self):
+ def test_basic_timeline_data(self):
log = self.create_log()
call1 = self.create_call(PythonCall, log=log)
call2 = self.create_call(PythonCall, log=log)
call3 = self.create_call(PythonCall, log=log, parent=call2)
- response = self.get("trace_call_data", id=log.id)
+ response = self.get("trace_timeline_call_data", id=log.id)
self.assert_json_response(response, [
{
- "name": call1.func_name,
+ "name": call1.name,
"start_time": call1.start_time,
"end_time": call1.end_time,
"depth": 0,
+ "id": call1.id,
},
{
- "name": call2.func_name,
+ "name": call2.name,
"start_time": call2.start_time,
"end_time": call2.end_time,
"depth": 0,
+ "id": call2.id,
},
{
- "name": call3.func_name,
+ "name": call3.name,
"start_time": call3.start_time,
"end_time": call3.end_time,
"depth": 1,
+ "id": call3.id,
},
- ])
+ ])
+
+ def test_call_data(self):
+ log = self.create_log()
+ call1 = self.create_call(PythonCall, log=log, name="a", start_time=0, end_time=2)
+ call2 = self.create_call(PythonCall, log=log, name="b", start_time=.5, end_time=1.5, parent=call1)
+ call3 = self.create_call(PythonCall, log=log, name="a", start_time=2, end_time=3)
+
+ response = self.get("trace_call_data", id=log.id, data={
+ "call_id": call2.id
+ })
+ self.assert_json_response(response, {
+ "call_time": 1,
+ "call_exclusive_time": 1,
+ "func_time": 1,
+ "func_exclusive_time": 1,
+ })
+
+ response = self.get("trace_call_data", id=log.id, data={
+ "call_id": call1.id
+ })
+ self.assert_json_response(response, {
+ "call_time": 2,
+ "call_exclusive_time": 1,
+ "func_time": 3,
+ "func_exclusive_time": 2,
+ })
+
+ def test_call_data_multiple_subcall(self):
+ log = self.create_log()
+ call = self.create_call(PythonCall, log=log, name="a", start_time=0, end_time=10)
+ for i in xrange(1, 9):
+ self.create_call(PythonCall, log=log, name="b", start_time=i, end_time=i+1, parent=call)
+
+ response = self.get("trace_call_data", id=log.id, data={
+ "call_id": call.id,
+ })
+ self.assert_json_response(response, {
+ "call_time": 10,
+ "call_exclusive_time": 2,
+ "func_time": 10,
+ "func_exclusive_time": 2,
+ })
@@ -10,5 +10,6 @@
url(r"^(?P<id>\d+)/compiled/(?P<compiled_id>\d+)/$", views.trace_compiled_detail, name="trace_compiled_detail"),
url(r"^(?P<id>\d+)/timeline/$", views.trace_timeline, name="trace_timeline"),
+ url(r"^(?P<id>\d+)/timeline-call-data\.json", views.trace_timeline_call_data, name="trace_timeline_call_data"),
url(r"^(?P<id>\d+)/call-data\.json", views.trace_call_data, name="trace_call_data"),
)
Oops, something went wrong.

0 comments on commit 663b6d8

Please sign in to comment.