Commit 40bf2099 authored by pdr@chromium.org's avatar pdr@chromium.org

[Devtools] Replace "Stacks" with "Causes"

This patch switches to "Causes" for explaining timeline records. The
major change is "Stacks" have been renamed "Causes". This is prepwork
for two upcoming projects: invalidation tracking which will fill out many
more causes, and javascript samples which are easily confused with
the "stack" term. If the causes checkbox is not checked, no initiator
or stack information will be shown.

Some minor rewording of the causes has also been done. For example,
the stack for a layout now reads "First layout invalidation" instead of
"Layout invalidated".

A screenshot is available at http://pr.gg/justcause3.png

BUG=410701

Review URL: https://codereview.chromium.org/631573002

git-svn-id: svn://svn.chromium.org/blink/trunk@183626 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 73c92caf
Test that causes are correctly generated for various types of events.
Running: testTimerInstall
PASS - record contained Timer installed: setTimeoutFunction @ setTimeoutFunction.js:
Running: testRequestAnimationFrame
PASS - record contained Animation frame requested: requestAnimationFrameFunction @ requestAnimationFrameFunction.js:
Running: testStyleRecalc
PASS - record contained Stack when first invalidated: styleRecalcFunction @ styleRecalcFunction.js:
Running: testLayout
PASS - record contained Stack when layout was forced: layoutFunction @ layoutFunction.js:
PASS - record contained First layout invalidation: layoutFunction @ layoutFunction.js:
<html>
<head>
<script src="../../http/tests/inspector/inspector-test.js"></script>
<script src="../../http/tests/inspector/timeline-test.js"></script>
<script>
function test()
{
function checkStringContains(string, contains)
{
var doesContain = string.indexOf(contains) >= 0;
InspectorTest.check(doesContain, contains + " should be present in " + string);
InspectorTest.addResult("PASS - record contained " + contains);
}
InspectorTest.runTestSuite([
function testTimerInstall(next)
{
function setTimeoutFunction(callback)
{
setTimeout(callback, 0);
}
var source = setTimeoutFunction.toString();
source += "\n//@ sourceURL=setTimeoutFunction.js";
InspectorTest.evaluateInPage(source);
InspectorTest.invokeAsyncWithTimeline("setTimeoutFunction", finishAndRunNextTest);
function finishAndRunNextTest()
{
var linkifier = new WebInspector.Linkifier();
var record = InspectorTest.findFirstTimelineRecord("TimerFire");
InspectorTest.check(record, "Should receive a TimerFire record.");
var contentHelper = new WebInspector.TimelineDetailsContentHelper(record.traceEvent().thread.target(), linkifier, true);
WebInspector.TracingTimelineUIUtils._generateCauses(record.traceEvent(), contentHelper);
var causes = contentHelper.element.textContent;
InspectorTest.check(causes, "Should generate causes");
checkStringContains(causes, "Timer installed: setTimeoutFunction @ setTimeoutFunction.js:");
next();
}
},
function testRequestAnimationFrame(next)
{
function requestAnimationFrameFunction(callback)
{
requestAnimationFrame(callback);
}
var source = requestAnimationFrameFunction.toString();
source += "\n//@ sourceURL=requestAnimationFrameFunction.js";
InspectorTest.evaluateInPage(source);
InspectorTest.invokeAsyncWithTimeline("requestAnimationFrameFunction", finishAndRunNextTest);
function finishAndRunNextTest()
{
var linkifier = new WebInspector.Linkifier();
var record = InspectorTest.findFirstTimelineRecord("FireAnimationFrame");
InspectorTest.check(record, "Should receive a FireAnimationFrame record.");
var contentHelper = new WebInspector.TimelineDetailsContentHelper(record.traceEvent().thread.target(), linkifier, true);
WebInspector.TracingTimelineUIUtils._generateCauses(record.traceEvent(), contentHelper);
var causes = contentHelper.element.textContent;
InspectorTest.check(causes, "Should generate causes");
checkStringContains(causes, "Animation frame requested: requestAnimationFrameFunction @ requestAnimationFrameFunction.js:");
next();
}
},
function testStyleRecalc(next)
{
function styleRecalcFunction(callback)
{
var element = document.getElementById("testElement");
element.style.backgroundColor = "papayawhip";
callback();
}
var source = styleRecalcFunction.toString();
source += "\n//@ sourceURL=styleRecalcFunction.js";
InspectorTest.evaluateInPage(source);
InspectorTest.invokeAsyncWithTimeline("styleRecalcFunction", finishAndRunNextTest);
function finishAndRunNextTest()
{
var linkifier = new WebInspector.Linkifier();
var record = InspectorTest.findFirstTimelineRecord("RecalculateStyles");
InspectorTest.check(record, "Should receive a RecalculateStyles record.");
var contentHelper = new WebInspector.TimelineDetailsContentHelper(record.traceEvent().thread.target(), linkifier, true);
WebInspector.TracingTimelineUIUtils._generateCauses(record.traceEvent(), contentHelper);
var causes = contentHelper.element.textContent;
InspectorTest.check(causes, "Should generate causes");
checkStringContains(causes, "Stack when first invalidated: styleRecalcFunction @ styleRecalcFunction.js:");
next();
}
},
function testLayout(next)
{
function layoutFunction(callback)
{
var element = document.getElementById("testElement");
element.style.width = "200px";
var forceLayout = element.offsetWidth;
callback();
}
var source = layoutFunction.toString();
source += "\n//@ sourceURL=layoutFunction.js";
InspectorTest.evaluateInPage(source);
InspectorTest.invokeAsyncWithTimeline("layoutFunction", finishAndRunNextTest);
function finishAndRunNextTest()
{
var linkifier = new WebInspector.Linkifier();
var record = InspectorTest.findFirstTimelineRecord("Layout");
InspectorTest.check(record, "Should receive a Layout record.");
var contentHelper = new WebInspector.TimelineDetailsContentHelper(record.traceEvent().thread.target(), linkifier, true);
WebInspector.TracingTimelineUIUtils._generateCauses(record.traceEvent(), contentHelper);
var causes = contentHelper.element.textContent;
InspectorTest.check(causes, "Should generate causes");
checkStringContains(causes, "Stack when layout was forced: layoutFunction @ layoutFunction.js:");
checkStringContains(causes, "First layout invalidation: layoutFunction @ layoutFunction.js:");
next();
}
}
]);
}
</script>
</head>
<body onload="runTest()">
<p>
Test that causes are correctly generated for various types of events.
</p>
<div id="testElement"></div>
</body>
</html>
...@@ -42,7 +42,7 @@ WebInspector.TimelineFlameChartDataProvider = function(model, frameModel) ...@@ -42,7 +42,7 @@ WebInspector.TimelineFlameChartDataProvider = function(model, frameModel)
this._frameModel = frameModel; this._frameModel = frameModel;
this._font = "12px " + WebInspector.fontFamily(); this._font = "12px " + WebInspector.fontFamily();
this._linkifier = new WebInspector.Linkifier(); this._linkifier = new WebInspector.Linkifier();
this._captureStacksSetting = WebInspector.settings.createSetting("timelineCaptureStacks", true); this._captureCausesSetting = WebInspector.settings.createSetting("timelineCaptureCauses", true);
this._filters = []; this._filters = [];
this.addFilter(WebInspector.TracingTimelineUIUtils.hiddenEventsFilter()); this.addFilter(WebInspector.TracingTimelineUIUtils.hiddenEventsFilter());
this.addFilter(new WebInspector.TracingTimelineModel.ExclusiveEventNameFilter([WebInspector.TracingTimelineModel.RecordType.Program])); this.addFilter(new WebInspector.TracingTimelineModel.ExclusiveEventNameFilter([WebInspector.TracingTimelineModel.RecordType.Program]));
...@@ -198,7 +198,7 @@ WebInspector.TimelineFlameChartDataProvider.prototype = { ...@@ -198,7 +198,7 @@ WebInspector.TimelineFlameChartDataProvider.prototype = {
{ {
var levelCount = this._appendAsyncEvents(threadTitle, asyncEvents); var levelCount = this._appendAsyncEvents(threadTitle, asyncEvents);
if (Runtime.experiments.isEnabled("timelineJSCPUProfile")) { if (Runtime.experiments.isEnabled("timelineJSCPUProfile")) {
if (this._captureStacksSetting.get()) { if (this._captureCausesSetting.get()) {
var jsFrameEvents = this._generateJSFrameEvents(syncEvents); var jsFrameEvents = this._generateJSFrameEvents(syncEvents);
syncEvents = jsFrameEvents.mergeOrdered(syncEvents, WebInspector.TracingModel.Event.orderedCompareStartTime); syncEvents = jsFrameEvents.mergeOrdered(syncEvents, WebInspector.TracingModel.Event.orderedCompareStartTime);
} }
......
...@@ -143,11 +143,11 @@ WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallba ...@@ -143,11 +143,11 @@ WebInspector.TimelineModel.forAllRecords = function(recordsArray, preOrderCallba
WebInspector.TimelineModel.prototype = { WebInspector.TimelineModel.prototype = {
/** /**
* @param {boolean} captureStacks * @param {boolean} captureCauses
* @param {boolean} captureMemory * @param {boolean} captureMemory
* @param {boolean} capturePictures * @param {boolean} capturePictures
*/ */
startRecording: function(captureStacks, captureMemory, capturePictures) startRecording: function(captureCauses, captureMemory, capturePictures)
{ {
}, },
......
...@@ -42,11 +42,11 @@ WebInspector.TimelineModelImpl.prototype = { ...@@ -42,11 +42,11 @@ WebInspector.TimelineModelImpl.prototype = {
}, },
/** /**
* @param {boolean} captureStacks * @param {boolean} captureCauses
* @param {boolean} captureMemory * @param {boolean} captureMemory
* @param {boolean} capturePictures * @param {boolean} capturePictures
*/ */
startRecording: function(captureStacks, captureMemory, capturePictures) startRecording: function(captureCauses, captureMemory, capturePictures)
{ {
console.assert(!capturePictures, "Legacy timeline does not support capturing pictures"); console.assert(!capturePictures, "Legacy timeline does not support capturing pictures");
this.reset(); this.reset();
...@@ -54,7 +54,7 @@ WebInspector.TimelineModelImpl.prototype = { ...@@ -54,7 +54,7 @@ WebInspector.TimelineModelImpl.prototype = {
console.assert(this._currentTarget); console.assert(this._currentTarget);
this._clientInitiatedRecording = true; this._clientInitiatedRecording = true;
var maxStackFrames = captureStacks ? 30 : 0; var maxStackFrames = captureCauses ? 30 : 0;
var includeGPUEvents = Runtime.experiments.isEnabled("gpuTimeline"); var includeGPUEvents = Runtime.experiments.isEnabled("gpuTimeline");
var liveEvents = [ WebInspector.TimelineModel.RecordType.BeginFrame, var liveEvents = [ WebInspector.TimelineModel.RecordType.BeginFrame,
WebInspector.TimelineModel.RecordType.DrawFrame, WebInspector.TimelineModel.RecordType.DrawFrame,
......
...@@ -347,11 +347,11 @@ WebInspector.TimelinePanel.prototype = { ...@@ -347,11 +347,11 @@ WebInspector.TimelinePanel.prototype = {
panelStatusBarElement.appendChild(flameChartToggleButton.element); panelStatusBarElement.appendChild(flameChartToggleButton.element);
} }
this._captureStacksSetting = WebInspector.settings.createSetting("timelineCaptureStacks", true); this._captureCausesSetting = WebInspector.settings.createSetting("timelineCaptureCauses", true);
this._captureStacksSetting.addChangeListener(this._refreshViews, this); this._captureCausesSetting.addChangeListener(this._refreshViews, this);
panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Stacks"), panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Causes"),
this._captureStacksSetting, this._captureCausesSetting,
WebInspector.UIString("Capture JavaScript stack on every timeline event"))); WebInspector.UIString("Capture causes for timeline events (e.g., stack traces)")));
this._captureMemorySetting = WebInspector.settings.createSetting("timelineCaptureMemory", false); this._captureMemorySetting = WebInspector.settings.createSetting("timelineCaptureMemory", false);
panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Memory"), panelStatusBarElement.appendChild(this._createSettingCheckbox(WebInspector.UIString("Memory"),
this._captureMemorySetting, this._captureMemorySetting,
...@@ -639,7 +639,7 @@ WebInspector.TimelinePanel.prototype = { ...@@ -639,7 +639,7 @@ WebInspector.TimelinePanel.prototype = {
_startRecording: function(userInitiated) _startRecording: function(userInitiated)
{ {
this._userInitiatedRecording = userInitiated; this._userInitiatedRecording = userInitiated;
this._model.startRecording(this._captureStacksSetting.get(), this._captureMemorySetting.get(), this._captureLayersAndPicturesSetting && this._captureLayersAndPicturesSetting.get()); this._model.startRecording(this._captureCausesSetting.get(), this._captureMemorySetting.get(), this._captureLayersAndPicturesSetting && this._captureLayersAndPicturesSetting.get());
if (this._lazyFrameModel) if (this._lazyFrameModel)
this._lazyFrameModel.setMergeRecords(false); this._lazyFrameModel.setMergeRecords(false);
......
...@@ -121,11 +121,11 @@ WebInspector.TracingTimelineModel.VirtualThread = function(name) ...@@ -121,11 +121,11 @@ WebInspector.TracingTimelineModel.VirtualThread = function(name)
WebInspector.TracingTimelineModel.prototype = { WebInspector.TracingTimelineModel.prototype = {
/** /**
* @param {boolean} captureStacks * @param {boolean} captureCauses
* @param {boolean} captureMemory * @param {boolean} captureMemory
* @param {boolean} capturePictures * @param {boolean} capturePictures
*/ */
startRecording: function(captureStacks, captureMemory, capturePictures) startRecording: function(captureCauses, captureMemory, capturePictures)
{ {
function disabledByDefault(category) function disabledByDefault(category)
{ {
...@@ -137,7 +137,7 @@ WebInspector.TracingTimelineModel.prototype = { ...@@ -137,7 +137,7 @@ WebInspector.TracingTimelineModel.prototype = {
disabledByDefault("devtools.timeline.frame"), disabledByDefault("devtools.timeline.frame"),
WebInspector.TracingModel.ConsoleEventCategory WebInspector.TracingModel.ConsoleEventCategory
]; ];
if (captureStacks) { if (captureCauses) {
categoriesArray.push(disabledByDefault("devtools.timeline.stack")); categoriesArray.push(disabledByDefault("devtools.timeline.stack"));
if (Runtime.experiments.isEnabled("timelineJSCPUProfile")) { if (Runtime.experiments.isEnabled("timelineJSCPUProfile")) {
this._jsProfilerStarted = true; this._jsProfilerStarted = true;
......
...@@ -538,9 +538,7 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -538,9 +538,7 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
var recordTypes = WebInspector.TracingTimelineModel.RecordType; var recordTypes = WebInspector.TracingTimelineModel.RecordType;
// The messages may vary per event.name; // This message may vary per event.name;
var callSiteStackTraceLabel;
var callStackLabel;
var relatedNodeLabel; var relatedNodeLabel;
var contentHelper = new WebInspector.TimelineDetailsContentHelper(event.thread.target(), linkifier, true); var contentHelper = new WebInspector.TimelineDetailsContentHelper(event.thread.target(), linkifier, true);
...@@ -555,9 +553,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -555,9 +553,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(delta)); contentHelper.appendTextRow(WebInspector.UIString("Collected"), Number.bytesToString(delta));
break; break;
case recordTypes.TimerFire: case recordTypes.TimerFire:
callSiteStackTraceLabel = WebInspector.UIString("Timer installed");
// Fall-through intended.
case recordTypes.TimerInstall: case recordTypes.TimerInstall:
case recordTypes.TimerRemove: case recordTypes.TimerRemove:
contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), eventData["timerId"]); contentHelper.appendTextRow(WebInspector.UIString("Timer ID"), eventData["timerId"]);
...@@ -567,7 +562,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -567,7 +562,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
} }
break; break;
case recordTypes.FireAnimationFrame: case recordTypes.FireAnimationFrame:
callSiteStackTraceLabel = WebInspector.UIString("Animation frame requested");
contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), eventData["id"]); contentHelper.appendTextRow(WebInspector.UIString("Callback ID"), eventData["id"]);
break; break;
case recordTypes.FunctionCall: case recordTypes.FunctionCall:
...@@ -619,7 +613,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -619,7 +613,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
break; break;
case recordTypes.RecalculateStyles: // We don't want to see default details. case recordTypes.RecalculateStyles: // We don't want to see default details.
contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), event.args["elementCount"]); contentHelper.appendTextRow(WebInspector.UIString("Elements affected"), event.args["elementCount"]);
callStackLabel = WebInspector.UIString("Styles recalculation forced");
break; break;
case recordTypes.Layout: case recordTypes.Layout:
var beginData = event.args["beginData"]; var beginData = event.args["beginData"];
...@@ -627,8 +620,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -627,8 +620,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), beginData["totalObjects"]); contentHelper.appendTextRow(WebInspector.UIString("Layout tree size"), beginData["totalObjects"]);
contentHelper.appendTextRow(WebInspector.UIString("Layout scope"), contentHelper.appendTextRow(WebInspector.UIString("Layout scope"),
beginData["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document")); beginData["partialLayout"] ? WebInspector.UIString("Partial") : WebInspector.UIString("Whole document"));
callSiteStackTraceLabel = WebInspector.UIString("Layout invalidated");
callStackLabel = WebInspector.UIString("Layout forced");
relatedNodeLabel = WebInspector.UIString("Layout root"); relatedNodeLabel = WebInspector.UIString("Layout root");
break; break;
case recordTypes.ConsoleTime: case recordTypes.ConsoleTime:
...@@ -662,15 +653,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -662,15 +653,6 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
if (eventData && eventData["scriptName"] && event.name !== recordTypes.FunctionCall) if (eventData && eventData["scriptName"] && event.name !== recordTypes.FunctionCall)
contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), eventData["scriptName"], eventData["scriptLine"]); contentHelper.appendLocationRow(WebInspector.UIString("Function Call"), eventData["scriptName"], eventData["scriptLine"]);
if (initiator) {
var callSiteStackTrace = initiator.stackTrace;
if (callSiteStackTrace)
contentHelper.appendStackTrace(callSiteStackTraceLabel || WebInspector.UIString("Call Site stack"), callSiteStackTrace);
}
var eventStackTrace = event.stackTrace;
if (eventStackTrace)
contentHelper.appendStackTrace(callStackLabel || WebInspector.UIString("Call Stack"), eventStackTrace);
var warning = event.warning; var warning = event.warning;
if (warning) { if (warning) {
var div = document.createElement("div"); var div = document.createElement("div");
...@@ -679,10 +661,51 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct ...@@ -679,10 +661,51 @@ WebInspector.TracingTimelineUIUtils._buildTraceEventDetailsSynchronously = funct
} }
if (event.previewElement) if (event.previewElement)
contentHelper.appendElementRow(WebInspector.UIString("Preview"), event.previewElement); contentHelper.appendElementRow(WebInspector.UIString("Preview"), event.previewElement);
if (event.stackTrace || (event.initiator && event.initiator.stackTrace))
WebInspector.TracingTimelineUIUtils._generateCauses(event, contentHelper);
fragment.appendChild(contentHelper.element); fragment.appendChild(contentHelper.element);
return fragment; return fragment;
} }
/**
* @param {!WebInspector.TracingModel.Event} event
* @param {!WebInspector.TimelineDetailsContentHelper} contentHelper
*/
WebInspector.TracingTimelineUIUtils._generateCauses = function(event, contentHelper)
{
var recordTypes = WebInspector.TracingTimelineModel.RecordType;
var callSiteStackLabel;
var stackLabel;
switch (event.name) {
case recordTypes.TimerFire:
callSiteStackLabel = WebInspector.UIString("Timer installed");
break;
case recordTypes.FireAnimationFrame:
callSiteStackLabel = WebInspector.UIString("Animation frame requested");
break;
case recordTypes.RecalculateStyles:
stackLabel = WebInspector.UIString("Stack when style recalculation was forced");
break;
case recordTypes.Layout:
callSiteStackLabel = WebInspector.UIString("First layout invalidation");
stackLabel = WebInspector.UIString("Stack when layout was forced");
break;
}
// Direct cause.
if (event.stackTrace)
contentHelper.appendStackTrace(stackLabel || WebInspector.UIString("Stack when this event occurred"), event.stackTrace);
// Indirect cause / invalidation.
var initiator = event.initiator;
if (initiator && initiator.stackTrace)
contentHelper.appendStackTrace(callSiteStackLabel || WebInspector.UIString("Stack when first invalidated"), initiator.stackTrace);
}
/** /**
* @param {!Object} total * @param {!Object} total
* @param {!WebInspector.TracingTimelineModel} model * @param {!WebInspector.TracingTimelineModel} model
......
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