Commit 6c53291a authored by nduca@chromium.org's avatar nduca@chromium.org

about:tracing support for TRACE_ASYNC_START/FINISH events.


Review URL: http://codereview.chromium.org/9706010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126978 0039d316-1c4b-4281-b951-d872f2087c98
parent 07414c07
// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
...@@ -191,6 +191,13 @@ cr.define('tracing', function() { ...@@ -191,6 +191,13 @@ cr.define('tracing', function() {
this.buildPerThreadCpuSlicesFromCpuState(); this.buildPerThreadCpuSlicesFromCpuState();
}, },
/**
* Called by the TimelineModel after all other importers have imported their
* events.
*/
finalizeImport: function() {
},
/** /**
* Builds the cpuSlices array on each thread based on our knowledge of what * Builds the cpuSlices array on each thread based on our knowledge of what
* each Cpu is doing. This is done only for TimelineThreads that are * each Cpu is doing. This is done only for TimelineThreads that are
......
[
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 826, "ph": "B",
"name": "A", "args": {}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 827, "ph": "B",
"name": "Asub", "args": {}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 829, "ph": "B",
"name": "NonNest", "args": {"id": "1", "ui-nest": "0"}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 830, "ph": "B",
"name": "NonNest", "args": {"id": "2", "ui-nest": "0"}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 831, "ph": "E",
"name": "Asub", "args": {}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 832, "ph": "E",
"name": "NonNest", "args": {"id": "1", "ui-nest": "0"}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 833, "ph": "E",
"name": "NonNest", "args": {"id": "2", "ui-nest": "0"}},
{"cat": "PERF", "pid": 22630, "tid": 22630, "ts": 834, "ph": "E",
"name": "A", "args": {}},
{"cat": "PERF", "pid": 22630, "tid": 22631, "ts": 827, "ph": "B",
"name": "A", "args": {}},
{"cat": "PERF", "pid": 22630, "tid": 22631, "ts": 854, "ph": "E",
"name": "A", "args": {}}
]
\ No newline at end of file
/* /*
Copyright (c) 2012 The Chromium Authors. All rights reserved. * Copyright (c) 2012 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be * Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. * found in the LICENSE file.
*/ */
.timeline * {
-webkit-user-select: none;
cursor: default;
}
.timeline-drag-box { .timeline-drag-box {
background-color: rgba(0, 0, 255, 0.25); background-color: rgba(0, 0, 255, 0.25);
...@@ -15,6 +20,27 @@ found in the LICENSE file. ...@@ -15,6 +20,27 @@ found in the LICENSE file.
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
display: -webkit-box; display: -webkit-box;
padding: 1px 0; padding: 1px 0;
position: relative;
}
.timeline-track-close-button {
left: 0px;
position: absolute;
top: 0px;
}
.timeline-track-button {
color: rgba(0,0,0,0.1);
font-size: 10px;
height: 12px;
text-align: center;
width: 12px;
}
.timeline-track-button:hover {
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 25%;
box-shadow: 0 0 .05em rgba(0,0,0,0.4);
color: rgba(0,0,0,0.5);
} }
.timeline-thread-track:not(:first-child) { .timeline-thread-track:not(:first-child) {
...@@ -61,4 +87,5 @@ found in the LICENSE file. ...@@ -61,4 +87,5 @@ found in the LICENSE file.
.timeline-counter-track { .timeline-counter-track {
height: 30px; height: 30px;
position: relative;
} }
...@@ -396,7 +396,7 @@ cr.define('tracing', function() { ...@@ -396,7 +396,7 @@ cr.define('tracing', function() {
threads.forEach(function(thread) { threads.forEach(function(thread) {
var track = new tracing.TimelineThreadTrack(); var track = new tracing.TimelineThreadTrack();
track.heading = thread.userFriendlyName + ':'; track.heading = thread.userFriendlyName + ':';
track.tooltip = thread.userFriendlyDetials; track.tooltip = thread.userFriendlyDetails;
track.headingWidth = maxHeadingWidth; track.headingWidth = maxHeadingWidth;
track.viewport = this.viewport_; track.viewport = this.viewport_;
track.thread = thread; track.thread = thread;
...@@ -661,16 +661,17 @@ cr.define('tracing', function() { ...@@ -661,16 +661,17 @@ cr.define('tracing', function() {
}, },
onMouseDown_: function(e) { onMouseDown_: function(e) {
rect = this.tracks_.getClientRects()[0]; var canv = this.firstCanvas;
var rect = this.tracks_.getClientRects()[0];
var inside = rect && var inside = rect &&
e.clientX >= rect.left && e.clientX >= rect.left &&
e.clientX < rect.right && e.clientX < rect.right &&
e.clientY >= rect.top && e.clientY >= rect.top &&
e.clientY < rect.bottom; e.clientY < rect.bottom &&
e.x >= canv.offsetLeft;
if (!inside) if (!inside)
return; return;
var canv = this.firstCanvas;
var pos = { var pos = {
x: e.clientX - canv.offsetLeft, x: e.clientX - canv.offsetLeft,
y: e.clientY - canv.offsetTop y: e.clientY - canv.offsetTop
...@@ -743,6 +744,10 @@ cr.define('tracing', function() { ...@@ -743,6 +744,10 @@ cr.define('tracing', function() {
}, },
onDblClick_: function(e) { onDblClick_: function(e) {
var canv = this.firstCanvas;
if (e.x < canv.offsetLeft)
return;
var scale = 4; var scale = 4;
if (e.shiftKey) if (e.shiftKey)
scale = 1 / scale; scale = 1 / scale;
......
...@@ -18,44 +18,47 @@ found in the LICENSE file. ...@@ -18,44 +18,47 @@ found in the LICENSE file.
</head> </head>
<body> <body>
<script> <script>
'use strict';
var TimelineCpu = tracing.TimelineCpu; var TimelineCpu = tracing.TimelineCpu;
var TimelineSlice = tracing.TimelineSlice; var TimelineSlice = tracing.TimelineSlice;
var TimelineThreadSlice = tracing.TimelineThreadSlice;
var TimelineProcess = tracing.TimelineProcess; var TimelineProcess = tracing.TimelineProcess;
var TimelineThread = tracing.TimelineThread; var TimelineThread = tracing.TimelineThread;
var TimelineModel = tracing.TimelineModel; var TimelineModel = tracing.TimelineModel;
var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
// Helper function to create a slice.
function newAsyncSlice(start, duration, startThread, endThread) {
var s = new TimelineAsyncSlice('a', 0, start);
s.duration = duration;
s.startThread = startThread;
s.endThread = endThread;
return s;
}
function testThreadBounds_Empty() { function testThreadBounds_Empty() {
var t = new TimelineThread(undefined, 1); var t = new TimelineThread(new TimelineProcess(7), 1);
t.updateBounds(); t.updateBounds();
assertEquals(undefined, t.minTimestamp); assertEquals(undefined, t.minTimestamp);
assertEquals(undefined, t.maxTimestamp); assertEquals(undefined, t.maxTimestamp);
} }
function testThreadBounds_SubRow() { function testThreadBounds_SubRow() {
var t = new TimelineThread(undefined, 1); var t = new TimelineThread(new TimelineProcess(7), 1);
t.subRows[0].push(new TimelineSlice('a', 0, 1, {}, 3)); t.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 3));
t.updateBounds(); t.updateBounds();
assertEquals(1, t.minTimestamp); assertEquals(1, t.minTimestamp);
assertEquals(4, t.maxTimestamp); assertEquals(4, t.maxTimestamp);
} }
function testThreadBounds_NestedSubrow() { function testThreadBounds_AsyncSliceGroup() {
var t = new TimelineThread(undefined, 1); var t = new TimelineThread(new TimelineProcess(7), 1);
t.nonNestedSubRows.push([]); t.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 3));
t.nonNestedSubRows[0].push(new TimelineSlice('a', 0, 1, {}, 3)); t.asyncSlices.push(newAsyncSlice(0.1, 5, t, t));
t.updateBounds(); t.updateBounds();
assertEquals(1, t.minTimestamp); assertEquals(0.1, t.minTimestamp);
assertEquals(4, t.maxTimestamp); assertEquals(5.1, t.maxTimestamp);
}
function testThreadBounds_SubRowAndNonNestedSubRow() {
var t = new TimelineThread(undefined, 1);
t.subRows[0].push(new TimelineSlice('a', 0, 0.5, {}, 3));
t.nonNestedSubRows.push([]);
t.nonNestedSubRows[0].push(new TimelineSlice('b', 0, 1, {}, 4.5));
t.updateBounds();
assertEquals(0.5, t.minTimestamp);
assertEquals(5.5, t.maxTimestamp);
} }
function testModelBounds_EmptyModel() { function testModelBounds_EmptyModel() {
...@@ -73,10 +76,10 @@ function testModelBounds_OneEmptyThread() { ...@@ -73,10 +76,10 @@ function testModelBounds_OneEmptyThread() {
assertEquals(undefined, m.maxTimestamp); assertEquals(undefined, m.maxTimestamp);
} }
function testModelBounds_OneThrad() { function testModelBounds_OneThread() {
var m = new TimelineModel(); var m = new TimelineModel();
var t = m.getOrCreateProcess(1).getOrCreateThread(1); var t = m.getOrCreateProcess(1).getOrCreateThread(1);
t.subRows[0].push(new TimelineSlice('a', 0, 1, {}, 3)); t.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 3));
m.updateBounds(); m.updateBounds();
assertEquals(1, m.minTimestamp); assertEquals(1, m.minTimestamp);
assertEquals(4, m.maxTimestamp); assertEquals(4, m.maxTimestamp);
...@@ -85,7 +88,7 @@ function testModelBounds_OneThrad() { ...@@ -85,7 +88,7 @@ function testModelBounds_OneThrad() {
function testModelBounds_OneThreadAndOneEmptyThread() { function testModelBounds_OneThreadAndOneEmptyThread() {
var m = new TimelineModel(); var m = new TimelineModel();
var t1 = m.getOrCreateProcess(1).getOrCreateThread(1); var t1 = m.getOrCreateProcess(1).getOrCreateThread(1);
t1.subRows[0].push(new TimelineSlice('a', 0, 1, {}, 3)); t1.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 3));
var t2 = m.getOrCreateProcess(1).getOrCreateThread(1); var t2 = m.getOrCreateProcess(1).getOrCreateThread(1);
m.updateBounds(); m.updateBounds();
assertEquals(1, m.minTimestamp); assertEquals(1, m.minTimestamp);
...@@ -99,7 +102,6 @@ function testCpuBounds_Empty() { ...@@ -99,7 +102,6 @@ function testCpuBounds_Empty() {
assertEquals(undefined, cpu.maxTimestamp); assertEquals(undefined, cpu.maxTimestamp);
} }
function testCpuBounds_OneSlice() { function testCpuBounds_OneSlice() {
var cpu = new TimelineCpu(undefined, 1); var cpu = new TimelineCpu(undefined, 1);
cpu.slices.push(new TimelineSlice('a', 0, 1, {}, 3)); cpu.slices.push(new TimelineSlice('a', 0, 1, {}, 3));
...@@ -109,16 +111,120 @@ function testCpuBounds_OneSlice() { ...@@ -109,16 +111,120 @@ function testCpuBounds_OneSlice() {
} }
function testModelBounds_OneCpu() { function testModelBounds_OneCpu() {
var m = new TimelineModel();
var cpu = m.getOrCreateCpu(1);
cpu.slices.push(new TimelineSlice('a', 0, 1, {}, 3));
m.updateBounds();
assertEquals(1, m.minTimestamp);
assertEquals(4, m.maxTimestamp);
} }
function testModelBounds_OneCpuOneThread() { function testModelBounds_OneCpuOneThread() {
var m = new TimelineModel();
var cpu = m.getOrCreateCpu(1);
cpu.slices.push(new TimelineSlice('a', 0, 1, {}, 3));
var t = m.getOrCreateProcess(1).getOrCreateThread(1);
t.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 4));
m.updateBounds();
assertEquals(1, m.minTimestamp);
assertEquals(5, m.maxTimestamp);
}
function testPTIDFromPidAndTid() {
assertEquals('1:2', TimelineThread.getPTIDFromPidAndTid(1, 2));
}
function testAsyncSliceGroupBounds_Empty() {
var g = new TimelineAsyncSliceGroup(name);
g.updateBounds();
assertEquals(undefined, g.minTimestamp);
assertEquals(undefined, g.maxTimestamp);
}
function testAsyncSliceGroupBounds_Basic() {
var p1 = new TimelineProcess(1);
var t1 = new TimelineThread(p1, 1);
var g = new TimelineAsyncSliceGroup('a');
g.push(newAsyncSlice(0, 1, t1, t1));
g.push(newAsyncSlice(1, 1.5, t1, t1));
assertEquals(2, g.length);
g.updateBounds();
assertEquals(0, g.minTimestamp);
assertEquals(2.5, g.maxTimestamp);
}
function testAsyncSliceGroup_rebuildSubRows_twoNonOverlappingSlices() {
var p1 = new TimelineProcess(1);
var t1 = new TimelineThread(p1, 1);
var g = new TimelineAsyncSliceGroup('a');
g.slices.push(newAsyncSlice(0, 1, t1, t1));
g.slices.push(newAsyncSlice(1, 1, t1, t1));
assertEquals(1, g.subRows.length);
assertEquals(2, g.subRows[0].length);
assertEquals(g.slices[0], g.subRows[0][0]);
assertEquals(g.slices[1], g.subRows[0][1]);
}
function testAsyncSliceGroup_rebuildSubRows_twoOverlappingSlices() {
var p1 = new TimelineProcess(1);
var t1 = new TimelineThread(p1, 1);
var g = new TimelineAsyncSliceGroup('a');
g.slices.push(newAsyncSlice(0, 1, t1, t1));
g.slices.push(newAsyncSlice(0, 1.5, t1, t1));
g.updateBounds();
assertEquals(2, g.subRows.length);
assertEquals(1, g.subRows[0].length);
assertEquals(g.slices[0], g.subRows[0][0]);
assertEquals(1, g.subRows[1].length);
assertEquals(g.slices[1], g.subRows[1][0]);
}
function testAsyncSliceGroup_rebuildSubRows_threePartlyOverlappingSlices() {
var p1 = new TimelineProcess(1);
var t1 = new TimelineThread(p1, 1);
var g = new TimelineAsyncSliceGroup('a');
g.slices.push(newAsyncSlice(0, 1, t1, t1));
g.slices.push(newAsyncSlice(0, 1.5, t1, t1));
g.slices.push(newAsyncSlice(1, 1.5, t1, t1));
g.updateBounds();
assertEquals(2, g.subRows.length);
assertEquals(2, g.subRows[0].length);
assertEquals(g.slices[0], g.subRows[0][0]);
assertEquals(g.slices[2], g.subRows[0][1]);
assertEquals(1, g.subRows[1].length);
assertEquals(g.slices[1], g.subRows[1][0]);
}
function testAsyncSliceGroup_computeSubGroups_twoThreadSpecificSlices() {
var p1 = new TimelineProcess(1);
var t1 = new TimelineThread(p1, 1);
var t2 = new TimelineThread(p1, 2);
var g = new TimelineAsyncSliceGroup('a');
g.slices.push(newAsyncSlice(0, 1, t1, t1));
g.slices.push(newAsyncSlice(0, 1, t2, t2));
var subGroups = g.computeSubGroups();
assertEquals(2, subGroups.length);
assertEquals(g.name, subGroups[0].name);
assertEquals(1, subGroups[0].slices.length);
assertEquals(g.slices[0], subGroups[0].slices[0]);
assertEquals(g.name, subGroups[1].name);
assertEquals(1, subGroups[1].slices.length);
assertEquals(g.slices[1], subGroups[1].slices[0]);
} }
function testModelCanImportEmpty() { function testModelCanImportEmpty() {
var m; var m;
m = new TimelineModel([]); m = new TimelineModel([]);
m = new TimelineModel(""); m = new TimelineModel('');
} }
</script> </script>
</body> </body>
......
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html> <html>
<!-- <!--
Copyright (c) 2011 The Chromium Authors. All rights reserved. Copyright (c) 2012 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. found in the LICENSE file.
--> -->
...@@ -35,9 +35,6 @@ found in the LICENSE file. ...@@ -35,9 +35,6 @@ found in the LICENSE file.
<div class="timeline-test" src="./tests/instance_counters.json"> <div class="timeline-test" src="./tests/instance_counters.json">
</div> </div>
<div class="timeline-test" src="./tests/nonnested_trace.json">
</div>
<div class="timeline-test" src="./tests/tall_trace.json"> <div class="timeline-test" src="./tests/tall_trace.json">
</div> </div>
...@@ -50,7 +47,7 @@ found in the LICENSE file. ...@@ -50,7 +47,7 @@ found in the LICENSE file.
<div class="timeline-test" src="./tests/main_thread_has_unclosed_slices.json"> <div class="timeline-test" src="./tests/main_thread_has_unclosed_slices.json">
</div> </div>
<div class="timeline-test" src="./tests/linux_perf_simple.txt"> <div class="timeline-test" src="./tests/async_begin_end.json">
</div> </div>
<script> <script>
......
...@@ -111,6 +111,17 @@ cr.define('tracing', function() { ...@@ -111,6 +111,17 @@ cr.define('tracing', function() {
} }
}; };
function addCloseButtonElement(el) {
var closeEl = document.createElement('div');
closeEl.classList.add('timeline-track-button');
closeEl.classList.add('timeline-track-close-button');
closeEl.textContent = String.fromCharCode(215); // &times;
closeEl.addEventListener('click', function() {
el.style.display = 'None';
});
el.appendChild(closeEl);
}
/** /**
* Visualizes a TimelineThread using a series of of TimelineSliceTracks. * Visualizes a TimelineThread using a series of of TimelineSliceTracks.
* @constructor * @constructor
...@@ -181,12 +192,18 @@ cr.define('tracing', function() { ...@@ -181,12 +192,18 @@ cr.define('tracing', function() {
track.height = '4px'; track.height = '4px';
} }
for (var srI = 0; srI < this.thread_.nonNestedSubRows.length; ++srI) { if (this.thread_.asyncSlices.length) {
this.addTrack_(this.thread_.nonNestedSubRows[srI]); var subRows = this.thread_.asyncSlices.subRows;
for (var srI = 0; srI < subRows.length; srI++) {
var track = this.addTrack_(subRows[srI]);
track.asyncStyle = true;
}
} }
for (var srI = 0; srI < this.thread_.subRows.length; ++srI) {
for (var srI = 0; srI < this.thread_.subRows.length; srI++) {
this.addTrack_(this.thread_.subRows[srI]); this.addTrack_(this.thread_.subRows[srI]);
} }
if (this.tracks_.length > 0) { if (this.tracks_.length > 0) {
if (this.thread_.cpuSlices) { if (this.thread_.cpuSlices) {
this.tracks_[1].heading = this.heading_; this.tracks_[1].heading = this.heading_;
...@@ -197,6 +214,7 @@ cr.define('tracing', function() { ...@@ -197,6 +214,7 @@ cr.define('tracing', function() {
} }
} }
} }
addCloseButtonElement(this);
} }
}; };
...@@ -264,6 +282,7 @@ cr.define('tracing', function() { ...@@ -264,6 +282,7 @@ cr.define('tracing', function() {
this.tracks_[0].heading = this.heading_; this.tracks_[0].heading = this.heading_;
this.tracks_[0].tooltip = this.tooltip_; this.tracks_[0].tooltip = this.tooltip_;
} }
addCloseButtonElement(this);
} }
}; };
...@@ -366,58 +385,63 @@ cr.define('tracing', function() { ...@@ -366,58 +385,63 @@ cr.define('tracing', function() {
}; };
/** /**
* A pair representing an elided string and world-coordinate width * A pair representing an elided string and world-coordinate width
* to draw it. * to draw it.
* @constructor * @constructor
*/ */
function ElidedStringWidthPair(string, width) { function ElidedStringWidthPair(string, width) {
this.string = string; this.string = string;
this.width = width; this.width = width;
} }
/**
* A cache for elided strings.
* @constructor
*/
function ElidedTitleCache() {
}
ElidedTitleCache.prototype = {
/** /**
* A cache for elided strings. * Return elided text.
* @constructor * @param {track} A timeline slice track or other object that defines
*/ * functions labelWidth() and labelWidthWorld().
function ElidedTitleCache() { * @param {pixWidth} Pixel width.
} * @param {title} Original title text.
* @param {width} Drawn width in world coords.
ElidedTitleCache.prototype = { * @param {sliceDuration} Where the title must fit (in world coords).
/** * @return {ElidedStringWidthPair} Elided string and width.
* Return elided text. */
* @param {track} A timeline slice track or other object that defines get: function(track, pixWidth, title, width, sliceDuration) {
* functions labelWidth() and labelWidthWorld(). var elidedDict = elidedTitleCacheDict[title];
* @param {pixWidth} Pixel width. if (!elidedDict) {
* @param {title} Original title text. elidedDict = {};
* @param {width} Drawn width in world coords. elidedTitleCacheDict[title] = elidedDict;
* @param {sliceDuration} Where the title must fit (in world coords). }
* @return {ElidedStringWidthPair} Elided string and width. var elidedDictForPixWidth = elidedDict[pixWidth];
*/ if (!elidedDictForPixWidth) {
get: function(track, pixWidth, title, width, sliceDuration) { elidedDict[pixWidth] = {};
var elidedDict = elidedTitleCacheDict[title]; elidedDictForPixWidth = elidedDict[pixWidth];
if (!elidedDict) { }
elidedDict = {}; var stringWidthPair = elidedDictForPixWidth[sliceDuration];
elidedTitleCacheDict[title] = elidedDict; if (stringWidthPair === undefined) {
} var newtitle = title;
var stringWidthPair = elidedDict[sliceDuration]; var elided = false;
if (stringWidthPair === undefined) { while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
var newtitle = title; newtitle = newtitle.substring(0, newtitle.length * 0.75);
var elided = false; elided = true;
while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) { }
newtitle = newtitle.substring(0, newtitle.length * 0.75); if (elided && newtitle.length > 3)
elided = true; newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
} stringWidthPair = new ElidedStringWidthPair(
if (elided && newtitle.length > 3) newtitle,
newtitle = newtitle.substring(0, newtitle.length - 3) + '...'; track.labelWidth(newtitle));
stringWidthPair = new ElidedStringWidthPair( elidedDictForPixWidth[sliceDuration] = stringWidthPair;
newtitle, }
track.labelWidth(newtitle)); return stringWidthPair;
elidedDict[sliceDuration] = stringWidthPair; }
} };
return stringWidthPair;
},
};
/** /**
* A track that displays an array of TimelineSlice objects. * A track that displays an array of TimelineSlice objects.
...@@ -431,18 +455,28 @@ cr.define('tracing', function() { ...@@ -431,18 +455,28 @@ cr.define('tracing', function() {
__proto__: CanvasBasedTrack.prototype, __proto__: CanvasBasedTrack.prototype,
/** /**
* Should we elide text on trace labels? * Should we elide text on trace labels?
* Without eliding, text that is too wide isn't drawn at all. * Without eliding, text that is too wide isn't drawn at all.
* Disable if you feel this causes a performance problem. * Disable if you feel this causes a performance problem.
* This is a default value that can be overridden in tracks for testing. * This is a default value that can be overridden in tracks for testing.
* @const * @const
*/ */
SHOULD_ELIDE_TEXT: true, SHOULD_ELIDE_TEXT: true,
decorate: function() { decorate: function() {
this.classList.add('timeline-slice-track'); this.classList.add('timeline-slice-track');
this.elidedTitleCache = new ElidedTitleCache(); this.elidedTitleCache = new ElidedTitleCache();
this.asyncStyle_ = false;
},
get asyncStyle() {
return this.asyncStyle_;
},
set asyncStyle(v) {
this.asyncStyle_ = !!v;
this.invalidate();
}, },
get slices() { get slices() {
...@@ -503,6 +537,8 @@ cr.define('tracing', function() { ...@@ -503,6 +537,8 @@ cr.define('tracing', function() {
vp.applyTransformToCanavs(ctx); vp.applyTransformToCanavs(ctx);
// Slices. // Slices.
if (this.asyncStyle_)
ctx.globalAlpha = 0.25;
var tr = new tracing.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth, var tr = new tracing.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth,
2 * pixWidth, viewRWorld, pallette); 2 * pixWidth, viewRWorld, pallette);
tr.setYandH(0, canvasH); tr.setYandH(0, canvasH);
...@@ -560,12 +596,12 @@ cr.define('tracing', function() { ...@@ -560,12 +596,12 @@ cr.define('tracing', function() {
var drawnWidth = this.labelWidth(drawnTitle); var drawnWidth = this.labelWidth(drawnTitle);
if (shouldElide && if (shouldElide &&
this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) { this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) {
var elidedValues = this.elidedTitleCache.get( var elidedValues = this.elidedTitleCache.get(
this, pixWidth, this, pixWidth,
drawnTitle, drawnWidth, drawnTitle, drawnWidth,
slice.duration); slice.duration);
drawnTitle = elidedValues.string; drawnTitle = elidedValues.string;
drawnWidth = elidedValues.width; drawnWidth = elidedValues.width;
} }
if (drawnWidth * pixWidth < slice.duration) { if (drawnWidth * pixWidth < slice.duration) {
var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration); var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
...@@ -691,7 +727,7 @@ cr.define('tracing', function() { ...@@ -691,7 +727,7 @@ cr.define('tracing', function() {
const logOf10 = Math.log(10); const logOf10 = Math.log(10);
function log10(x) { function log10(x) {
return Math.log(x) / logOf10;; return Math.log(x) / logOf10;
} }
TimelineViewportTrack.prototype = { TimelineViewportTrack.prototype = {
...@@ -729,10 +765,10 @@ cr.define('tracing', function() { ...@@ -729,10 +765,10 @@ cr.define('tracing', function() {
// exceeds the ideal mark distance. // exceeds the ideal mark distance.
var divisors = [10, 5, 2, 1]; var divisors = [10, 5, 2, 1];
for (var i = 0; i < divisors.length; ++i) { for (var i = 0; i < divisors.length; ++i) {
var tightenedGuess = conservativeGuess / divisors[i] var tightenedGuess = conservativeGuess / divisors[i];
if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix) if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix)
continue; continue;
majorMarkDistanceWorld = conservativeGuess / divisors[i-1]; majorMarkDistanceWorld = conservativeGuess / divisors[i - 1];
break; break;
} }
if (majorMarkDistanceWorld < 100) { if (majorMarkDistanceWorld < 100) {
...@@ -748,8 +784,8 @@ cr.define('tracing', function() { ...@@ -748,8 +784,8 @@ cr.define('tracing', function() {
var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld); var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld);
var firstMajorMark = var firstMajorMark =
Math.floor(viewLWorld / majorMarkDistanceWorld) * Math.floor(viewLWorld / majorMarkDistanceWorld) *
majorMarkDistanceWorld; majorMarkDistanceWorld;
var minorTickH = Math.floor(canvasH * 0.25); var minorTickH = Math.floor(canvasH * 0.25);
...@@ -841,6 +877,7 @@ cr.define('tracing', function() { ...@@ -841,6 +877,7 @@ cr.define('tracing', function() {
decorate: function() { decorate: function() {
this.classList.add('timeline-counter-track'); this.classList.add('timeline-counter-track');
addCloseButtonElement(this);
}, },
get counter() { get counter() {
......
...@@ -40,15 +40,30 @@ found in the LICENSE file. ...@@ -40,15 +40,30 @@ found in the LICENSE file.
<script> <script>
</script> </script>
<script> <script>
var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
var TimelineCounter = tracing.TimelineCounter; var TimelineCounter = tracing.TimelineCounter;
var TimelineCounterTrack = tracing.TimelineCounterTrack; var TimelineCounterTrack = tracing.TimelineCounterTrack;
var TimelineCpu = tracing.TimelineCpu; var TimelineCpu = tracing.TimelineCpu;
var TimelineCpuTrack = tracing.TimelineCpuTrack; var TimelineCpuTrack = tracing.TimelineCpuTrack;
var TimelineProcess = tracing.TimelineProcess;
var TimelineSliceTrack = tracing.TimelineSliceTrack; var TimelineSliceTrack = tracing.TimelineSliceTrack;
var TimelineSlice = tracing.TimelineSlice; var TimelineSlice = tracing.TimelineSlice;
var TimelineThread = tracing.TimelineThread;
var TimelineThreadSlice = tracing.TimelineThreadSlice;
var TimelineThreadTrack = tracing.TimelineThreadTrack;
var TimelineViewport = tracing.TimelineViewport; var TimelineViewport = tracing.TimelineViewport;
var testDivs = {}; var testDivs = {};
// Helper function to create a slice.
function newAsyncSlice(start, duration, startThread, endThread) {
var s = new TimelineAsyncSlice('a', 0, start);
s.duration = duration;
s.startThread = startThread;
s.endThread = endThread;
return s;
}
function getTestDiv(name) { function getTestDiv(name) {
if (!testDivs[name]) { if (!testDivs[name]) {
testDivs[name] = document.createElement('div'); testDivs[name] = document.createElement('div');
...@@ -74,6 +89,23 @@ found in the LICENSE file. ...@@ -74,6 +89,23 @@ found in the LICENSE file.
track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end)); track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end));
} }
function testBasicSlicesWithAsyncFlag() {
var testEl = getTestDiv('testBasicSlicesWithAsyncFlag');
var track = TimelineSliceTrack();
testEl.appendChild(track);
track.asyncStyle = true;
track.heading = 'testBasicSlices+AsyncFlag';
track.slices = [
new TimelineSlice('a', 0, 1, {}, 1),
new TimelineSlice('b', 1, 2.1, {}, 4.8),
new TimelineSlice('b', 1, 7, {}, 0.5),
new TimelineSlice('c', 2, 7.6, {}, 0.4)
];
track.viewport = new TimelineViewport(testEl);
track.viewport.setPanAndScale(0,
track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end));
}
function testShrinkingSliceSizes() { function testShrinkingSliceSizes() {
var testEl = getTestDiv('testShrinkingSliceSizes'); var testEl = getTestDiv('testShrinkingSliceSizes');
var track = TimelineSliceTrack(); var track = TimelineSliceTrack();
...@@ -186,8 +218,8 @@ found in the LICENSE file. ...@@ -186,8 +218,8 @@ found in the LICENSE file.
/* You'll need visual inspection to test eliding with this one. */ /* You'll need visual inspection to test eliding with this one. */
function testElideVisualInspection() { function testElideVisualInspection() {
var optDicts = [ { trackName: 'elideOff', elide: false }, var optDicts = [{ trackName: 'elideOff', elide: false },
{ trackName: 'elideOn', elide: true } ]; { trackName: 'elideOn', elide: true }];
for (dictIndex in optDicts) { for (dictIndex in optDicts) {
dict = optDicts[dictIndex]; dict = optDicts[dictIndex];
var testEl = getTestDiv(dict.trackName); var testEl = getTestDiv(dict.trackName);
...@@ -198,7 +230,7 @@ found in the LICENSE file. ...@@ -198,7 +230,7 @@ found in the LICENSE file.
track.SHOULD_ELIDE_TEXT = false; track.SHOULD_ELIDE_TEXT = false;
} }
var tooLongTitle = 'Unless eliding this SHOULD NOT BE DISPLAYED. '; var tooLongTitle = 'Unless eliding this SHOULD NOT BE DISPLAYED. ';
var bigTitle = 'Very big title name that goes on longer ' + var bigTitle = 'Very big title name that goes on longer ' +
'than you may expect'; 'than you may expect';
testEl.appendChild(track); testEl.appendChild(track);
track.heading = 'Visual: ' + dict.trackName; track.heading = 'Visual: ' + dict.trackName;
...@@ -219,7 +251,7 @@ found in the LICENSE file. ...@@ -219,7 +251,7 @@ found in the LICENSE file.
var testEl = getTestDiv('testElide'); var testEl = getTestDiv('testElide');
var track = TimelineSliceTrack(); var track = TimelineSliceTrack();
testEl.appendChild(track); testEl.appendChild(track);
var bigtitle = 'Super duper long long title ' + var bigtitle = 'Super duper long long title ' +
'holy moly when did you get so verbose?'; 'holy moly when did you get so verbose?';
var smalltitle = 'small'; var smalltitle = 'small';
track.viewport = new TimelineViewport(testEl); track.viewport = new TimelineViewport(testEl);
...@@ -227,26 +259,26 @@ found in the LICENSE file. ...@@ -227,26 +259,26 @@ found in the LICENSE file.
track.slices = [ track.slices = [
// title, colorId, start, args, opt_duration // title, colorId, start, args, opt_duration
new TimelineSlice(bigtitle, 0, 1, {}, 1), new TimelineSlice(bigtitle, 0, 1, {}, 1),
new TimelineSlice(smalltitle, 1, 2, {}, 1), new TimelineSlice(smalltitle, 1, 2, {}, 1)
]; ];
track.viewport = new TimelineViewport(testEl); track.viewport = new TimelineViewport(testEl);
track.viewport.setPanAndScale(0, track.viewport.setPanAndScale(0,
track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end)); track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end));
var stringWidthPair = undefined; var stringWidthPair = undefined;
var pixWidth = track.viewport_.xViewVectorToWorld(1); var pixWidth = track.viewport_.xViewVectorToWorld(1);
// Small titles on big slices are not elided. // Small titles on big slices are not elided.
stringWidthPair = track.elidedTitleCache.get(track, pixWidth, smalltitle, stringWidthPair = track.elidedTitleCache.get(track, pixWidth, smalltitle,
track.labelWidth(smalltitle), 1); track.labelWidth(smalltitle), 1);
assertEquals(smalltitle, stringWidthPair.string); assertEquals(smalltitle, stringWidthPair.string);
// Keep shrinking the slice until eliding starts. // Keep shrinking the slice until eliding starts.
var elidedWhenSmallEnough = false; var elidedWhenSmallEnough = false;
for (var sliceLength = 1; sliceLength >= 0.00001; sliceLength /= 2.0) { for (var sliceLength = 1; sliceLength >= 0.00001; sliceLength /= 2.0) {
stringWidthPair = track.elidedTitleCache.get(track, pixWidth, smalltitle, stringWidthPair = track.elidedTitleCache.get(track, pixWidth, smalltitle,
track.labelWidth(smalltitle), sliceLength); track.labelWidth(smalltitle), sliceLength);
if (stringWidthPair.string.length < smalltitle.length) { if (stringWidthPair.string.length < smalltitle.length) {
elidedWhenSmallEnough = true; elidedWhenSmallEnough = true;
break; break;
} }
} }
assertTrue(elidedWhenSmallEnough); assertTrue(elidedWhenSmallEnough);
...@@ -256,12 +288,70 @@ found in the LICENSE file. ...@@ -256,12 +288,70 @@ found in the LICENSE file.
for (var x = 0; x < 10; x++) { for (var x = 0; x < 10; x++) {
superBigTitle += bigtitle; superBigTitle += bigtitle;
} }
stringWidthPair = track.elidedTitleCache.get(track, pixWidth, stringWidthPair = track.elidedTitleCache.get(track, pixWidth,
superBigTitle, track.labelWidth(superBigTitle), 1); superBigTitle, track.labelWidth(superBigTitle), 1);
assertTrue(stringWidthPair.string.length < superBigTitle.length); assertTrue(stringWidthPair.string.length < superBigTitle.length);
// And elided text ends with ... // And elided text ends with ...
var len = stringWidthPair.string.length; var len = stringWidthPair.string.length;
assertEquals('...', stringWidthPair.string.substring(len-3, len)); assertEquals('...', stringWidthPair.string.substring(len - 3, len));
}
function testTimelineThreadTrackWithRegularSlices() {
var testEl = getTestDiv('testTimelineThreadTrackWithRegularSlices');
var track = TimelineThreadTrack();
testEl.appendChild(track);
track.heading = 'testTimelineThreadTrackWithRegularSlices';
var thread = new TimelineThread(new TimelineProcess(7), 1);
thread.subRows = [
[
new TimelineThreadSlice('a', 0, 1, {}, 1),
new TimelineThreadSlice('b', 1, 2.1, {}, 4.8),
new TimelineThreadSlice('b', 1, 7, {}, 0.5),
new TimelineThreadSlice('c', 2, 7.6, {}, 0.4)
],
[
new TimelineThreadSlice('d', 3, 1.1, {}, 0.8),
new TimelineThreadSlice('e', 4, 7.1, {}, 0.3)
]
];
thread.updateBounds();
track.heading = 'thread regular';
track.headingWidth = '150px';
track.toolTip = thread.userFriendlyDetails + ':';
track.thread = thread;
track.viewport = new TimelineViewport(testEl);
track.viewport.setPanAndScale(0,
track.clientWidth / (1.1 * (thread.maxTimestamp - thread.minTimestamp));
}
function testTimelineThreadTrackWithRegularAndAsyncSlices() {
var testEl = getTestDiv('testTimelineThreadTrackWithAsyncSlices');
var track = TimelineThreadTrack();
testEl.appendChild(track);
var thread = new TimelineThread(new TimelineProcess(7), 1);
thread.subRows = [
[
new TimelineThreadSlice('a', 0, 1, {}, 1),
new TimelineThreadSlice('b', 1, 2.1, {}, 4.8),
new TimelineThreadSlice('b', 1, 7, {}, 0.5),
new TimelineThreadSlice('c', 2, 7.6, {}, 0.4)
],
[
new TimelineThreadSlice('d', 3, 1.1, {}, 0.8),
new TimelineThreadSlice('e', 4, 7.1, {}, 0.3)
]
];
thread.asyncSlices.push(newAsyncSlice(1.2, 7.2 - 1.2, thread, thread));
thread.asyncSlices.push(newAsyncSlice(1.3, 7.3 - 1.3, thread, thread));
thread.updateBounds();
track.heading = 'thread regular + async';
track.headingWidth = '150px';
track.toolTip = thread.userFriendlyDetails + ':';
track.thread = thread;
track.viewport = new TimelineViewport(testEl);
track.viewport.setPanAndScale(0,
track.clientWidth /
(1.1 * (thread.maxTimestamp - thread.minTimestamp)));
} }
</script> </script>
......
...@@ -23,7 +23,7 @@ found in the LICENSE file. ...@@ -23,7 +23,7 @@ found in the LICENSE file.
function testCanImportEmpty() { function testCanImportEmpty() {
self.assertFalse(tracing.TraceEventImporter.canImport([])); self.assertFalse(tracing.TraceEventImporter.canImport([]));
self.assertFalse(tracing.TraceEventImporter.canImport("")); self.assertFalse(tracing.TraceEventImporter.canImport(''));
} }
function testBasicSingleThreadNonnestedParsing() { function testBasicSingleThreadNonnestedParsing() {
...@@ -559,6 +559,33 @@ function testImportStringWithMissingCloseSquareBracketAndNewline() { ...@@ -559,6 +559,33 @@ function testImportStringWithMissingCloseSquareBracketAndNewline() {
assertEquals(1, m.numProcesses); assertEquals(1, m.numProcesses);
} }
function testStartFinishOneSliceOneThread() {
var events = [
// Time is intentionally out of order.
{name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53,
ph: 'F', id: 72},
{name: 'a', args: {}, pid: 52, ts: 524, cat: 'foo', tid: 53,
ph: 'S', id: 72, args: {foo: 'bar'}}
];
var m = new tracing.TimelineModel(events);
var t = m.processes[52].threads[53];
assertNotUndefined(t);
assertEquals(1, t.asyncSlices.slices.length);
assertEquals('a', t.asyncSlices.slices[0].title);
assertEquals(72, t.asyncSlices.slices[0].id);
assertEquals('bar', t.asyncSlices.slices[0].args.foo);
assertEquals(0, t.asyncSlices.slices[0].start);
assertAlmostEquals((60 - 24) / 1000, t.asyncSlices.slices[0].duration);
assertEquals(t, t.asyncSlices.slices[0].startThread);
assertEquals(t, t.asyncSlices.slices[0].endThread);
}
// TODO(nduca): one slice, two threads
// TODO(nduca): one slice, two pids
// TODO(nduca): one slice that doesnt end
// TODO(nduca): one slice that is missing a start
</script> </script>
</body> </body>
</html> </html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment