--- a/devtools/client/locales/en-US/performance.properties
+++ b/devtools/client/locales/en-US/performance.properties
@@ -58,23 +58,23 @@ graphs.ms=ms
# AS SHORT AS POSSIBLE so it doesn't obstruct important parts of the graph.
graphs.memory=MB
# LOCALIZATION NOTE (category.*):
# These strings are displayed in the categories graph of the Performance Tools,
# as the legend for each block in every bar. These labels should be kept
# AS SHORT AS POSSIBLE so they don't obstruct important parts of the graph.
category.other=Gecko
-category.css=Styles
+category.layout=Layout
category.js=JIT
category.gc=GC
category.network=Network
category.graphics=Graphics
-category.storage=Storage
-category.events=Input & Events
+category.dom=DOM
+category.idle=Idle
category.tools=Tools
# LOCALIZATION NOTE (table.bytes):
# This string is displayed in the call tree after bytesize units.
# %S represents the value in bytes.
table.bytes=%S B
# LOCALIZATION NOTE (table.ms2):
--- a/devtools/client/performance/modules/categories.js
+++ b/devtools/client/performance/modules/categories.js
@@ -2,26 +2,30 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { L10N } = require("devtools/client/performance/modules/global");
/**
* Details about each label stack frame category.
- * @see CATEGORY_MAPPINGS.
+ * To be kept in sync with the js::ProfilingStackFrame::Category in ProfilingStack.h
*/
const CATEGORIES = [{
+ color: "#d99b28",
+ abbrev: "idle",
+ label: L10N.getStr("category.idle")
+}, {
color: "#5e88b0",
abbrev: "other",
label: L10N.getStr("category.other")
}, {
color: "#46afe3",
- abbrev: "css",
- label: L10N.getStr("category.css")
+ abbrev: "layout",
+ label: L10N.getStr("category.layout")
}, {
color: "#d96629",
abbrev: "js",
label: L10N.getStr("category.js")
}, {
color: "#eb5368",
abbrev: "gc",
label: L10N.getStr("category.gc")
@@ -30,99 +34,40 @@ const CATEGORIES = [{
abbrev: "network",
label: L10N.getStr("category.network")
}, {
color: "#70bf53",
abbrev: "graphics",
label: L10N.getStr("category.graphics")
}, {
color: "#8fa1b2",
- abbrev: "storage",
- label: L10N.getStr("category.storage")
+ abbrev: "dom",
+ label: L10N.getStr("category.dom")
}, {
- color: "#d99b28",
- abbrev: "events",
- label: L10N.getStr("category.events")
-}, {
+ // The devtools-only "tools" category which gets computed by
+ // computeIsContentAndCategory and is not part of the category list in
+ // ProfilingStack.h.
color: "#8fa1b2",
abbrev: "tools",
label: L10N.getStr("category.tools")
}];
/**
- * Mapping from category bitmasks in the profiler data to additional details.
- * To be kept in sync with the js::ProfilingStackFrame::Category in ProfilingStack.h
+ * Get the numeric index for the given category abbreviation.
+ * See `CATEGORIES` above.
*/
-const CATEGORY_MAPPINGS = {
- // js::ProfilingStackFrame::Category::OTHER
- "16": CATEGORIES[0],
- // js::ProfilingStackFrame::Category::CSS
- "32": CATEGORIES[1],
- // js::ProfilingStackFrame::Category::JS
- "64": CATEGORIES[2],
- // js::ProfilingStackFrame::Category::GC
- "128": CATEGORIES[3],
- // js::ProfilingStackFrame::Category::CC
- "256": CATEGORIES[3],
- // js::ProfilingStackFrame::Category::NETWORK
- "512": CATEGORIES[4],
- // js::ProfilingStackFrame::Category::GRAPHICS
- "1024": CATEGORIES[5],
- // js::ProfilingStackFrame::Category::STORAGE
- "2048": CATEGORIES[6],
- // js::ProfilingStackFrame::Category::EVENTS
- "4096": CATEGORIES[7],
- // non-bitmasks for specially-assigned categories
- "9000": CATEGORIES[8],
-};
-
-/**
- * Get the numeric bitmask (or set of masks) for the given category
- * abbreviation. See `CATEGORIES` and `CATEGORY_MAPPINGS` above.
- *
- * CATEGORY_MASK can be called with just a name if it is expected that the
- * category is mapped to by exactly one bitmask. If the category is mapped
- * to by multiple masks, CATEGORY_MASK for that name must be called with
- * an additional argument specifying the desired id (in ascending order).
- */
-const [CATEGORY_MASK, CATEGORY_MASK_LIST] = (() => {
- const bitmasksForCategory = {};
- const all = Object.keys(CATEGORY_MAPPINGS);
-
- for (const category of CATEGORIES) {
- bitmasksForCategory[category.abbrev] = all
- .filter(mask => CATEGORY_MAPPINGS[mask] == category)
- .map(mask => +mask)
- .sort();
+const CATEGORY_INDEX = (() => {
+ const indexForCategory = {};
+ for (let categoryIndex = 0; categoryIndex < CATEGORIES.length; categoryIndex++) {
+ const category = CATEGORIES[categoryIndex];
+ indexForCategory[category.abbrev] = categoryIndex;
}
- return [
- function(name, index) {
- if (!(name in bitmasksForCategory)) {
- throw new Error(`Category abbreviation "${name}" does not exist.`);
- }
- if (arguments.length == 1) {
- if (bitmasksForCategory[name].length != 1) {
- throw new Error(`Expected exactly one category number for "${name}".`);
- } else {
- return bitmasksForCategory[name][0];
- }
- } else {
- if (index > bitmasksForCategory[name].length) {
- throw new Error(`Index "${index}" too high for category "${name}".`);
- }
- return bitmasksForCategory[name][index - 1];
- }
- },
-
- function(name) {
- if (!(name in bitmasksForCategory)) {
- throw new Error(`Category abbreviation "${name}" does not exist.`);
- }
- return bitmasksForCategory[name];
+ return function(name) {
+ if (!(name in indexForCategory)) {
+ throw new Error(`Category abbreviation "${name}" does not exist.`);
}
- ];
+ return indexForCategory[name];
+ };
})();
exports.CATEGORIES = CATEGORIES;
-exports.CATEGORY_MAPPINGS = CATEGORY_MAPPINGS;
-exports.CATEGORY_MASK = CATEGORY_MASK;
-exports.CATEGORY_MASK_LIST = CATEGORY_MASK_LIST;
+exports.CATEGORY_INDEX = CATEGORY_INDEX;
--- a/devtools/client/performance/modules/logic/frame-utils.js
+++ b/devtools/client/performance/modules/logic/frame-utils.js
@@ -4,17 +4,17 @@
"use strict";
const global = require("devtools/client/performance/modules/global");
const demangle = require("devtools/client/shared/demangle");
const { assert } = require("devtools/shared/DevToolsUtils");
const { isChromeScheme, isContentScheme, isWASM, parseURL } =
require("devtools/client/shared/source-utils");
-const { CATEGORY_MASK, CATEGORY_MAPPINGS } = require("devtools/client/performance/modules/categories");
+const { CATEGORY_INDEX, CATEGORIES } = require("devtools/client/performance/modules/categories");
// Character codes used in various parsing helper functions.
const CHAR_CODE_R = "r".charCodeAt(0);
const CHAR_CODE_0 = "0".charCodeAt(0);
const CHAR_CODE_9 = "9".charCodeAt(0);
const CHAR_CODE_CAP_Z = "Z".charCodeAt(0);
const CHAR_CODE_LPAREN = "(".charCodeAt(0);
@@ -185,17 +185,17 @@ function parseLocation(location, fallbac
/**
* Sets the properties of `isContent` and `category` on a frame.
*
* @param {InflatedFrame} frame
*/
function computeIsContentAndCategory(frame) {
// Only C++ stack frames have associated category information.
- if (frame.category) {
+ if (frame.category !== null && frame.category !== undefined) {
return;
}
const location = frame.location;
// There are 3 variants of location strings in the profiler (with optional
// column numbers):
// 1) "name (resource:line)"
@@ -229,28 +229,28 @@ function computeIsContentAndCategory(fra
}
if (schemeStartIndex !== 0) {
for (let j = schemeStartIndex; j < location.length; j++) {
if (location.charCodeAt(j) === CHAR_CODE_R &&
isChromeScheme(location, j) &&
(location.includes("resource://devtools") ||
location.includes("resource://devtools"))) {
- frame.category = CATEGORY_MASK("tools");
+ frame.category = CATEGORY_INDEX("tools");
return;
}
}
}
if (location === "EnterJIT") {
- frame.category = CATEGORY_MASK("js");
+ frame.category = CATEGORY_INDEX("js");
return;
}
- frame.category = CATEGORY_MASK("other");
+ frame.category = CATEGORY_INDEX("other");
}
/**
* Get caches to cache inflated frames and computed frame keys of a frame
* table.
*
* @param object framesTable
* @return object
@@ -388,17 +388,20 @@ function getFrameInfo(node, options) {
data.functionName = global.L10N.getStr("table.root");
} else {
data = parseLocation(node.location, node.line, node.column);
data.hasOptimizations = node.hasOptimizations();
data.isContent = node.isContent;
data.isMetaCategory = node.isMetaCategory;
}
data.samples = node.youngestFrameSamples;
- data.categoryData = CATEGORY_MAPPINGS[node.category] || {};
+ const hasCategory = node.category !== null && node.category !== undefined;
+ data.categoryData = hasCategory
+ ? (CATEGORIES[node.category] || CATEGORIES[CATEGORY_INDEX("other")])
+ : {};
data.nodeType = node.nodeType;
// Frame name (function location or some meta information)
if (data.isMetaCategory) {
data.name = data.categoryData.label;
} else if (shouldDemangle(data.functionName)) {
data.name = demangle(data.functionName);
} else {
--- a/devtools/client/performance/test/browser_perf-tree-view-02.js
+++ b/devtools/client/performance/test/browser_perf-tree-view-02.js
@@ -119,17 +119,17 @@ add_task(function() {
is($fun(".call-tree-url", $$(".call-tree-item")[2]).textContent.trim(), "baz",
"The .A.B node's function cell displays the correct url.");
ok($fun(".call-tree-url", $$(".call-tree-item")[2]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
"The .A.B node's function cell displays the correct url tooltiptext.");
is($fun(".call-tree-line", $$(".call-tree-item")[2]).textContent.trim(), ":34",
"The .A.B node's function cell displays the correct line.");
is($fun(".call-tree-host", $$(".call-tree-item")[2]).textContent.trim(), "foo",
"The .A.B node's function cell displays the correct host.");
- is($fun(".call-tree-category", $$(".call-tree-item")[2]).textContent.trim(), "Styles",
+ is($fun(".call-tree-category", $$(".call-tree-item")[2]).textContent.trim(), "Layout",
"The .A.B node's function cell displays the correct category.");
is($$dur(3).textContent.trim(), "5 ms",
"The .A.E node's duration cell displays the correct value.");
is($$per(3).textContent.trim(), "25%",
"The .A.E node's percentage cell displays the correct value.");
is($$sam(3).textContent.trim(), "0",
"The .A.E node's samples cell displays the correct value.");
--- a/devtools/client/performance/test/browser_perf-tree-view-08.js
+++ b/devtools/client/performance/test/browser_perf-tree-view-08.js
@@ -4,17 +4,17 @@
/**
* Tests that the profiler's tree view renders generalized platform data
* when `contentOnly` is on correctly.
*/
const { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
const { CallView } = require("devtools/client/performance/modules/widgets/tree-view");
-const { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
+const { CATEGORY_INDEX } = require("devtools/client/performance/modules/categories");
const RecordingUtils = require("devtools/shared/performance/recording-utils");
add_task(function() {
const threadNode = new ThreadNode(gProfile.threads[0], { startTime: 0, endTime: 20,
contentOnly: true });
// Don't display the synthesized (root) and the real (root) node twice.
threadNode.calls = threadNode.calls[0].calls;
@@ -84,26 +84,26 @@ const gProfile = RecordingUtils.deflateP
]
}, {
time: 1 + 1 + 2,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
{ location: "http://content/E" },
{ location: "http://content/F" },
- { location: "platform_JS", category: CATEGORY_MASK("js") },
+ { location: "platform_JS", category: CATEGORY_INDEX("js") },
]
}, {
time: 1 + 1 + 2 + 3,
frames: [
{ location: "(root)" },
- { location: "platform_JS2", category: CATEGORY_MASK("js") },
+ { location: "platform_JS2", category: CATEGORY_INDEX("js") },
]
}, {
time: 1 + 1 + 2 + 3 + 5,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
- { location: "platform_GC", category: CATEGORY_MASK("gc", 1) },
+ { location: "platform_GC", category: CATEGORY_INDEX("gc") },
]
}]
}]
});
--- a/devtools/client/performance/test/helpers/synth-utils.js
+++ b/devtools/client/performance/test/helpers/synth-utils.js
@@ -1,53 +1,53 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Generates a generalized profile with some samples.
*/
exports.synthesizeProfile = () => {
- const { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
+ const { CATEGORY_INDEX } = require("devtools/client/performance/modules/categories");
const RecordingUtils = require("devtools/shared/performance/recording-utils");
return RecordingUtils.deflateProfile({
meta: { version: 2 },
threads: [{
samples: [{
time: 1,
frames: [
- { category: CATEGORY_MASK("other"), location: "(root)" },
- { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
- { category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
- { category: CATEGORY_MASK("js"), location: "C (http://foo/bar/baz:56)" }
+ { category: CATEGORY_INDEX("other"), location: "(root)" },
+ { category: CATEGORY_INDEX("other"), location: "A (http://foo/bar/baz:12)" },
+ { category: CATEGORY_INDEX("layout"), location: "B (http://foo/bar/baz:34)" },
+ { category: CATEGORY_INDEX("js"), location: "C (http://foo/bar/baz:56)" }
]
}, {
time: 1 + 1,
frames: [
- { category: CATEGORY_MASK("other"), location: "(root)" },
- { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
- { category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
- { category: CATEGORY_MASK("gc", 1), location: "D (http://foo/bar/baz:78:9)" }
+ { category: CATEGORY_INDEX("other"), location: "(root)" },
+ { category: CATEGORY_INDEX("other"), location: "A (http://foo/bar/baz:12)" },
+ { category: CATEGORY_INDEX("layout"), location: "B (http://foo/bar/baz:34)" },
+ { category: CATEGORY_INDEX("gc"), location: "D (http://foo/bar/baz:78:9)" }
]
}, {
time: 1 + 1 + 2,
frames: [
- { category: CATEGORY_MASK("other"), location: "(root)" },
- { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
- { category: CATEGORY_MASK("css"), location: "B (http://foo/bar/baz:34)" },
- { category: CATEGORY_MASK("gc", 1), location: "D (http://foo/bar/baz:78:9)" }
+ { category: CATEGORY_INDEX("other"), location: "(root)" },
+ { category: CATEGORY_INDEX("other"), location: "A (http://foo/bar/baz:12)" },
+ { category: CATEGORY_INDEX("layout"), location: "B (http://foo/bar/baz:34)" },
+ { category: CATEGORY_INDEX("gc"), location: "D (http://foo/bar/baz:78:9)" }
]
}, {
time: 1 + 1 + 2 + 3,
frames: [
- { category: CATEGORY_MASK("other"), location: "(root)" },
- { category: CATEGORY_MASK("other"), location: "A (http://foo/bar/baz:12)" },
- { category: CATEGORY_MASK("gc", 2), location: "E (http://foo/bar/baz:90)" },
- { category: CATEGORY_MASK("network"), location: "F (http://foo/bar/baz:99)" }
+ { category: CATEGORY_INDEX("other"), location: "(root)" },
+ { category: CATEGORY_INDEX("other"), location: "A (http://foo/bar/baz:12)" },
+ { category: CATEGORY_INDEX("gc"), location: "E (http://foo/bar/baz:90)" },
+ { category: CATEGORY_INDEX("network"), location: "F (http://foo/bar/baz:99)" }
]
}]
}]
});
};
/**
* Generates a simple implementation for a tree class.
--- a/devtools/client/performance/test/unit/test_profiler-categories.js
+++ b/devtools/client/performance/test/unit/test_profiler-categories.js
@@ -2,32 +2,24 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests if the profiler categories are mapped correctly.
*/
add_task(function() {
- const { CATEGORIES, CATEGORY_MAPPINGS } = require("devtools/client/performance/modules/categories");
+ const { CATEGORIES } = require("devtools/client/performance/modules/categories");
const { L10N } = require("devtools/client/performance/modules/global");
const count = CATEGORIES.length;
ok(count,
"Should have a non-empty list of categories available.");
ok(CATEGORIES.some(e => e.color),
"All categories have an associated color.");
ok(CATEGORIES.every(e => e.label),
"All categories have an associated label.");
ok(CATEGORIES.every(e => e.label === L10N.getStr("category." + e.abbrev)),
"All categories have a correctly localized label.");
-
- ok(Object.keys(CATEGORY_MAPPINGS).every(e => (Number(e) >= 9000 && Number(e) <= 9999) ||
- Number.isInteger(Math.log2(e))),
- "All bitmask mappings keys are powers of 2, or between 9000-9999 for special " +
- "categories.");
-
- ok(Object.keys(CATEGORY_MAPPINGS).every(e => CATEGORIES.includes(CATEGORY_MAPPINGS[e])),
- "All bitmask mappings point to a category.");
});
--- a/devtools/client/performance/test/unit/test_tree-model-07.js
+++ b/devtools/client/performance/test/unit/test_tree-model-07.js
@@ -1,17 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that when displaying only content nodes, platform nodes are generalized.
*/
-var { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
+var { CATEGORY_INDEX } = require("devtools/client/performance/modules/categories");
add_task(function test() {
const { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
const url = (n) => `http://content/${n}`;
// Create a root node from a given samples array.
const root = getFrameNodePath(new ThreadNode(gThread, { startTime: 5, endTime: 30,
@@ -30,32 +30,39 @@ add_task(function test() {
* - F
* - (JS)
*/
// Test the root node.
equal(root.calls.length, 2, "root has 2 children");
ok(getFrameNodePath(root, url("A")), "root has content child");
- ok(getFrameNodePath(root, "64"), "root has platform generalized child");
- equal(getFrameNodePath(root, "64").calls.length, 0,
+ ok(getFrameNodePath(root, `${CATEGORY_INDEX("js")}`),
+ "root has platform generalized child");
+ equal(getFrameNodePath(root, `${CATEGORY_INDEX("js")}`).calls.length, 0,
"platform generalized child is a leaf.");
- ok(getFrameNodePath(root, `${url("A")} > 128`),
+ ok(getFrameNodePath(root, `${url("A")} > ${CATEGORY_INDEX("gc")}`),
"A has platform generalized child of another type");
- equal(getFrameNodePath(root, `${url("A")} > 128`).calls.length, 0,
+ equal(getFrameNodePath(root, `${url("A")} > ${CATEGORY_INDEX("gc")}`).calls.length, 0,
"second generalized type is a leaf.");
- ok(getFrameNodePath(root, `${url("A")} > ${url("E")} > ${url("F")} > 64`),
+ ok(getFrameNodePath(
+ root,
+ `${url("A")} > ${url("E")} > ${url("F")} > ${CATEGORY_INDEX("js")}`
+ ),
"a second leaf of the first generalized type exists deep in the tree.");
- ok(getFrameNodePath(root, `${url("A")} > 128`),
+ ok(getFrameNodePath(root, `${url("A")} > ${CATEGORY_INDEX("gc")}`),
"A has platform generalized child of another type");
- equal(getFrameNodePath(root, "64").category,
- getFrameNodePath(root, `${url("A")} > ${url("E")} > ${url("F")} > 64`).category,
+ equal(getFrameNodePath(root, `${CATEGORY_INDEX("js")}`).category,
+ getFrameNodePath(
+ root,
+ `${url("A")} > ${url("E")} > ${url("F")} > ${CATEGORY_INDEX("js")}`
+ ).category,
"generalized frames of same type are duplicated in top-down view");
});
var gThread = synthesizeProfileForTest([{
time: 5,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
@@ -63,35 +70,35 @@ var gThread = synthesizeProfileForTest([
{ location: "http://content/C" }
]
}, {
time: 5 + 6,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
{ location: "http://content/B" },
- { location: "contentY", category: CATEGORY_MASK("css") },
+ { location: "contentY", category: CATEGORY_INDEX("layout") },
{ location: "http://content/D" }
]
}, {
time: 5 + 6 + 7,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
- { location: "contentY", category: CATEGORY_MASK("css") },
+ { location: "contentY", category: CATEGORY_INDEX("layout") },
{ location: "http://content/E" },
{ location: "http://content/F" },
- { location: "contentY", category: CATEGORY_MASK("js") },
+ { location: "contentY", category: CATEGORY_INDEX("js") },
]
}, {
time: 5 + 20,
frames: [
{ location: "(root)" },
- { location: "contentX", category: CATEGORY_MASK("js") },
+ { location: "contentX", category: CATEGORY_INDEX("js") },
]
}, {
time: 5 + 25,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
- { location: "contentZ", category: CATEGORY_MASK("gc", 1) },
+ { location: "contentZ", category: CATEGORY_INDEX("gc") },
]
}]);
--- a/devtools/client/performance/test/unit/test_tree-model-08.js
+++ b/devtools/client/performance/test/unit/test_tree-model-08.js
@@ -4,17 +4,17 @@
/**
* Verifies if FrameNodes retain and parse their data appropriately.
*/
add_task(function test() {
const FrameUtils = require("devtools/client/performance/modules/logic/frame-utils");
const { FrameNode } = require("devtools/client/performance/modules/logic/tree-model");
- const { CATEGORY_MASK } = require("devtools/client/performance/modules/categories");
+ const { CATEGORY_INDEX } = require("devtools/client/performance/modules/categories");
const compute = frame => {
FrameUtils.computeIsContentAndCategory(frame);
return frame;
};
const frames = [
new FrameNode("hello/<.world (http://foo/bar.js:123:987)", compute({
location: "hello/<.world (http://foo/bar.js:123:987)",
@@ -34,17 +34,17 @@ add_task(function test() {
}), false),
new FrameNode("hello/<.world (resource://foo.js -> http://bar/baz.js:123:987)", compute({
location: "hello/<.world (resource://foo.js -> http://bar/baz.js:123:987)",
line: 456,
}), false),
new FrameNode("Foo::Bar::Baz", compute({
location: "Foo::Bar::Baz",
line: 456,
- category: CATEGORY_MASK("other"),
+ category: CATEGORY_INDEX("other"),
}), false),
new FrameNode("EnterJIT", compute({
location: "EnterJIT",
}), false),
new FrameNode("chrome://browser/content/content.js", compute({
location: "chrome://browser/content/content.js",
line: 456,
column: 123
--- a/devtools/client/performance/test/unit/test_tree-model-09.js
+++ b/devtools/client/performance/test/unit/test_tree-model-09.js
@@ -1,16 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Tests that when displaying only content nodes, platform nodes are generalized.
*/
+var { CATEGORY_INDEX } = require("devtools/client/performance/modules/categories");
+
add_task(function test() {
const { ThreadNode } = require("devtools/client/performance/modules/logic/tree-model");
const url = (n) => `http://content/${n}`;
// Create a root node from a given samples array.
const root = getFrameNodePath(new ThreadNode(gThread, { startTime: 5, endTime: 25,
contentOnly: true }), "(root)");
@@ -27,26 +29,29 @@ add_task(function test() {
* - F
* - (Tools)
*/
// Test the root node.
equal(root.calls.length, 2, "root has 2 children");
ok(getFrameNodePath(root, url("A")), "root has content child");
- ok(getFrameNodePath(root, "9000"),
+ ok(getFrameNodePath(root, `${CATEGORY_INDEX("tools")}`),
"root has platform generalized child from Chrome JS");
- equal(getFrameNodePath(root, "9000").calls.length, 0,
+ equal(getFrameNodePath(root, `${CATEGORY_INDEX("tools")}`).calls.length, 0,
"platform generalized child is a leaf.");
- ok(getFrameNodePath(root, `${url("A")} > ${url("E")} > ${url("F")} > 9000`),
+ ok(getFrameNodePath(root,
+ `${url("A")} > ${url("E")} > ${url("F")} > ${CATEGORY_INDEX("tools")}`),
"a second leaf of the generalized Chrome JS exists.");
- equal(getFrameNodePath(root, "9000").category,
- getFrameNodePath(root, `${url("A")} > ${url("E")} > ${url("F")} > 9000`).category,
+ equal(getFrameNodePath(root, `${CATEGORY_INDEX("tools")}`).category,
+ getFrameNodePath(root,
+ `${url("A")} > ${url("E")} > ${url("F")} > ${CATEGORY_INDEX("tools")}`
+ ).category,
"generalized frames of same type are duplicated in top-down view");
});
var gThread = synthesizeProfileForTest([{
time: 5,
frames: [
{ location: "(root)" },
{ location: "http://content/A" },
--- a/devtools/client/shared/widgets/FlameGraph.js
+++ b/devtools/client/shared/widgets/FlameGraph.js
@@ -8,17 +8,19 @@ const { ELLIPSIS } = require("devtools/s
loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
loader.lazyRequireGetter(this, "EventEmitter",
"devtools/shared/event-emitter");
loader.lazyRequireGetter(this, "getColor",
"devtools/client/shared/theme", true);
-loader.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
+loader.lazyRequireGetter(this, "CATEGORIES",
+ "devtools/client/performance/modules/categories", true);
+loader.lazyRequireGetter(this, "CATEGORY_INDEX",
"devtools/client/performance/modules/categories", true);
loader.lazyRequireGetter(this, "FrameUtils",
"devtools/client/performance/modules/logic/frame-utils");
loader.lazyRequireGetter(this, "demangle",
"devtools/client/shared/demangle");
loader.lazyRequireGetter(this, "AbstractCanvasGraph",
"devtools/client/shared/widgets/Graphs", true);
@@ -1287,17 +1289,18 @@ var FlameGraphUtils = {
mutableFrameKeyOptions.isLeaf = stackDepth === 0;
let frameKey = inflatedFrame.getFrameKey(mutableFrameKeyOptions);
// If not skipping the frame, add it to the current level. The (root)
// node isn't useful for flame graphs.
if (frameKey !== "" && frameKey !== "(root)") {
// If the frame is a meta category, use the category label.
if (mutableFrameKeyOptions.isMetaCategoryOut) {
- frameKey = CATEGORY_MAPPINGS[frameKey].label;
+ const category = CATEGORIES[frameKey] || CATEGORIES[CATEGORY_INDEX("other")];
+ frameKey = category.label;
}
sampleFrames[stackDepth] = inflatedFrame;
sampleFrameKeys[stackDepth] = frameKey;
// If we shouldn't flatten the current frame into the previous one,
// increment the stack depth.
if (!flattenRecursion || frameKey !== prevFrameKey) {