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
// found in the LICENSE file.
......@@ -191,6 +191,13 @@ cr.define('tracing', function() {
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
* 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.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
*/
* Copyright (c) 2012 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
.timeline * {
-webkit-user-select: none;
cursor: default;
}
.timeline-drag-box {
background-color: rgba(0, 0, 255, 0.25);
......@@ -15,6 +20,27 @@ found in the LICENSE file.
-webkit-box-orient: vertical;
display: -webkit-box;
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) {
......@@ -61,4 +87,5 @@ found in the LICENSE file.
.timeline-counter-track {
height: 30px;
position: relative;
}
......@@ -396,7 +396,7 @@ cr.define('tracing', function() {
threads.forEach(function(thread) {
var track = new tracing.TimelineThreadTrack();
track.heading = thread.userFriendlyName + ':';
track.tooltip = thread.userFriendlyDetials;
track.tooltip = thread.userFriendlyDetails;
track.headingWidth = maxHeadingWidth;
track.viewport = this.viewport_;
track.thread = thread;
......@@ -661,16 +661,17 @@ cr.define('tracing', function() {
},
onMouseDown_: function(e) {
rect = this.tracks_.getClientRects()[0];
var canv = this.firstCanvas;
var rect = this.tracks_.getClientRects()[0];
var inside = rect &&
e.clientX >= rect.left &&
e.clientX < rect.right &&
e.clientY >= rect.top &&
e.clientY < rect.bottom;
e.clientY < rect.bottom &&
e.x >= canv.offsetLeft;
if (!inside)
return;
var canv = this.firstCanvas;
var pos = {
x: e.clientX - canv.offsetLeft,
y: e.clientY - canv.offsetTop
......@@ -743,6 +744,10 @@ cr.define('tracing', function() {
},
onDblClick_: function(e) {
var canv = this.firstCanvas;
if (e.x < canv.offsetLeft)
return;
var scale = 4;
if (e.shiftKey)
scale = 1 / scale;
......
......@@ -18,44 +18,47 @@ found in the LICENSE file.
</head>
<body>
<script>
'use strict';
var TimelineCpu = tracing.TimelineCpu;
var TimelineSlice = tracing.TimelineSlice;
var TimelineThreadSlice = tracing.TimelineThreadSlice;
var TimelineProcess = tracing.TimelineProcess;
var TimelineThread = tracing.TimelineThread;
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() {
var t = new TimelineThread(undefined, 1);
var t = new TimelineThread(new TimelineProcess(7), 1);
t.updateBounds();
assertEquals(undefined, t.minTimestamp);
assertEquals(undefined, t.maxTimestamp);
}
function testThreadBounds_SubRow() {
var t = new TimelineThread(undefined, 1);
t.subRows[0].push(new TimelineSlice('a', 0, 1, {}, 3));
var t = new TimelineThread(new TimelineProcess(7), 1);
t.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 3));
t.updateBounds();
assertEquals(1, t.minTimestamp);
assertEquals(4, t.maxTimestamp);
}
function testThreadBounds_NestedSubrow() {
var t = new TimelineThread(undefined, 1);
t.nonNestedSubRows.push([]);
t.nonNestedSubRows[0].push(new TimelineSlice('a', 0, 1, {}, 3));
function testThreadBounds_AsyncSliceGroup() {
var t = new TimelineThread(new TimelineProcess(7), 1);
t.subRows[0].push(new TimelineThreadSlice('a', 0, 1, {}, 3));
t.asyncSlices.push(newAsyncSlice(0.1, 5, t, t));
t.updateBounds();
assertEquals(1, t.minTimestamp);
assertEquals(4, 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);
assertEquals(0.1, t.minTimestamp);
assertEquals(5.1, t.maxTimestamp);
}
function testModelBounds_EmptyModel() {
......@@ -73,10 +76,10 @@ function testModelBounds_OneEmptyThread() {
assertEquals(undefined, m.maxTimestamp);
}
function testModelBounds_OneThrad() {
function testModelBounds_OneThread() {
var m = new TimelineModel();
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();
assertEquals(1, m.minTimestamp);
assertEquals(4, m.maxTimestamp);
......@@ -85,7 +88,7 @@ function testModelBounds_OneThrad() {
function testModelBounds_OneThreadAndOneEmptyThread() {
var m = new TimelineModel();
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);
m.updateBounds();
assertEquals(1, m.minTimestamp);
......@@ -99,7 +102,6 @@ function testCpuBounds_Empty() {
assertEquals(undefined, cpu.maxTimestamp);
}
function testCpuBounds_OneSlice() {
var cpu = new TimelineCpu(undefined, 1);
cpu.slices.push(new TimelineSlice('a', 0, 1, {}, 3));
......@@ -109,16 +111,120 @@ function testCpuBounds_OneSlice() {
}
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() {
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() {
var m;
m = new TimelineModel([]);
m = new TimelineModel("");
m = new TimelineModel('');
}
</script>
</body>
......
<!DOCTYPE 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
found in the LICENSE file.
-->
......@@ -35,9 +35,6 @@ found in the LICENSE file.
<div class="timeline-test" src="./tests/instance_counters.json">
</div>
<div class="timeline-test" src="./tests/nonnested_trace.json">
</div>
<div class="timeline-test" src="./tests/tall_trace.json">
</div>
......@@ -50,7 +47,7 @@ found in the LICENSE file.
<div class="timeline-test" src="./tests/main_thread_has_unclosed_slices.json">
</div>
<div class="timeline-test" src="./tests/linux_perf_simple.txt">
<div class="timeline-test" src="./tests/async_begin_end.json">
</div>
<script>
......
......@@ -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.
* @constructor
......@@ -181,12 +192,18 @@ cr.define('tracing', function() {
track.height = '4px';
}
for (var srI = 0; srI < this.thread_.nonNestedSubRows.length; ++srI) {
this.addTrack_(this.thread_.nonNestedSubRows[srI]);
if (this.thread_.asyncSlices.length) {
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]);
}
if (this.tracks_.length > 0) {
if (this.thread_.cpuSlices) {
this.tracks_[1].heading = this.heading_;
......@@ -197,6 +214,7 @@ cr.define('tracing', function() {
}
}
}
addCloseButtonElement(this);
}
};
......@@ -264,6 +282,7 @@ cr.define('tracing', function() {
this.tracks_[0].heading = this.heading_;
this.tracks_[0].tooltip = this.tooltip_;
}
addCloseButtonElement(this);
}
};
......@@ -366,58 +385,63 @@ cr.define('tracing', function() {
};
/**
* A pair representing an elided string and world-coordinate width
* to draw it.
* @constructor
/**
* A pair representing an elided string and world-coordinate width
* to draw it.
* @constructor
*/
function ElidedStringWidthPair(string, width) {
this.string = string;
this.width = width;
}
function ElidedStringWidthPair(string, width) {
this.string = string;
this.width = width;
}
/**
* A cache for elided strings.
* @constructor
*/
function ElidedTitleCache() {
}
ElidedTitleCache.prototype = {
/**
* A cache for elided strings.
* @constructor
*/
function ElidedTitleCache() {
}
ElidedTitleCache.prototype = {
/**
* Return elided text.
* @param {track} A timeline slice track or other object that defines
* functions labelWidth() and labelWidthWorld().
* @param {pixWidth} Pixel width.
* @param {title} Original title text.
* @param {width} Drawn width in world coords.
* @param {sliceDuration} Where the title must fit (in world coords).
* @return {ElidedStringWidthPair} Elided string and width.
*/
get: function(track, pixWidth, title, width, sliceDuration) {
var elidedDict = elidedTitleCacheDict[title];
if (!elidedDict) {
elidedDict = {};
elidedTitleCacheDict[title] = elidedDict;
}
var stringWidthPair = elidedDict[sliceDuration];
if (stringWidthPair === undefined) {
var newtitle = title;
var elided = false;
while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
newtitle = newtitle.substring(0, newtitle.length * 0.75);
elided = true;
}
if (elided && newtitle.length > 3)
newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
stringWidthPair = new ElidedStringWidthPair(
newtitle,
track.labelWidth(newtitle));
elidedDict[sliceDuration] = stringWidthPair;
}
return stringWidthPair;
},
};
* Return elided text.
* @param {track} A timeline slice track or other object that defines
* functions labelWidth() and labelWidthWorld().
* @param {pixWidth} Pixel width.
* @param {title} Original title text.
* @param {width} Drawn width in world coords.
* @param {sliceDuration} Where the title must fit (in world coords).
* @return {ElidedStringWidthPair} Elided string and width.
*/
get: function(track, pixWidth, title, width, sliceDuration) {
var elidedDict = elidedTitleCacheDict[title];
if (!elidedDict) {
elidedDict = {};
elidedTitleCacheDict[title] = elidedDict;
}
var elidedDictForPixWidth = elidedDict[pixWidth];
if (!elidedDictForPixWidth) {
elidedDict[pixWidth] = {};
elidedDictForPixWidth = elidedDict[pixWidth];
}
var stringWidthPair = elidedDictForPixWidth[sliceDuration];
if (stringWidthPair === undefined) {
var newtitle = title;
var elided = false;
while (track.labelWidthWorld(newtitle, pixWidth) > sliceDuration) {
newtitle = newtitle.substring(0, newtitle.length * 0.75);
elided = true;
}
if (elided && newtitle.length > 3)
newtitle = newtitle.substring(0, newtitle.length - 3) + '...';
stringWidthPair = new ElidedStringWidthPair(
newtitle,
track.labelWidth(newtitle));
elidedDictForPixWidth[sliceDuration] = stringWidthPair;
}
return stringWidthPair;
}
};
/**
* A track that displays an array of TimelineSlice objects.
......@@ -431,18 +455,28 @@ cr.define('tracing', function() {
__proto__: CanvasBasedTrack.prototype,
/**
* Should we elide text on trace labels?
* Without eliding, text that is too wide isn't drawn at all.
* Disable if you feel this causes a performance problem.
* This is a default value that can be overridden in tracks for testing.
* @const
*/
/**
* Should we elide text on trace labels?
* Without eliding, text that is too wide isn't drawn at all.
* Disable if you feel this causes a performance problem.
* This is a default value that can be overridden in tracks for testing.
* @const
*/
SHOULD_ELIDE_TEXT: true,
decorate: function() {
this.classList.add('timeline-slice-track');
this.elidedTitleCache = new ElidedTitleCache();
this.asyncStyle_ = false;
},
get asyncStyle() {
return this.asyncStyle_;
},
set asyncStyle(v) {
this.asyncStyle_ = !!v;
this.invalidate();
},
get slices() {
......@@ -503,6 +537,8 @@ cr.define('tracing', function() {
vp.applyTransformToCanavs(ctx);
// Slices.
if (this.asyncStyle_)
ctx.globalAlpha = 0.25;
var tr = new tracing.FastRectRenderer(ctx, viewLWorld, 2 * pixWidth,
2 * pixWidth, viewRWorld, pallette);
tr.setYandH(0, canvasH);
......@@ -560,12 +596,12 @@ cr.define('tracing', function() {
var drawnWidth = this.labelWidth(drawnTitle);
if (shouldElide &&
this.labelWidthWorld(drawnTitle, pixWidth) > slice.duration) {
var elidedValues = this.elidedTitleCache.get(
this, pixWidth,
drawnTitle, drawnWidth,
slice.duration);
drawnTitle = elidedValues.string;
drawnWidth = elidedValues.width;
var elidedValues = this.elidedTitleCache.get(
this, pixWidth,
drawnTitle, drawnWidth,
slice.duration);
drawnTitle = elidedValues.string;
drawnWidth = elidedValues.width;
}
if (drawnWidth * pixWidth < slice.duration) {
var cX = vp.xWorldToView(slice.start + 0.5 * slice.duration);
......@@ -691,7 +727,7 @@ cr.define('tracing', function() {
const logOf10 = Math.log(10);
function log10(x) {
return Math.log(x) / logOf10;;
return Math.log(x) / logOf10;
}
TimelineViewportTrack.prototype = {
......@@ -729,10 +765,10 @@ cr.define('tracing', function() {
// exceeds the ideal mark distance.
var divisors = [10, 5, 2, 1];
for (var i = 0; i < divisors.length; ++i) {
var tightenedGuess = conservativeGuess / divisors[i]
var tightenedGuess = conservativeGuess / divisors[i];
if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix)
continue;
majorMarkDistanceWorld = conservativeGuess / divisors[i-1];
majorMarkDistanceWorld = conservativeGuess / divisors[i - 1];
break;
}
if (majorMarkDistanceWorld < 100) {
......@@ -748,8 +784,8 @@ cr.define('tracing', function() {
var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld);
var firstMajorMark =
Math.floor(viewLWorld / majorMarkDistanceWorld) *
majorMarkDistanceWorld;
Math.floor(viewLWorld / majorMarkDistanceWorld) *
majorMarkDistanceWorld;
var minorTickH = Math.floor(canvasH * 0.25);
......@@ -841,6 +877,7 @@ cr.define('tracing', function() {
decorate: function() {
this.classList.add('timeline-counter-track');
addCloseButtonElement(this);
},
get counter() {
......
......@@ -40,15 +40,30 @@ found in the LICENSE file.
<script>
</script>
<script>
var TimelineAsyncSlice = tracing.TimelineAsyncSlice;
var TimelineAsyncSliceGroup = tracing.TimelineAsyncSliceGroup;
var TimelineCounter = tracing.TimelineCounter;
var TimelineCounterTrack = tracing.TimelineCounterTrack;
var TimelineCpu = tracing.TimelineCpu;
var TimelineCpuTrack = tracing.TimelineCpuTrack;
var TimelineProcess = tracing.TimelineProcess;
var TimelineSliceTrack = tracing.TimelineSliceTrack;
var TimelineSlice = tracing.TimelineSlice;
var TimelineThread = tracing.TimelineThread;
var TimelineThreadSlice = tracing.TimelineThreadSlice;
var TimelineThreadTrack = tracing.TimelineThreadTrack;
var TimelineViewport = tracing.TimelineViewport;
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) {
if (!testDivs[name]) {
testDivs[name] = document.createElement('div');
......@@ -74,6 +89,23 @@ found in the LICENSE file.
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() {
var testEl = getTestDiv('testShrinkingSliceSizes');
var track = TimelineSliceTrack();
......@@ -186,8 +218,8 @@ found in the LICENSE file.
/* You'll need visual inspection to test eliding with this one. */
function testElideVisualInspection() {
var optDicts = [ { trackName: 'elideOff', elide: false },
{ trackName: 'elideOn', elide: true } ];
var optDicts = [{ trackName: 'elideOff', elide: false },
{ trackName: 'elideOn', elide: true }];
for (dictIndex in optDicts) {
dict = optDicts[dictIndex];
var testEl = getTestDiv(dict.trackName);
......@@ -198,7 +230,7 @@ found in the LICENSE file.
track.SHOULD_ELIDE_TEXT = false;
}
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';
testEl.appendChild(track);
track.heading = 'Visual: ' + dict.trackName;
......@@ -219,7 +251,7 @@ found in the LICENSE file.
var testEl = getTestDiv('testElide');
var track = TimelineSliceTrack();
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?';
var smalltitle = 'small';
track.viewport = new TimelineViewport(testEl);
......@@ -227,26 +259,26 @@ found in the LICENSE file.
track.slices = [
// title, colorId, start, args, opt_duration
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.setPanAndScale(0,
track.clientWidth / (1.1 * track.slices[track.slices.length - 1].end));
var stringWidthPair = undefined;
var pixWidth = track.viewport_.xViewVectorToWorld(1);
// 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);
assertEquals(smalltitle, stringWidthPair.string);
// Keep shrinking the slice until eliding starts.
var elidedWhenSmallEnough = false;
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);
if (stringWidthPair.string.length < smalltitle.length) {
elidedWhenSmallEnough = true;
break;
elidedWhenSmallEnough = true;
break;
}
}
assertTrue(elidedWhenSmallEnough);
......@@ -256,12 +288,70 @@ found in the LICENSE file.
for (var x = 0; x < 10; x++) {
superBigTitle += bigtitle;
}
stringWidthPair = track.elidedTitleCache.get(track, pixWidth,
stringWidthPair = track.elidedTitleCache.get(track, pixWidth,
superBigTitle, track.labelWidth(superBigTitle), 1);
assertTrue(stringWidthPair.string.length < superBigTitle.length);
// And elided text ends with ...
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>
......
......@@ -23,7 +23,7 @@ found in the LICENSE file.
function testCanImportEmpty() {
self.assertFalse(tracing.TraceEventImporter.canImport([]));
self.assertFalse(tracing.TraceEventImporter.canImport(""));
self.assertFalse(tracing.TraceEventImporter.canImport(''));
}
function testBasicSingleThreadNonnestedParsing() {
......@@ -559,6 +559,33 @@ function testImportStringWithMissingCloseSquareBracketAndNewline() {
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>
</body>
</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