Bug 1150295 - Display an icon next to frames in the call tree that contain viewable optimization data. r=vp,a=kwierso

This commit is contained in:
Jordan Santell 2015-06-17 14:31:15 -07:00
Родитель 4cf955fbee
Коммит 27921a0d16
7 изменённых файлов: 208 добавлений и 11 удалений

Просмотреть файл

@ -459,6 +459,7 @@ FrameNode.prototype = {
parsedData.categoryData = categoryData;
parsedData.isContent = this.isContent;
parsedData.isMetaCategory = this.isMetaCategory;
parsedData.hasOptimizations = this.hasOptimizations();
return this._data = parsedData;
},

Просмотреть файл

@ -16,6 +16,8 @@ const { AbstractTreeItem } = require("resource:///modules/devtools/AbstractTreeI
const MILLISECOND_UNITS = L10N.getStr("table.ms");
const PERCENTAGE_UNITS = L10N.getStr("table.percentage");
const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
const VIEW_OPTIMIZATIONS_TOOLTIP = L10N.getStr("table.view-optimizations.tooltiptext");
const CALL_TREE_INDENTATION = 16; // px
const DEFAULT_SORTING_PREDICATE = (frameA, frameB) => {
@ -85,10 +87,14 @@ const sum = vals => vals.reduce((a, b) => a + b, 0);
* An object specifying which cells are visible in the tree. Defaults to
* the caller's `visibleCells` if a caller exists, otherwise defaults
* to DEFAULT_VISIBLE_CELLS.
* @param boolean showOptimizationHint [optional]
* Whether or not to show an icon indicating if the frame has optimization
* data.
*/
function CallView({
caller, frame, level, hidden, inverted,
sortingPredicate, autoExpandDepth, visibleCells
sortingPredicate, autoExpandDepth, visibleCells,
showOptimizationHint
}) {
AbstractTreeItem.call(this, {
parent: caller,
@ -114,6 +120,7 @@ function CallView({
this.frame = frame;
this.hidden = hidden;
this.inverted = inverted;
this.showOptimizationHint = showOptimizationHint;
this._onUrlClick = this._onUrlClick.bind(this);
};
@ -256,6 +263,16 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
cell.setAttribute("type", "function");
cell.appendChild(arrowNode);
// Render optimization link to JIT view if the frame
// has optimizations
if (this.root.showOptimizationHint && frameInfo.hasOptimizations && !frameInfo.isMetaCategory) {
let icon = doc.createElement("description");
icon.setAttribute("tooltiptext", VIEW_OPTIMIZATIONS_TOOLTIP);
icon.setAttribute("type", "linkable");
icon.className = "opt-icon";
cell.appendChild(icon);
}
// Don't render a name label node if there's no function name. A different
// location label node will be rendered instead.
if (frameName) {
@ -280,6 +297,7 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
return cell;
},
_appendFunctionDetailsCells: function(doc, cell, frameInfo) {
if (frameInfo.fileName) {
let urlNode = doc.createElement("description");

Просмотреть файл

@ -137,6 +137,7 @@ skip-if = os == 'linux' # Bug 1172120
[browser_profiler_tree-view-08.js]
[browser_profiler_tree-view-09.js]
[browser_profiler_tree-view-10.js]
[browser_profiler_tree-view-11.js]
[browser_timeline-filters-01.js]
[browser_timeline-filters-02.js]
[browser_timeline-waterfall-background.js]

Просмотреть файл

@ -0,0 +1,153 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Tests that if a recording set `withJITOptimizations` on, then an
* icon is next to the frame with optimizations
*/
const RecordingUtils = devtools.require("devtools/performance/recording-utils");
const { CATEGORY_MASK } = devtools.require("devtools/performance/global");
function* spawnTest() {
let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin;
let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin;
let profilerData = { threads: [gThread] };
Services.prefs.setBoolPref(JIT_PREF, true);
Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
Services.prefs.setBoolPref(INVERT_PREF, false);
// Make two recordings, so we have one to switch to later, as the
// second one will have fake sample data
yield startRecording(panel);
yield stopRecording(panel);
yield DetailsView.selectView("js-calltree");
yield injectAndRenderProfilerData();
let rows = $$("#js-calltree-view .call-tree-item");
is(rows.length, 4, "4 call tree rows exist");
for (let row of rows) {
let name = $(".call-tree-name", row).value;
switch (name) {
case "A":
ok($(".opt-icon", row), "found an opt icon on a leaf node with opt data");
break;
case "C":
ok(!$(".opt-icon", row), "frames without opt data do not have an icon");
break;
case "Gecko":
ok(!$(".opt-icon", row), "meta category frames with opt data do not have an icon");
break;
case "(root)":
ok(!$(".opt-icon", row), "root frame certainly does not have opt data");
break;
default:
ok(false, `Unidentified frame: ${name}`);
break;
}
}
yield teardown(panel);
finish();
function *injectAndRenderProfilerData() {
// Get current recording and inject our mock data
info("Injecting mock profile data");
let recording = PerformanceController.getCurrentRecording();
recording._profile = profilerData;
// Force a rerender
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
JsCallTreeView.render(OverviewView.getTimeInterval());
yield rendered;
}
}
let gUniqueStacks = new RecordingUtils.UniqueStacks();
function uniqStr(s) {
return gUniqueStacks.getOrAddStringIndex(s);
}
// Since deflateThread doesn't handle deflating optimization info, use
// placeholder names A_O1, B_O2, and B_O3, which will be used to manually
// splice deduped opts into the profile.
let gThread = RecordingUtils.deflateThread({
samples: [{
time: 0,
frames: [
{ location: "(root)" }
]
}, {
time: 5,
frames: [
{ location: "(root)" },
{ location: "A (http://foo:1)" },
]
}, {
time: 5 + 1,
frames: [
{ location: "(root)" },
{ location: "C (http://foo/bar/baz:56)" }
]
}, {
time: 5 + 1 + 2,
frames: [
{ location: "(root)" },
{ category: CATEGORY_MASK("other"), location: "PlatformCode" }
]
}],
markers: []
}, gUniqueStacks);
// 3 RawOptimizationSites
let gRawSite1 = {
_testFrameInfo: { name: "A", line: "12", file: "@baz" },
line: 12,
column: 2,
types: [{
mirType: uniqStr("Object"),
site: uniqStr("A (http://foo/bar/bar:12)"),
typeset: [{
keyedBy: uniqStr("constructor"),
name: uniqStr("Foo"),
location: uniqStr("A (http://foo/bar/baz:12)")
}, {
keyedBy: uniqStr("primitive"),
location: uniqStr("self-hosted")
}]
}],
attempts: {
schema: {
outcome: 0,
strategy: 1
},
data: [
[uniqStr("Failure1"), uniqStr("SomeGetter1")],
[uniqStr("Failure2"), uniqStr("SomeGetter2")],
[uniqStr("Failure3"), uniqStr("SomeGetter3")]
]
}
};
gThread.frameTable.data.forEach((frame) => {
const LOCATION_SLOT = gThread.frameTable.schema.location;
const OPTIMIZATIONS_SLOT = gThread.frameTable.schema.optimizations;
let l = gThread.stringTable[frame[LOCATION_SLOT]];
switch (l) {
case "A (http://foo:1)":
frame[LOCATION_SLOT] = uniqStr("A (http://foo:1)");
frame[OPTIMIZATIONS_SLOT] = gRawSite1;
break;
case "PlatformCode":
frame[LOCATION_SLOT] = uniqStr("PlatformCode");
frame[OPTIMIZATIONS_SLOT] = gRawSite1;
break;
}
});

Просмотреть файл

@ -11,7 +11,7 @@ let JsCallTreeView = Heritage.extend(DetailsSubview, {
rerenderPrefs: [
"invert-call-tree",
"show-platform-data",
"flatten-tree-recursion"
"flatten-tree-recursion",
],
rangeChangeDebounceTime: 75, // ms
@ -42,13 +42,14 @@ let JsCallTreeView = Heritage.extend(DetailsSubview, {
* The { startTime, endTime }, in milliseconds.
*/
render: function (interval={}) {
let recording = PerformanceController.getCurrentRecording();
let profile = recording.getProfile();
let options = {
contentOnly: !PerformanceController.getOption("show-platform-data"),
invertTree: PerformanceController.getOption("invert-call-tree"),
flattenRecursion: PerformanceController.getOption("flatten-tree-recursion")
flattenRecursion: PerformanceController.getOption("flatten-tree-recursion"),
showOptimizationHint: recording.getConfiguration().withJITOptimizations,
};
let recording = PerformanceController.getCurrentRecording();
let profile = recording.getProfile();
let threadNode = this._prepareCallTree(profile, interval, options);
this._populateCallTree(threadNode, options);
this.emit(EVENTS.JS_CALL_TREE_RENDERED);
@ -104,7 +105,8 @@ let JsCallTreeView = Heritage.extend(DetailsSubview, {
hidden: inverted,
// Call trees should only auto-expand when not inverted. Passing undefined
// will default to the CALL_TREE_AUTO_EXPAND depth.
autoExpandDepth: inverted ? 0 : undefined
autoExpandDepth: inverted ? 0 : undefined,
enableOptimizations: options.enableOptimizations
});
// Bind events.
@ -112,6 +114,9 @@ let JsCallTreeView = Heritage.extend(DetailsSubview, {
// Pipe "focus" events to the view, mostly for tests
root.on("focus", () => this.emit("focus"));
// TODO tests for optimization event and rendering
// optimization bubbles in call tree
root.on("optimization", (_, node) => this.emit("optimization", node));
// Clear out other call trees.
this.container.innerHTML = "";

Просмотреть файл

@ -111,6 +111,12 @@ table.url.tooltiptext=View source in Debugger
# buttons (small magnifying glass icons) which spawn a new tab.
table.zoom.tooltiptext=Inspect frame in new tab
# LOCALIZATION NOTE (table.view-optimizations.tooltiptext):
# This string is displayed in the icon displayed next to frames that
# have optimization data
table.view-optimizations.tooltiptext=View optimizations in JIT View
# LOCALIZATION NOTE (recordingsList.saveDialogTitle):
# This string is displayed as a title for saving a recording to disk.
recordingsList.saveDialogTitle=Save profile…

Просмотреть файл

@ -641,25 +641,38 @@ menuitem.marker-color-graphs-blue:before,
cursor: pointer;
}
#jit-optimizations-view .opt-icon::before {
.opt-icon::before {
content: "";
background-image: url(chrome://browser/skin/devtools/webconsole.svg);
background-repeat: no-repeat;
background-size: 72px 60px;
/* show grey "i" bubble by default */
background-position: -36px -36px;
width: 12px;
height: 12px;
display: inline-block;
margin: 5px 6px 0 0;
max-height: 12px;
}
.theme-light #jit-optimizations-view .opt-icon::before {
#jit-optimizations-view .opt-icon::before {
margin: 5px 6px 0 0;
}
description.opt-icon {
margin: 0px 0px 0px 0px;
}
description.opt-icon::before {
margin: 1px 4px 0px 0px;
}
.theme-light .opt-icon::before {
background-image: url(chrome://browser/skin/devtools/webconsole.svg#light-icons);
}
#jit-optimizations-view .opt-icon[severity=warning]::before {
.opt-icon[severity=warning]::before {
background-position: -24px -24px;
}
.opt-icon[type=linkable]::before {
cursor: pointer;
}
ul.frames-list {
list-style-type: none;